ARM RTX Real-Time Operating System
A Cortex-M Optimized RTOS that Simplifies Embedded Programming
Bob Boys ARM San Jose, California bob.boys@arm.com
Agenda
Agenda: CMSIS Super-loop vs. RTOS Round Robin RTOS Create a Project Debugging Cooperative Multitasking RTOS Objects Semaphore Example Interrupts Preemptive Task Switching
CMSIS:
Cortex Microcontroller Software Interface Standard.
CMSIS-Core CMSIS-DSP
Standard header files for various Cortex-M processors. Simplify startup routines. Startup.s and system.c System View Description (SVD) XML files. (peripherals)
Digital Signal processing libraries Free for Cortex-M0, M3 and M4
Version 3.0
CMSIS-RTOS CMSIS-DAP
A standard API for RTOSs Includes RTX a BSD license this means it if free A standard to connect on-board debug adapters Uses USB and a small processor on the PC board.
FILES: WWW.KEIL.COM: MDK
www.onarm.com/cmsis www.onarm.com CMSIS is created and maintained by ARM
HOW TO TRY OUT CMSIS:
www.keil.com/arm download free Keil MDK toolset Comes with simulator. Or connect to a target with Debug adapter: Keil ULINK, ST-Link V 2 or Segger J-Link (black case V 6 or later) Need Serial Wire Viewer support for RTOS: Many examples are provided. See my labs. www.keil.com Ports for RTX: Keil MDK, GCC and IAR
Important Things in Cortex-M for RTOS
Set/Unset hardware breakpoints on-the-fly. Dedicated SysTick timer. Serial Wire Viewer (SWV): see events real-time. ITM printf DAP read/write capability: Watch and Memory Kernel Awareness windows Watchpoints: dedicated breakpoints. ETM trace: see all program counters. LDREX and STREX instructions. .plus a few other things to make RTOS life easier
A RTOSs Advantages Over a Superloop
Developers focus on writing their application Rather than the scheduling and timing of it Device drivers, I/O and interrupt interfaces included Easy to get working Enables logical partitioning of application Application code is more portable Even simple applications need boot code, interrupt management and device drivers; Included within RTOS Buying an RTOS for this code alone is cheaper than writing it from scratch:
BECAUSE ITS FREE !!!!!!
Keil RTOS Advantages
Royalty Free Small footprint Fast interrupts Optimized for Cortex M processors. Kernel aware debugging in Keil Vision (secret weapon) CMSIS-RTOS compliant.
Task Specifications Priority Levels No. of Tasks Defined 254 Unlimited
Memory Requirements CODE Space
(depending on used functionality)
Bytes <4K
< 500
No. of Tasks Active
Context Switch Interrupt Latency
250
< 200 Cycles Not Applicable for Cortex M
RAM Space
(each active task requires an own stack space)
RTX Performance
Task Specifications
Interupt Latency
Cycles
n/a
Initialize system, start task
Create task (no task switch)
1,147
403
Create task (with task switch)
Delete Task Task Switch Set event
461
218 192 89
Send semaphore
72
RTX MODES
Round Robin:
Each task given a time slice. When slice is up switches to next task. If task not done will complete it later.
Pre-emptive:
A task runs until interrupted by a higher priority
task. This is a context switch First task will run later
Co-operative:
A task runs until gets a blocking OS call or uses a
os_task_pass() call.
RTX TASKS AND SYSTEM VIEWER
Uses DAP R/W if using a real chip Shows what is running and waiting etc. Updates in real-time.
RTX EVENT VIEWER
With the Simulator or if chip uses SWV. Updates in real-time. Shows when tasks operating.
RTX EVENT VIEWER:
Can zoom in to look at times. Very useful for debugging and configuring RX
Most Popular Functions:
All functions are in the RTX Help. Here are some of the most common ones:
NOTE: Cortex-M use SysTick Timer. Is Interrupt 15. RTX uses SVCall instruction to access privileged
resources. Is Interrupt 11. RTX will use special features of Cortex-M for most efficient operation.
First some configuration items:
#include <RTL.h> Select RTX Kernel in Options for target as shown below:
OSTID t_phaseA;
assigns task ID of Task A.
One for each task
Now, set up and start RTX:
Create and start task phaseA:
(one for each task)
t_phaseA = os_tsk_create (phaseA, 0); (task name, priority 0 to 254)
Initialize RTX and start init
os_sys_init(init); (the task to call)
Self delete a task: useful when init task done:
os_tsk_delete_self ();
Controlling Functions for Tasks:
Delays a task a set of clock ticks (of SysTick).
os_dly_wait (8);
(number of SysTick ticks) (10 msec) Task is put in WAIT_DLY state. When Delay done, is put in READY state.
Events:
Send event signal to clock task:
os_evt_set (0x0001, task); (bit pattern event flag number, OS_TID task name)
Wait for an event flag 0x0001
os_evt_wait_and (0x0001, 0xffff); os_evt_wait_or (0x0001, 0xffff); (event flag number, timeout value) and: waits for a number of event flags. or: waits for one event flag.
Mutex
Declare a mutex:
OS_MUT(OS_ID Mutex name);
Initialize a Mutex:
os_mut_init(OS_ID Mutex name);
Acquire a Mutex: os_mut_wait(OS_ID Mutex name, timeout); Release a Mutex:
os_mut_release(OS_ID Mutex name, timeout);
Super-Loop: Typical Programming Method
Application implemented as an endless loop with function calls Implies a fixed order for function execution Time-critical program areas use Interrupt Service Routines (ISR)s Communication via global variables; not data communication protocols Perfect for small embedded systems Often used for 8/16-bit MCUs
Interrupt 1() Interrupt 2() Interrupt 3()
Main.c While(1) Func1() Func1() Func2() Func2()
Func3() Func3() Func4() Func4()
Func5() Func5()
Super-Loop Scaling Pitfalls - Timers
Super-Loop can be synchronized with the System timer, but If system requires several different cycle times, it is hard to implement Typically only 4 hardware timers so only 4 events Creates software overhead application becomes hard to understand
Super-Loop Scaling Pitfalls Scalability
Applications become complex and hard to extend A simple change can have unpredictable side effects Time consuming to analyze
Func1() Func1() Func1() Func!!() Funca() Funca() While( Func2()1)Funca() Func22() Funcd() Func1() Func@() Func3() Funcbb() Func33() Funcb() Func2() Funca() Funca() Func4() Funcc() Func44() Funcca() Func3() Funcb() Funcb() Func5() Funcdd() Func55() Funcda() Func4() Funcc() Funcc()
Func44() Funccc() Func44() Funcca() Func5() Funcd() Funcd() Func55() Funcqq() Func55() Funcda() Func55() Funcda()
Interrupt 1()
Interrupt 2()
Interrupt 3()
Using a RTOS
Giving Even More Control to Your Project
Using an RTOS
Programs are made up of multiple files and functions
Main.c IO.c
Math.c
HighPri() Init_periph() Low_pwr_check() Wait() Master_switch() Check_flags() Check_clock() Set_globals() Interupt_ISRS()
Check_ext_1() Foo() Bar() Test() Do_it() Check_serial() Check_input() Check_ports() Open_port()
Divide() Float() Shift_reg() Calc_Global_foo()
GUI.c
Config_LCD() Wipe_screen() Set_cursor() Write_regs() Write_row()
Debug info()
Using an RTOS
Functions can be grouped together logically
Main.c IO.c
Math.c
HighPri()
Init_periph() Low_pwr_check() Wait() Master_switch()
Check_ext_1()
Foo() Bar()
Divide() Float() Shift_reg()
Calc_Global_foo()
GUI.c
Test()
Do_it() Check_serial() Check_input() Check_ports()
Check_flags()
Check_clock()
Config_LCD()
Wipe_screen() Set_cursor() Write_regs() Write_row()
Set_globals()
Interupt_ISRS() Debug info()
Open_port()
RTOS -Tasks
Logical Groups can be managed by an RTOS task Tasks can share functions
Divide() HighPri() Bar() Low_pwr_check() Check_ports() Open_port() Check_ext_1()
Config_LCD() Shift_reg() Init_periph()
Check_clock() Check_serial()
Float()
Do_it()
Check_input() Wipe_screen()
Master_switch()
Set_globals() Foo() Test() Calc_Global_foo()
Set_cursor()
Write_regs() Write_row()
Check_flags()
Interupt_ISRS() Wait() Debug info()
RTOS -Tasks
Logical Groups can be managed by an RTOS task Tasks can share functions
Divide() HighPri() Bar() Low_pwr_check() Check_ports() Open_port() Do_it() Check_input() Wipe_screen() Master_switch() Set_globals() Foo() Test() Calc_Global_foo()
Config_LCD() Shift_reg() Init_periph()
Check_clock() Check_serial()
Task A
Check_ext_1()
Float()
Set_cursor()
Write_regs() Write_row()
Check_flags()
Interupt_ISRS() Wait() Debug info()
RTOS -Tasks
Logical Groups can be managed by an RTOS task Tasks can share functions
Divide() HighPri() Bar() Low_pwr_check() Check_ports() Open_port() Do_it() Check_input() Wipe_screen() Master_switch()
Config_LCD() Shift_reg() Init_periph()
Check_clock() Check_serial()
Task A
Check_ext_1()
Float()
Kernel
Check_flags()
Interupt_ISRS() Wait() Debug info()
Set_globals() Foo() Test() Calc_Global_foo()
Set_cursor()
Write_regs() Write_row()
RTOS -Tasks
Logical Groups can be managed by an RTOS task Tasks can share functions
Divide() Config_LCD() Shift_reg() Init_periph() HighPri() Bar() Low_pwr_check() Check_ports() Open_port() Do_it()
Check_clock() Check_serial()
Task A
Check_ext_1()
Task B
Check_input() Wipe_screen()
Float()
Master_switch()
Kernel
Check_flags()
Interupt_ISRS() Wait() Debug info()
Set_globals() Foo() Test() Calc_Global_foo()
Set_cursor()
Write_regs() Write_row()
RTOS -Tasks
Logical Groups can be managed by an RTOS task Tasks can share functions
HighPri() Bar() Low_pwr_check() Check_ports() Open_port()
Master_switch()
Task A Task C Kernel
Check_clock() Check_serial()
Check_ext_1()
Divide() Config_LCD() Shift_reg() Init_periph()
Task B
Check_input() Wipe_screen()
Float()
Do_it()
Set_globals() Foo()
Set_cursor()
Write_regs() Write_row()
Check_flags()
Test()
Interupt_ISRS() Wait()
Calc_Global_foo()
Debug info()
RTOS -Tasks
Logical Groups can be managed by an RTOS task Tasks can share functions
Divide() Config_LCD() Shift_reg() Init_periph()
Master_switch()
Task B Task A Task Task D C Kernel
HighPri() Bar() Low_pwr_check() Check_ports() Open_port()
Check_clock()
Check_serial()
Check_ext_1()
Float()
Do_it()
Check_input()
Wipe_screen()
Set_globals() Foo()
Set_cursor()
Check_flags()
Test()
Interupt_ISRS() Wait()
Calc_Global_foo()
Write_regs() Write_row()
Debug info()
Creating Tasks
__task void taskA (void) { While(1) { } } __task void taskB (void) { While(1) { } } __task void taskC (void) { While(1) { } } __task void taskD (void) { While(1) { } }
Tasks are logical components of your project Tasks do not replace functions They call functions
Round Robin Basic Example
Simplest version of RTOS We will go over a more complex version later Each task gets a time slice in which to run Default time slice duration is 5 timer Ticks Short time slices will make it appear that all tasks execute simultaneously
TASK A
TASK D
TASK B
TASK C
Round Robin Basic Example
Simplest version of RTOS We will go over a more complex version later Each task gets a time slice in which to run Default time slice duration is 5 timer Ticks Short time slices will make it appear that all tasks execute simultaneously
5 Timer Ticks
TASK A
TASK D
TASK B
TASK C
Round Robin Basic Example
Timer tick is basic unit of measurement for all RTOS Basis of all delays, functions, timers, time slices etc. Default is 1 timer tick = 10000 timer overflows (10 ms)
TASK A
TASK D
TASK B
TASK C
Round Robin Basic Example
Timer tick is basic unit of measurement for all RTOS Basis of all delays, functions, timers, time slices etc. Default is 1 timer tick = 10000 timer overflows (10 ms)
5 Timer Ticks
TASK A
TASK D
TASK B
TASK C
Round Robin Basic Example
A task can stop executing before its time-slice is up RTX switches to the next task that is ready to run if one of these functions are called: os_tsk_pass() any os_wait library function
TASK A
TASK D
TASK B
TASK C
Round Robin Basic Example
A task can stop executing before its time-slice is up RTX switches to the next task that is ready to run if one of these functions are called: os_tsk_pass() any os_wait library function
5 Timer Ticks
TASK A
TASK D
TASK B
TASK C
Forcing a Task Switch
__task void taskA (void) { While(1) { } } __task void taskB (void) { While(1) { } } __task void taskC (void) { While(1) { } } __task void taskD (void) { While(1) { } }
Adding os_dly_wait() will force taskD to switch earlier than the Round Robin timeout
Forcing a Task Switch
__task void taskA (void) { While(1) { } } __task void taskB (void) { While(1) { } } __task void taskC (void) { While(1) { } } __task void taskD (void) { While(1) { os_dly_wait(20); } }
Adding os_dly_wait() will force taskD to switch earlier than the Round Robin timeout
Round Robin Basic Example
Task D is running As soon as wait function is reached, task switch occurs Task switches out even though there were 4 more timer ticks to go Task D will not be set to a READY state until 20 timer ticks have passed
TASK A
TASK D
TASK B
TASK C
Round Robin Basic Example
Task D is running As soon as wait function is reached, task switch occurs Task switches out even though there were 4 more timer ticks to go Task D will not be set to a READY state until 20 timer ticks have passed
TASK A
TASK D
os_dly_wait(20)
TASK B
TASK C
Round Robin Basic Example
Task D is running As soon as wait function is reached, task switch occurs Task switches out even though there were 4 more timer ticks to go Task D will not be set to a READY state until 20 timer ticks have passed
1 Timer Tick
TASK A
TASK D
os_dly_wait(20)
TASK B
TASK C
Round Robin Basic Example
Task A task state is RUNNING Task D task state is WAIT_DLY The other tasks states are READY A task set as state INACTIVE will be skipped
TASK A
TASK D
TASK B
TASK C
RTX Task States
RUNNING READY WAIT_DLY WAIT_ITV WAIT_OR WAIT_AND
WAIT_SEM WAIT_MUT
INACTIVE WAIT_MBX
Controlling the Flow of Your Code
__task void taskA (void) { While(1) { } } __task void taskB (void) { While(1) { } } __task void taskC (void) { While(1) { } } __task void taskD (void) { While(1) { os_dly_wait(20); } }
Use waits and events to control your code, instead of relying on a Round robin Task switch More efficient use of your resources
Controlling the Flow of Your Code
__task void taskA (void) { __task void taskA (void) While(1) {{ { While (1) counterA++; } os_dly_wait (5); } __task void taskB (void) { While(1) While (1) {{ counterB++; } if (counterB == 0) { } os_tsk_pass (); __task void taskC (void) { __task void taskC (void) { While(1) { While(1) { os_evt_wait_or } counterC++; (0x0001, 0xffff); } __task void taskD (void) { While(1) { os_dly_wait(20); } }
Use waits and events to control your code, instead of relying on a Round robin Task switch More efficient use of your resources
RTOS Objects
Use Cooperative Multitasking for Greater Efficiency
Cooperative Multitasking
Round Robin is not scalable Cooperative Multitasking gives more flexibility The task scheduler processes all
the tasks and then puts the highest ready task into the running state The highest priority task can then continue with its execution
TASK A
Use Semaphores and other RTOS objects for inter-task communication
TASK D
TASK B
TASK C
Cooperative Multitasking
Round Robin is not scalable Cooperative Multitasking gives more flexibility The task scheduler processes all
the tasks and then puts the highest ready task into the running state The highest priority task can then continue with its execution
TASK A
Use Semaphores and other RTOS objects for inter-task communication
TASK D
TASK B
TASK C
RTX OS wait Library Functions
RTX Library function Task State
os_dly_wait() os_itv_wait() os_evt_wait_or() os_evt_wait_and() os_sem_wait()
os_mut_wait() os_mbx_wait()
WAIT_DLY WAIT_ITV WAIT_OR WAIT_AND WAIT_SEM
WAIT_MUT WAIT_MBX
Managing Resources With Semaphores
Round Robin Task switch at 1 Tick Serial data is garbled Similar issue can occur with memory access Need to use a Semaphore It is like a virtual token Used when a resource is needed by multiple tasks
__task void taskA (void) { While(1) { Printf(Oh, sweet Caroline, Good times never seem); }
__task void taskB (void) { While(1) { Printf(And were rolling, rolling, rolling down the); } __task void taskC (void) { While(1) { Printf(I tell ya, life ain't easy for a boy named Sue); }
Serial Out Oh, Swee And wer I tell y t Caroli e rollin a, life
Managing Resources With Semaphores
When there is a free semaphore: The first task that requests it gets the token No other task can obtain the token until it is released
__task void taskA (void) { While(1) { S Printf(Oh, sweet Caroline, Good times never seem); }
__task void taskB (void) { While(1) { Printf(And were rolling, rolling, rolling down the); } __task void taskC (void) { While(1) { Printf(I tell ya, life ain't easy for a boy named Sue); }
Serial Out Oh, sweet Caroline, Good times never seem
Managing Resources With Semaphores
When a task requests a semaphore that is not available: Task is put to sleep When the token is returned S to the semaphore: Task wakes up and its status set to ready
__task void taskA (void) { While(1) { Printf(Oh, sweet Caroline, Good times never seem); }
__task void taskB (void) { While(1) { Printf(And were rolling, rolling, rolling down the); } __task void taskC (void) { While(1) { Printf(I tell ya, life ain't easy for a boy named Sue); }
Serial Out Oh, sweet Caroline, Good times never seem And were rolling, rolling, rolling down the
Other RTX Objects
Mutexes and Mailboxes
Mutexes
(Mutual exclusion locks) A software object that locks a common resource Similar to a semaphore But any task can release a semaphore token Only the task that locks the common resource can access it Big feature Priority inheritance (more later)
Mailboxes Pass Messages Between Tasks
A task acquires a premade mailbox and waits for messages If a message in a specified mailbox is not available: The waiting task is put to sleep Task wakes up as soon as another task sends a message to the mailbox
__task void taskA (void) { os_mbx_wait (A, &rptr, 0xffff); }
__task void taskB (void) { os_mbx_send(A, &rptr, 0xffff);
__task void taskC (void) { os_mbx_send(A, &rptr, 0xffff);
__task void taskD (void) { os_mbx_send(A, &rptr, 0xffff);
Mailboxes Pass Messages Between Tasks
A task acquires a premade mailbox and waits for messages If a message in a specified mailbox is not available: The waiting task is put to sleep Task wakes up as soon as another task sends a message to the mailbox
__task void taskA (void) { os_mbx_wait (A, &rptr, 0xffff); }
__task void taskB (void) { os_mbx_send(A, &rptr, 0xffff);
__task void taskC (void) { os_mbx_send(A, &rptr, 0xffff);
__task void taskD (void) { os_mbx_send(A, &rptr, 0xffff);
Mailboxes Pass Messages Between Tasks
A task acquires a premade mailbox and waits for messages If a message in a specified mailbox is not available: The waiting task is put to sleep Task wakes up as soon as another task sends a message to the mailbox
__task void taskA (void) { os_mbx_wait (A, &rptr, 0xffff); }
__task void taskB (void) { os_mbx_send(A, &rptr, 0xffff);
__task void taskC (void) { os_mbx_send(A, &rptr, 0xffff);
__task void taskD (void) { os_mbx_send(A, &rptr, 0xffff);
Mailboxes Pass Messages Between Tasks
A task acquires a premade mailbox and waits for messages If a message in a specified mailbox is not available: The waiting task is put to sleep Task wakes up as soon as another task sends a message to the mailbox
__task void taskA (void) { os_mbx_wait (A, &rptr, 0xffff); }
M M
__task void taskB (void) { os_mbx_send(A, &rptr, 0xffff);
__task void taskC (void) { os_mbx_send(A, &rptr, 0xffff);
__task void taskD (void) { os_mbx_send(A, &rptr, 0xffff);
Interrupts are handled by Kernel
To initiate a task switch, the RTX Kernel is called Kernel manages the time plus other house keeping chores Kernel will alert a task if an interrupt sends it a message
TASK A
TASK D
TASK B
TASK C
Preemptive Task Switching
Giving Even More Control to Your Project
Preemptive Task Switching
Assign priorities to different tasks for greater control of data flow No need to service a housekeeping message, if the fire alarm is going off
ISR TASK E
Priority
TASK C
TASK B TASK B
TASK A
TASK A
Time
TASK A
Preemptive Task Switching
Assign priorities to different tasks for greater control of data flow No need to service a housekeeping message, if the fire alarm is going off
ISR TASK E
Priority
Time slice
Preemption
TASK C
TASK B TASK B
TASK A
TASK A
Time
TASK A
THE END
That gives a brief introduction to ARM RTX !
Remember it is FREE ! Really FREE.
ARM University program supports you and RTX !
Other Examples
Evaluation tools can be found at: www.keil.com/demo
RTX examples can be found on your hard drive at: C:\keil\ARM\RL\RTX\Examples\ RL-ARM Getting Started Guide: http://www.keil.com/rl-arm/rl-gettingstarted.asp
Little Book of Semaphores by Allen B. Downey (Third party book): greenteapress.com/semaphores
PRIMARY
18 140 171 149 186 205 200 217 227
ACCENT
18 140 171 253 201 134 254 225 186
159 180 59
203 210 150
227 230 199
159 180 59
234 157 182
246 203 218
145 27 29
190 127 108
218 182 169
145 27 29
109 102 95
169 161 155
154 139 124
197 187 178
222 217 211
55 55 104
125 128 158
180 180 199