Freertos Arm Cortex
Short Description
Descripción: FreeRTOS-ARM-Cortex...
Description
ARM Cortex Embedded System Multitasking Programming Using FreeRTOS Andrew Eliasz Feb 2013
First Technology Transfer - Feb 2013
1
FreeRTOS Overview
First Technology Transfer - Feb 2013
2
FreeRTOS - The Who ●
FreeRTOS was developed by a “very clever” software engineer Richard Barry
●
As it says on the Lulu website who publish his books ●
●
●
● ●
●
He's been directly involved in the start up of several companies, primarily working in the industrial automation and aerospace and simulation markets. Barry is currently a director of Real Time Engineers Ltd. (owners of FreeRTOS) and Head of Innovation at Wittenstein High Integrity Systems.
Interestingly the only books on developing with FreeRTOS are those written by Richard In this respect FreeRTOS has something in common with Micriums' uCOS operating system ●
●
Richard Barry graduated with 1st Class Honors in Computing for Real Time Systems.
The books on developing with uCOS II / uCOS III are written by the developer of uCOS - Jean Labrosse
Both FreeRTOS and uCOS have API (Application Programming Interface)s that are specific to them
First Technology Transfer - Feb 2013
3
FreeRTOS - Some claims ●
Aims to: ●
●
●
●
●
●
●
"Provide a free product that surpasses the quality and service demanded by users of commercial alternatives"
FreeRTOSTM is a market leading RTOS from Real Time Engineers Ltd. that supports 33 architectures and received 103000 downloads during 2012. It is professionally developed, strictly quality controlled, robust, supported, and free to embed in commercial products without any requirement to expose your proprietary source code FreeRTOS has become the de facto standard RTOS for microcontrollers by removing common objections to using free software, and in so doing, providing a truly compelling free software model Question: Which other operating systems use the FreeRTOS API ? Question: Are there any embedded operating systems that use the Posix API ( or a subset of the Posix API ) ? Question: How is FreeRTOS tested ?
First Technology Transfer - Feb 2013
4
FreeRTOS - More Technical Claims ●
FreeRTOS never performs a non-deterministic operation, such as walking a linked list, from inside a critical section or interrupt ●
●
FreeRTOS uses an efficient software timer implementation that does not use any CPU time unless a timer actually needs servicing ● ●
●
Question: Timers are, essentially, counters that generate a time even when the timer overflows/reaches 0 [depending on whether it counts up or down] Question: What does this mean ?
The FreeRTOS queue usage model manages to combine simplicity with flexibility (in a tiny code size) - attributes that are normally mutually exclusive ●
●
Software timers do not contain variables that need to be counted down to zero
Lists of Blocked (pended) tasks do not require time consuming periodic servicing. ●
●
Question: What does this mean ?
Question: How is this possible ?
FreeRTOS queues are base primitives on-top of which other communication and synchronisation primitives are build ●
The code re-use opportunities that result reduce overall code size –
This, in turn, assists testing and helps ensure robustness
–
Question: How would you test this claim ?
First Technology Transfer - Feb 2013
5
FreeRTOS - Technology Highlights ●
FreeRTOS contains some very interesting features :
First Technology Transfer - Feb 2013
6
Wikipedia - FreeRTOS Description ●
FreeRTOS provides methods for multiple threads or tasks, mutexes, semaphores and software timers. ●
A tick-less mode is provided for low power applications.
●
Thread priorities are supported.
●
In addition there are four schemes of memory allocation provided
●
–
Allocate only
–
Allocate and free - using simple but fast, algorithm
–
A more complex fast allocate and free algorithm with memory coalescence;
–
A C library supporting allocate and free with some mutual exclusion protection
FreeRTOS has none of the features typical of operating systems such as Linux or Microsoft Windows i.e. no support for –
Device drivers
–
Virtual memory management, user accounts, and networking
First Technology Transfer - Feb 2013
7
Wikipedia - FreeRTOS Description ctd. ●
FreeRTOS can be thought of as a 'thread library' rather than an 'operating system' –
Command line interface and POSIX like IO abstraction add-ons are available ●
●
FreeRTOS+IO provides a Linux/POSIX like open(), read(), write(), ioctl() type interface to peripheral driver libraries
–
VxWorks from Wind River Systems has a proprietary API and also supports a Posix API (i.e. the software was not designed with a Posix API in mind)
–
The Rowebots RTOS was implemented with a Posix API in mind
FreeRTOS implements multiple threads by ●
●
Having the host program call a thread tick method at regular short intervals The thread tick method switches tasks depending on priority and a round-robin scheduling scheme –
Switching is driven via a hardware timer interrupt
–
The time interval is configurable, typically from 1 to 10 msec
First Technology Transfer - Feb 2013
8
Mastering FreeRTOS - Plan of Action ● ● ●
FreeRTOS, although a tiny operating system, is far from simple Mostly written in generic C code, but, also some platform specific C and assembler This course is focused on ARM Cortex M3/M4 processors ● How much of the M3/M4 processor architecture do you need to understand ? ● Which compilers and IDEs can you use for developing applications ? - e.g. – Keil (now owned by ARM) IDE – IAR – Code Sourcery (Lite / commercial Eclipse based) - compiler is GCC – Code Red (widely favoured by NXP) ● Which drivers and protocols stacks will you be using ? – Open source ? – Commercial ? – Developed in house ? ● Complex systems and protocols – USB ? – Ethernet and TCP/IP ? – CAN (Controller Area Network) ? – Rich GUIs ?
First Technology Transfer - Feb 2013
9
Mastering FreeRTOS - Plan of Action - ctd. ●
Start off with something that works e.g. ● Evaluation board approximating the capabilities your project will need and for which a port of FreeRTOS exists and for which there are some demonstrator programs that work –
Ideally there is a port that uses the IDE you will be using in your project
●
Evaluate and organise the available documentation and forum discussions
●
The published FreeRTOS books are good, but only take you so far
●
Areas of “obscurity” –
Architecture specific details
–
Complex libraries and peripherals are not part of the package
–
USB ? ● TCP/IP ? ● LCD graphics libraries ? ● CAN bus support ? Libraries supplied with your IDE and Microcontroller vendor ? ●
● ●
Have these libraries been integrated with FreeRTOS ? If not ... then who will integrate them ?
First Technology Transfer - Feb 2013
10
FreeRTOS - Analysis and Design Issues ●
●
●
How to progress from initial requirements analysis and design to code specification and implementation ●
UML based requirements analysis and desing
●
Hatley-Pirbhai or Ward-Mellor analysis and design
●
How might you realise an object oriented design in C ?
●
How is code to be “split up into modules”
●
How is code to be partitioned into tasks ?
Testing and integration ●
MISRA guidelines
●
Unit testing
●
Code tracing and debugging
How will the embedded systems targets be upgraded ? ●
Patches and Bug fixes ?
●
Enhancements ?
●
Additional modules and features ?
First Technology Transfer - Feb 2013
11
CMSIS-RTOS Standard Based on ARM Technical Documentation as well as a Hitex Presentation by Niall Cooling of Feabhas
First Technology Transfer - Feb 2013
12
CMSIS ●
●
CMSIS represents an attempt by ARM to standardise many aspects of device driver and associated library implementation across multiple processors and multiple IDEs The areas of standardization includes: ●
Hardware Abstraction Layer (HAL) for Cortex-M processor registers –
●
●
Standardized definitions for the SysTick, NVIC, System Control Block registers, MPU registers, and core access functions.
Standardized system exception namesso that RTOS and middleware components can use system exceptions without running into compatibility issues. Standardized methods to organize header files- to facilitate learning how to program new Cortex-M microcontroller products and improve software portability.
First Technology Transfer - Feb 2013
13
CMSIS - ctd. ●
Common methods for system initialization to be used by each MCU vendor e.g. –
●
Standardized intrinsic functions - normally used to produce instructions that cannot be generated by IEC/ISO C –
●
●
Standardized SystemInit() function, provided in each device driver library, for configuring the clock.
Standardized intrinsic functions, improve software re-usability and portability
Standardized ways to determine the system clock frequency through a software variable SystemFrequency, defined in the device driver. Allows RTOS to setup the SysTick unit based on the system clock frequency
First Technology Transfer - Feb 2013
14
CMSIS - Versions ● ● ● ●
v1 - Core v2 - DSP v3 - RTOS Quasi Concurrent Programming :
First Technology Transfer - Feb 2013
15
CMSIS - Organisation ●
The CMSIS follows a layered architecture approach ●
Core Peripheral Access Layer –
●
Device Peripheral Access Layer (MCU specific) –
●
Provides name definitions, address definitions, and helper functions to access core registers and core peripherals. Deals with name definitions, address definitions, and driver code to access peripherals.
Access Functions for Peripherals (MCU specific and optional) –
Implements additional helper functions for peripherals as necessary
First Technology Transfer - Feb 2013
16
CMSIS - Organisation - ctd. ●
Outline of CMSIS architecture
First Technology Transfer - Feb 2013
17
CMSIS - RTOS Architecture ●
●
●
Kernel (RTK) ●
Scheduling
●
Mutual exclusion
Executive (RTE) ●
Inter-task communication & synchronisation
●
Memory management
RTOS ●
File management System –
●
FAT file system
Networking –
●
CMSIS - RTOS
E.g. TCP/IP, CAN
Graphical User Interface support –
E.g. OpenGL ES, Embedded Qt
First Technology Transfer - Feb 2013
18
CMSIS Components ●
The CMSIS consists of the following components: ●
●
●
●
●
CMSIS-CORE: provides an interface to Cortex-M0, Cortex-M3, Cortex-M4, SC000, and SC300 processors and peripheral registers CMSIS-DSP: DSP library with over 60 functions in fixed-point (fractional q7, q15, q31) and single precision floating-point (32-bit) implementation CMSIS-RTOS API: standardized programming interface for realtime operating systems for thread control, resource, and time management CMSIS-SVD: System View Description XML files that contain the programmer's view of a complete microcontroller system including peripherals
The standard is fully scalable across the Cortex-M processor series of microcontrollers
First Technology Transfer - Feb 2013
19
CMSIS v3 Architecture ●
The CMSIS architecture as it currently stands :
First Technology Transfer - Feb 2013
20
CMSIS - RTOS - Services ●
The services (features) provided via CMSIS-RTOS
First Technology Transfer - Feb 2013
21
Mutual Exclusion ●
Support for mutual exclusion comes from the implementation of mutexes and semaphores
First Technology Transfer - Feb 2013
22
Interthread Communication ●
Multithreading interthread communication and synchronisation is provided by the implementation of primitives such as ● Signals ● Message queues ● Mailboxes ● ISR communication - Communication/Interaction with Interrupt Service Routines
First Technology Transfer - Feb 2013
23
CMSIS - DSP Library ●
●
●
The CMSIS-DSP library provides functions supporting ●
Vector operations
●
Matrix computing
●
Complex arithmetic
●
Filter functions
●
Control functions
●
PID controller
●
Fourier transforms
Most algorithms are available in floating-point and various fixed-point formats and are optimized for the Cortex-M series processors. The Cortex-M4 processor implementation uses the ARM DSP SIMD (Single Instruction Multiple Data) instruction set and floating-point hardware ●
The CMSIS-DSP library, is written entirely in C –
The source code is provided and the algorithms can be adapted for specific application requirements.
First Technology Transfer - Feb 2013
24
CMSIS SVD ●
The CMSIS-SVD System View Description XML specification ●
●
●
●
●
Describes the programmer's view of the microcontroller system including the peripheral registers SVD files can be used to create the CMSIS-CORE header files which will include peripheral register and interrupt definitions. CMSIS-DVD files can also be used to create peripheral awareness dialogs for debuggers ARM provides downloadable SVD files for many devices
http://www.arm.com/products/processors/cortex-m/cortexmicrocontroller-software-interface-standard.php
First Technology Transfer - Feb 2013
25
CMSIS v3 Adaptation to some RTOS ●
The CMSIS API, in general, has to be adapted to a particular RTOS e.g.
First Technology Transfer - Feb 2013
26
Some RTOSes and IDEs to Consider ●
●
RTOSes ●
Free RTOS
●
ChibiOS/RT
●
Keil RTX
IDEs ●
IAR
●
Keil
●
Atollic True Studio
●
Rowley Associates - Cross Works
●
Code Sourcery
First Technology Transfer - Feb 2013
27
CMSIS - Device Driver Architecture ●
●
●
●
For each device, the MCU vendor should provide a header file that pulls-in additional header files required by the device driver library and the Core Peripheral Access Layer ● Include the file device_family.h into the source code. In order to be CMSIS-compliant, the vendor implementation must use the predefined exception and interrupt constants, core functions, and middleware functions for accessing device peripherals CMSIS-compliant device drivers will contain a startup-code file, which will include the vector table for various supported compilers Typical CMSIS-compliant source code files ●
device_family.h - the header file defining the device.
●
core_cm3.h - the header file defining the device core.
●
core_cm3.c - implements core intrinsic functions.
●
●
●
system_device_family.h - implements device specific interrupt and peripheral register definitions. system_device_family.c - impoements system functions and the initialization code. startup_device_family.s - implements the start-up code.
First Technology Transfer - Feb 2013
28
CMSIS - Device Driver Architecture ●
Typical CMSIS compliant code structure :
First Technology Transfer - Feb 2013
29
FreeRTOS View on CMSIS-RTOS ●
●
http://www.embedded.com/electronics-blogs/industry-comment/ , March 6 2012 While the hardware interface to a Cortex-M core is common across all Cortex-M microcontrollers, the software interface to an RTOS is different in every case. ●
●
POSIX, a common operating system interface in use for many years, is generally not regarded as an optimal solution for smaller devices, such as Cortex-M3 microcontrollers, so will CMSIS be a better choice?
The cynic might say that ARM has such a strong position in the microcontroller market that, if it put its weight behind it, CMSIS RTOS will be successful whether it technically merits the success or not. ●
●
●
Ultimately, however, its success (or otherwise) will depend on whether customers want it. For customers to want it, they have to see a benefit. One clear benefit is that of having a single API for a set of two or more RTOSes. Is this benefit alone enough to make CMSIS RTOS a success?
First Technology Transfer - Feb 2013
30
FreeRTOS View on CMSIS-RTOS ●
●
●
The end-user perspective : A common interface to multiple RTOS solutions will make software more portable and facilitate migrating application code between different RTOS suppliers. From the engineering point of view, especially for high-volume products, the selected microcontroller will be the lowest cost and smallest possible for the task in hand. ●
●
●
The software running on the microcontroller will be trimmed down. Where space, time, or responsiveness are at a premium, any software that is not adding value to that application will be removed. An RTOS abstraction layer will, by its very nature, not add to the RTOS functionality, but will make the code bigger and slow down the execution speed. –
●
The RTOS abstraction layer will , therefore, always be a prime candidate for removal.
A commercial RTOS is a financial investment and it is unlikely that developers will switch between different RTOSes that frequently
First Technology Transfer - Feb 2013
31
FreeRTOS View on CMSIS-RTOS ●
Open-source RTOS vendor perspective ●
●
Because of the financial investment required to select a new commercial RTOS, CMSIS RTOS compliance will provide more mobility to, and between, free open-source RTOS products. –
Potentially a big benefit to open-source suppliers.
–
The potential investment made by commercial silicon, RTOS, and tool vendors in CMSIS-RTOS-compliant infrastructure will be equally applicable to open-source-compliant products
However, by its very nature, an RTOS abstraction layer will make the RTOS larger and less responsive. –
Counter productive as an RTOS should be designed to be small or fast (or both).
–
Using the abstraction layer will not allow the RTOS to be used to its full potential, or provide the maximum potential benefit to the end user.
–
Maybe the abstraction layer will be removed in end products that make it to market as a final development phase
First Technology Transfer - Feb 2013
32
FreeRTOS View on CMSIS-RTOS ●
Commercial RTOS vendor perspective ●
●
The plus points listed above for an open-source RTOS vendor may very well be negative points for a commercial vendors. Commercial RTOS vendors invest in their products to differentiate them in a number of different ways. –
Performance, size, price, support, etc. are all attributes used to position an RTOS.
–
Anything that homogenizes RTOS offerings in the market, and in so doing reduces any differentiation gained by technical and/or marketing investments, makes the RTOS market a tougher market in which to work.
It may also make future investment harder to justify. Tools and middleware will become the key differentiator, until they, too become commoditized. ●
●
First Technology Transfer - Feb 2013
33
FreeRTOS View on CMSIS-RTOS ●
The non-RTOS-affiliated middleware supplier ●
Supply software that must work with any RTOS their customers might be using. – – –
Need to provide their own abstraction layer to, and test with, a number of different RTOS solutions. Replacing these multiple abstraction layers with a single abstraction layer is a benefit. The benefit will only become a reality however if the end users are actually using CMSIS RTOS in the products they are designing and shipping.
First Technology Transfer - Feb 2013
34
FreeRTOS View on CMSIS-RTOS ●
●
The microcontroller vendor ● Typically, provides software (predominantly drivers) that must interface with any system their customers are using - a standard such as CMSIS - RTOS is beneficial especially when drivers other than those based on a simple model need to be provided The tool vendor ● Provides products such as execution trace and profiling tools, as well as kernel aware debugger plug-ins. – Generally such tools do not access the RTOS through the RTOS's API, so will largely be unaffected. – Also, these tools are predominantly supplied by the RTOS vendors themselves ● Hence unlikely to want their tools to be used with any competitive products.
First Technology Transfer - Feb 2013
35
Patterns of Inter-process Communication and Synchronisation A Generic Approach
First Technology Transfer - Feb 2013
36
Rationale ●
There are a number of patterns for inter-process / inter-task communication
●
These patterns apply to most pre-emptive multi-tasking operating systems
●
●
●
●
●
●
●
Knowing these patterns makes it easier to structure code development for a variety of operating system APIs In this course the target OS API is the FreeRTOS API The FreeRTOS specific examples and code snippets will be covered in a follow on section The examples in this section will be given in pseudo-code and C. The examples will be general in the sense that they will not be targeted at any particular operating system ( real time or otherwise ). From time to time specific operating system API interfaces will be mentioned to illustrate real world code examples. The most important thing is to understand the underlying mechanisms, as well as the various patterns of programming with the various operating system intertask communication primitives.
First Technology Transfer - Feb 2013
37
Tasks ●
●
●
●
Real time and embedded systems need to be able to handle multiple inputs and multiple outputs within relatively tight time constraints. A practical concurrent design decomposes the application into ●
Small
●
Sequential
●
Schedulable
●
program units.
The design and implementation objective is to do this in such a way that when the system is multi-tasking the design performance and timing requirements are satisfied. A typical RTOS kernel provides ●
Task objects
●
Task management services
●
That facilitate the task of designing concurrency into the application.
First Technology Transfer - Feb 2013
38
Definition of a Task ●
Formally a task is ●
An independent thread of execution
●
Competes with other concurrent tasks for processor execution time
●
●
●
Is schedulable - competes for execution time based on some pre-determined scheduling algorithm Upon creation is given a distinct set of parameters and supporting data structures –
Name
–
Unique id
–
Priority ( if associated with a preemptive scheduler)
–
Task control block
–
Stack
–
Task routine
Typically, when a kernel starts, ●
It creates its own set of system tasks and
●
Allocates a suitable priority to each task
●
The priorities are from a set of priority levels reserved for RTOS system tasks
First Technology Transfer - Feb 2013
39
System Tasks ●
Typical system tasks include ● ●
Startup ( initialisation ) task Idle task - uses up CPU idle cycles when there is nothing else to do
●
Logging task - logs system messages
●
Exception-handling task
●
Debug agent task
First Technology Transfer - Feb 2013
40
Finite State Machine Approach to Task Scheduling ●
●
A common pattern when designing schedulers is to specify a number of possible states any given task can be in. A simple pattern, typical of many pre-emptive scheduling kernels envisions the following states ●
●
●
Ready state - task eligible to run, but cannot run because a higher priority task is executing Blocked state - occurs where the task –
Has requested a resource that is not immediately available
–
Has requested to wait until some event occurs
–
Has voluntarily delayed itself for some duration
Running state - the task is currently the one with the highest priority and is running
First Technology Transfer - Feb 2013
41
Finite State Machine Approach to Task Scheduling
First Technology Transfer - Feb 2013
42
Priority Levels ●
Where a kernel supports more than one task per priority level ●
The kernel maintains a task-ready list
One per priority level or – A single combined list Without blocked states lower priority tasks could not run. –
●
●
●
●
A task can only move to the blocked state by making a blocking call - requesting that some blocking condition be met. If higher priority tasks are not designed to block then CPU starvation may occur A blocked task remains blocked until the blocking condition is met
First Technology Transfer - Feb 2013
43
Priority Levels ●
Common blocking conditions include ●
●
● ●
A semaphore token for which a task is waiting being released A message on which a task is waiting arriving in a message queue A time delay imposed on the task expiring
When a task becomes unblocked ●
●
●
It will move from the blocked state to the ready state if it is not the highest priority task If it is the highest priority task it moves directly to the running state and preempts the currently running task The preempted task is itself moved into the appropriate position in the task-ready list
First Technology Transfer - Feb 2013
44
Task Creation and Task Management Services ●
●
●
●
Typically, a kernel will provide a task-management services API for manipulating tasks with methods for ● Creating and deleting tasks ● Controlling task scheduling ● Obtaining information about tasks Some kernels allow a program to ● First create the task ● Then start the task Some kernels provide user-configurable hooks which ● Make it possible to run programmer-supplied functions when some specific kernel event occurs ● The programmer registers the function with the kernel - by passing in the corresponding function pointer as one of the arguments to the relevant kernel API function Possible kernel events ● When a task is first created ● When a task is suspended and a context switch occurs ● When a task is deleted
First Technology Transfer - Feb 2013
45
Task Deletion ●
Deletion of tasks in embedded applications needs to be thought about.
●
An executing task
●
●
Can acquire memory
●
Access resources using other kernel objects
If a task is not deleted correctly its resources may not be released correctly and this may result in unfortunate system behaviour e.g. ●
A task obtains a semaphore to obtain exclusive access to a shared data structure
●
The task is deleted while operating on this data structure
●
The abrupt deletion of the task may result in –
A corrupt data structure - because the write operation did not complete
–
An unreleased semaphore - which means that other tasks will not be able to acquire the resource
–
A data structure that is inaccessible - because of the unreleased semaphore ●
i.e. the premature deletion of a task can result in memory or resource leaks
First Technology Transfer - Feb 2013
46
Task Scheduling API Functionality ●
●
Many kernels provide an API that permits manual scheduling. A typical set of operations supported by such an API might include the following : ●
Suspend - to suspend a task
●
Resume - to resume a task
●
Delay - to delay a task
●
Restart - to restart a task
●
GetPriority - to get the priority of the current task
●
SetPriority - to dynamically set the priority of some task
●
●
Preemption lock - to lock out higher priority tasks from preempting the current task Preemption unlock - undoes a preemption lock operation
First Technology Transfer - Feb 2013
47
Obtaining Task Information ●
●
Kernel API Functionality for Obtaining Task Information is useful e.g. when monitoring or debugging an application Information typically retrieved includes details such as ●
The IDs of the installed tasks
●
The Task Control Block of the current task
●
The resources owned by the task – –
The state of the resource The attributes of the resource
First Technology Transfer - Feb 2013
48
Varieties of Tasks ●
A common classification of tasks involves classifying them as either ●
Run to completion tasks, or
●
Infinite loop tasks
First Technology Transfer - Feb 2013
49
Run-to-completion tasks ●
The pseudocode demonstrating a run-to-completion task might look as follows ARunToCompletionTask() { InitialisationApplication; Create a number of infnite loop tasks; Create required kernel objects/resources; Delete/Suspend this task }
●
In this example of a run-to-completion task ●
A high priority task –
Initialises and starts up up a collection of tasks associated with the application and then
–
Ends itself.
First Technology Transfer - Feb 2013
50
Infinite Loop Tasks ●
Infinite loop tasks must include blocking calls if other tasks are to have a chance of running. ●
The pseudocode for an infinite loop task might look as follows InfniteLoopTask() { Initialisation steps Loop Forever { body of loop code - includes one or more blocking calls } }
●
The important point to bear in mind with the above examples is that they are based on a co-operative multi-tasking model ●
At the highest priority level a task runs to completion or till it voluntarily makes a blocking call.
First Technology Transfer - Feb 2013
51
Intertask Communication ●
Typical kernel objects for interprocess communication and synchronisation include ●
Semaphores
●
Message queues
●
Signals
●
Pipes
First Technology Transfer - Feb 2013
52
Semaphores ●
●
Formally - A semaphore is a kernel object that one or more threads of execution (tasks) can acquire or release for the purposes of synchronisation or mutual exclusion.
When a semaphore object is created the kernel assigns a data structure - a semaphore control block (SCB) to it. The kernel also assigns a unique ID, a count value and a task-waiting list.
First Technology Transfer - Feb 2013
53
Semaphores ●
●
Semaphore A semaphore can be thought of as a key which a task must have in order to access some resource ●
●
●
If the task can acquire the semaphore then it can access the resource If a task cannot acquire the semaphore it must wait till some other task releases it.
There are two main kinds of semaphore ●
Binary
●
Counting
First Technology Transfer - Feb 2013
54
Binary Semaphores ●
●
A binary semaphore can have only two possible values 0 and 1. ● When a semaphore is not held by any task it has the value 1. ● When a task acquires a semaphore its value is set to 0. ● No other task can acquire the semaphore whilst its value is 0. A binary semaphore is a global resource - shared by all tasks that need it ● Hence, it is possible for a task other than the task that initially acquired the semaphore to release the semaphore ● It is the choreography of semaphore usage amongst tasks that makes semaphores effective.
First Technology Transfer - Feb 2013
55
Counting Semaphores ●
●
●
A counting semaphore can be acquired or released multiple times. It does this via a counter. ● If the count value is 0 the counting semaphore is in the unavailable state. If the count is greater than 1 then the semaphore is available. ● When a counting semaphore is created its initial count can be specified. In some operating systems the maximum count value can also be specified - i.e. the counting semaphore is a bounded semaphore ( the count is bounded ). In other operating systems the count may be unbounded. Acquiring a counting semaphore reduces the counter value by 1, and releasing the counting semaphore increases the counter value by 1.
First Technology Transfer - Feb 2013
56
Mutual Exclusion Semaphores (Mutexes) ●
●
A mutex is a binary semaphore - that may, in some implementations, have additional features such as ●
Ownership
●
Recursive locking
●
Task deletion safety
●
Priority inversion avoidance protocol
The mutex can be in one of two states locked (0), or unlocked (1). ●
A mutex is initially created in the unlocked state - and can be acquired by a task.
●
When a mutex is acquired its state becomes the locked state.
●
●
●
When a mutex is released (by the task that owns it) its state becomes the unlocked state. [ In some implementations the verbs lock and unlock are used in place of acquire and release ]
Ownership ●
When a task acquires a mutex it is said to acquire ownership
●
When a task releases a mutex that mutex can be acquired by another task.
First Technology Transfer - Feb 2013
57
Recursive Locking ●
Recursive locking means that a task that owns a mutex can acquire it multiple times in the locked state. ●
●
In some implementation recursive locking may be automatically built into the mutex , in others it may be a property that can be explicitly enabled. ●
●
●
Such a mutex is called a recursive mutex.
A recursive mutex permits nesting of attempts to lock the mutex. A function that acquires the mutex can call another function that can acquire the mutex again, and can release the mutex just before it returns.
Task Deletion Safety Premature task deletion can be avoided by using a task deletion lock when a task locks and unlocks the mutex. ●
The task cannot be deleted while it owns the mutex. The mutex is said to have built-in task deletion safety.
First Technology Transfer - Feb 2013
58
Priority Inversion Avoidance ●
Priority inversion occurs when ●
●
●
●
A higher priority task is blocked because it is waiting for a lower priority task to release a needed mutex, and where The lower priority task has itself been preempted by an intermediate level priority task. Effectively the priority of the high priority task has been inverted to that of the low priority task.
Protocols associated with mutex implementation have been devised to avoid priority inversions ●
Priority inheritance protocol - the priority of the lower priority task that has acquired the mutex is raised to the priority level of the task that requested the mutex when inversion happens. –
●
Once the low priority task releases the mutex its priority reverts to the low level.
Ceiling priority protocol - the priority level of the task that acquires the mutex is automatically set to the highest priority of all possible tasks the might request that mutex when the task acquires the mutex, and –
Reverts to its regular priority when it releases the mutex.
First Technology Transfer - Feb 2013
59
Mutex State Diagram
First Technology Transfer - Feb 2013
60
Typical Semaphore Operations ●
●
Typical semaphore operations are ●
Creating and deleting a semaphore
●
Acquiring and releasing a semaphore
●
Clearing the task-waiting list associated with a semaphore
●
Obtaining information about a semaphore
Calls for implementing different kinds of semaphores (where a kernel supports multiple semaphores) may provide extra functionality e.g. ●
●
●
Binary semaphore - specify initial semaphore state and taskwaiting order Counting semaphore - specify initial semaphore value, bound on the count and the task-waiting order Mutex - specify the task-waiting order, enable task deletion safety, recursion, priority inversion protocols
First Technology Transfer - Feb 2013
61
Semaphore Operations ●
Deleting a Semaphore ●
● ●
When a semaphore is deleted –
Any blocked tasks in the task-waiting list are unblocked and moved into the ready state ( or into the running state if they are the highest priority tasks )
–
Any task attempting to acquire a deleted semaphore returns with an error message
N.B. - a semaphore should not be deleted if it is acquired.
Acquiring and Releasing Semaphores ●
Typical ways of acquiring semaphore –
Wait forever - task remains blocked until it is able to acquire the semaphore
–
Wait with a timeout - task remains blocked until it can acquire the semaphore, or till the time-out period specified expires ( in which case the task is placed on the ready list )
–
Do not wait - if a semaphore token is not available then the task does not block - it simply returns ( usually returning some value / setting some variable ) indicating it was not able to acquire the semaphore.
First Technology Transfer - Feb 2013
62
Semaphore Operations - ctd. ●
Interrupt Service Routines (ISRs) and Semaphores ●
●
●
●
●
ISRs may release binary and counting semaphores. It is not (in general) meaningful to acquire a binary or counting semaphore inside an ISR Locking and unlocking of mutexes inside an ISR is also not meaningful ( and most kernels do not support such an action )
Binary and Counting Semaphores and Mutexes ●
Any task can release a binary or counting semaphore
●
A mutex can only be release by the task that acquired it
Clearing Semaphore Task Waiting Lists ●
●
Some kernels provide a flush operation for clearing all tasks on a semaphore task-waiting list. The flush operation can be used as a mechanism for broadcast ( more specifically multicast ) signaling.
First Technology Transfer - Feb 2013
63
Thread Rendezvous ●
A possible use of the flush mechanism –
–
A set of tasks need to finish their particular operations, and then to block by trying to acquire a common semaphore that is made unavailable When the last task has finished its particular operations it can execute a semaphore flush operation on the common semaphore ● This will free all the tasks waiting on the semaphore's waiting list
First Technology Transfer - Feb 2013
64
Getting Semaphore Information ●
Needed in monitoring and debugging situations
●
Typical semaphore information operations are ●
●
Show information - provides general information about the semaphore Show blocked tasks - gets a list of IDs of tasks blocked on the semaphore
First Technology Transfer - Feb 2013
65
Semaphore Usage Patterns ●
Typical Semaphore Usage Patterns ●
Wait and signal synchronisation
●
Multiple task wait and signal synchronisation
●
Credit tracking synchronisation
●
Single shared resource access synchronisation
●
Recursive shared resource access synchronisation
●
Multiple shared resource access synchronisation
First Technology Transfer - Feb 2013
66
Wait and Signal Synchronisation ●
Here two tasks communicate for the purpose of synchronisation without the exchange of information. ●
Wait and Signal Synchronisation can be accomplished using a binary semaphore –
Initially the binary semaphore is unavailable ( value = 0 )
–
The higher priority task (the wait task) runs first At some point it makes a request for the semaphore ● But is blocked because the semaphore is not available ● This gives the lower priority task ( the signal task) an opportunity to run At some point the lower priority task releases the semaphore thus ●
–
●
Unblocking the higher priority task which – Then pre-empts the lower priority task
First Technology Transfer - Feb 2013
67
Multiple Task Wait and Signal Synchronisation ●
In this pattern there are several higher priority tasks ( the wait tasks ) ●
Initially the binary semaphore is unavailable ( value = 0 )
●
The higher priority tasks (the wait tasks) run first At some point each makes a request for the semaphore ● But is blocked because the semaphore is not available This gives the lower priority task ( the signal task) an opportunity to run –
●
●
At some point the lower priority task flushes the semaphore thus –
Unblocking the higher priority tasks which then pre-empt the lower priority task
First Technology Transfer - Feb 2013
68
Credit Tracking Synchronisation ●
●
In this pattern - the signaling task executes at a higher rate than the signaled task ( the wait task ), and the signaling task runs at a higher priority than the signaled task ●
Some mechanism is needed to count signalling occurrences
●
This can be done using a counting semaphore –
When a signaling task signals the semaphore count is ( atomically ) incremented ( a release operation is performed on it )
–
When the signaled task is able to run it tries to acquire the counting semaphore - and either blocks ( if the semaphore counter is at zero ) or succeeds and decrements the semaphore count ( atomically ) by one
–
The underlying assumption is that the signaling task may signal in bursts and the signaled task has a chance to "catch up" in between bursts
Example scenario: ●
An Interrupt Service Routine (ISR) –
Executes at high priority when the interrupt is triggered
–
Offloads remaining processing to a lower priority task waiting on a semaphore
First Technology Transfer - Feb 2013
69
Credit Tracking Synchronisation - ctd. ●
Pseudo code for credit tracking synchronisation tWaitTask () { .. Acquire counting semaphore .. } tSignalTask () { .. Release counting semaphore .. }
First Technology Transfer - Feb 2013
70
Synchronising Access to a Shared Resource ●
A semaphore can be used to Synchronise Access to a Shared Resource ●
●
The objective is to ensure that only one task / concurrent thread of execution at a time can access the shared resource.
The strategy is as follows: ● ●
●
●
●
Create a binary semaphore - initially in the available state In order to access the shared resource a task must first acquire the semaphore Any other task attempting to acquire this semaphore will now block When a task has finished with the shared resource it releases the semaphore A task that was blocked in its attempt to acquire the semaphore is now unblocked and can proceed to make use of the shared resource
First Technology Transfer - Feb 2013
71
Synchronising Access to a Shared Resource ●
Pseudo code for shared resource access synchronisation tAccessTask () { .. Acquire binary semaphore Make use of the shared resource ( e.g. read or write to it ) Release the binary semaphore .. }
●
Questions: ●
●
●
What if a task accidentally releases the semaphore - e.g. a task that did not acquire the semaphore in the first place ? What if there is more than one task blocking on the semaphore when the semaphore is released ? How might the use of a mutex semaphore ( a semaphore that supports the concept of ownership wherein only the task that successfully acquired the mutex can release it ) help ?
First Technology Transfer - Feb 2013
72
Synchronising Recursive Access to a Shared Resource ●
The scenario is as follows ●
●
A task, tAccessTask, calls routine A which in turn calls routine B and all three need access to the same shared resource
The pseudo code for such a scenario might be tAccessTask() { .. Acquire mutex Access shared resource Call Routine A Release mutex .. } Routine A() { .. Acquire mutex Access shared resource Call Routine B Release mutex .. } Routine B() { .. Acquire mutex Access shared resource Release mutex .. }
First Technology Transfer - Feb 2013
In this scenario , using a regular mutex , - Routine A would end up blocking on a semaphore that the task it is running in already owns. One solution to such a problem would be to use a recursive mutex - here once a task has acquired a mutex then additional attempts from the task itself, or from routines called by that task to acquire the mutex will succeed
73
Controlling Access to Multiple Equivalent Shared Resources ●
The strategy here is to use a counting semaphore ●
Initialise the count of the semaphore to the number of equivalent shared resources
When a task requests the semaphore ● The semaphore count will be decremented by one if it is greater than zero ● The task will block if the semaphore count is zero Problems can arise if a task releases a semaphore it did not first acquire –
●
First Technology Transfer - Feb 2013
74
Message Queues ●
A message queue can be used by tasks and ISRs to communicate and to synchronise with data - it ●
●
●
Utilises a buffer like object - a pipeline that, temporarily, holds messages from a sender(s) till the target receiver is able to read them Decouples sending and receiving tasks
When a message queue is created, then, typically, ●
An associated Queue Control Block (QCB) is assigned to it
●
A message queue name and a unique ID are assigned to it
●
●
Memory buffers are assigned to it - based on properties such as queue length and maximum message length One or more task waiting lists may be associated with it - typically –
A sending task - blocks when the message queue is full
–
A reading task - blocks when the message queue is empty
First Technology Transfer - Feb 2013
75
Message Queues - ctd. ●
Messages are read from the head of the message queue and added to the tail of the message queue
First Technology Transfer - Feb 2013
76
Message Queue - State Behaviour ●
When a message queue is created its initial state will be the empty state
●
If a task attempts to receive messages from an empty message queue it will block
●
●
if the task wishes to it is held in the task waiting list ( in e.g. a FIFO or priority-based order ) If a message is sent to a task blocking on an empty message queue then it will be delivered directly to the blocked task ●
● ●
The task will be removed from the task-waiting list and moved to either the ready state or to the run state The message queue remains in the empty state
If a message is sent to an empty message queue and there is no blocked task then the message queue is in the non-empty state ●
●
Additional messages will be queued up till the number of messages reaches the maximum number the queue can hold ( the queue length ) When the queue is in the full state then any tasking sending a message to that queue will fail –
Failure may be indicated ( depending on kernel implementation details ) e.g. by ●
Getting the sending function to return an error code to that task
●
Allowing the task to block and moving it to the sending-task-waiting-list
First Technology Transfer - Feb 2013
77
Message Queue - State Diagram
First Technology Transfer - Feb 2013
78
Message Sending Mechanisms ●
The details are dependent on ●
The implementation of the kernel, and also, on whether
●
Virtual memory management is in operatio and –
●
The various tasks are running in their own virtual address spaces.
Here are some possibilities: ●
The message is copied twice –
Firstly from the task's memory area to the message queue's memory area
–
Secondly from the message queue's memory area to the receiving task's memory area, however,
If there is a receiving task already blocked on the message queue then ( in some kernel implementations ) the message may be copied directly from the sending task's memory are to the receiving task's memory area In real time embedded systems the cost in terms of CPU cycles and memory requirements involved in copying may be reduced by using pointers instead ●
●
First Technology Transfer - Feb 2013
79
Memory Allocation for Message Queues ●
Memory locations for message queues are also kernel implementation dependent ●
The kernel may provide a system memory pool –
All queues share this common memory area
–
Helps reduce memory requirements if it is reasonable to assume that it will be rare for all message queues to be full to capacity at the same time
–
Problems may arise in the case of message queues needing to hold large messages which use up ( on occasions ) a large share of the available system memory
Thus causing other message queues to fail because there is not enough available memory When a message queue is created a private buffer is allocated to it ●
●
●
●
Uses up more memory More reliable - as ensures there is adequate memory available for all messages
First Technology Transfer - Feb 2013
80
Operations on Message Queues ●
Create message queue
●
Delete message queue
●
Send message to message queue
●
Blocking policy
●
●
Non-blocking - ISRs and tasks
●
Blocking with timeout - tasks only
●
Blocking for ever - tasks only
Queueing discipline ●
FIFO
●
LIFO
●
Broadcast a message
●
Multicast a message
●
Query message queue for status information
●
●
Get information about the message queue ( e.g. queue ID, queuing order ( e.g. FIFO or priority based ), number of queued messages ) Get list of tasks in the queue's task waiting list
First Technology Transfer - Feb 2013
81
Operations on Message Queues - ctd. ●
●
●
Receive message from message queue Blocking policy - on an empty message queue - blocked receiving tasks fill the task list ●
Blocking with timeout
●
Blocking for ever
Message reading policy ●
●
●
Destructive read - message permanently removed from message queue's storage buffer Mon-destructive read - receiving task "peeks" at message at head of queue but does not remove it
Queuing order ●
FIFO
●
Priority based
First Technology Transfer - Feb 2013
82
Message Queue - Use Patterns ●
●
●
Typical Patterns for Using Message Queues ●
Non-interlocked , one-way data communication
●
Interlocked , one-way data communication
●
Interlocked , two-way data communication
●
Broadcast communication
What other patterns of message queue usage have you come across ? What happens when message queueing is to be attempted on distributed systems ?
First Technology Transfer - Feb 2013
83
Loosely Coupled Data Communication ●
●
Non-Interlocked, One-Way ( Loosely Coupled ) Data Communication involves ●
A sending task - which sources messages
●
A message queue
●
A receiving task - which acts as a sink for messages
Details of behaviour will depend on the relative priorities of the sending and receiving tasks ●
Where the receiving task has higher priority it will run first until it blocks on an empty message queue –
●
When the sending task sends a message the receiving task will unblock and run again
Where the receiving task has the lower priority then the sending task will fill the message queue with messages till it blocks ( or suspends itself ) –
The receiving task will "wake up" and start processing the received messages
First Technology Transfer - Feb 2013
84
ISRs and Message Queues ●
Interrupt Service Routines (ISRs) typically use the noninterlocked, one-way data communication pattern ● ●
●
The receiving task runs and waits on a message queue When triggered, the ISR places one or more message on the message queue
ISRs must send messages in a non-blocking way ●
If the message queue if full then messages may be lost or overwritten –
Implementation dependent
First Technology Transfer - Feb 2013
85
Interlocked, One-Way Data Communication ●
In this pattern the sending task sends a message and waits to see if the message has been received ●
●
●
If the message is, for some reason, not received correctly then it can be re-transmitted The pattern can be used as means of closing a synchronisation loop so that the sending and receiving tasks operate in lockstep with one another
possible implementation of lockstep - involving a sending task, a message queue ( with a length of 1 ) , a receiving task and a binary semaphore ●
●
●
●
●
The initial value of the binary semaphore is 0 The sending task sends a message to the message queue and blocks on the binary semaphore The receiving task receives the message and increments the binary semaphore The sending task unblocks and sends the next message The semaphore is, in effect, acting as a simple acknowledgement to the sender that it is OK to send the next message
First Technology Transfer - Feb 2013
86
One-Way Data Communication example ●
Pseudocode for Interlocked, One-Way Data Communication example tSenderTask() { ... Send message to message queue Acquire binary semaphore ... } tReceiverTask() { ... Receive message from message queue Release binary semaphore ... }
First Technology Transfer - Feb 2013
87
Interlocked, Two-Way Data Communication ●
Interlocked, Two-Way Data Communication involves two tasks and two message queues ●
Synchronisation details depend on the kind of data that needs to be exchanged –
For two way exchange of data two message queues are required
–
For simple acknowledgement a semaphore can be used
tClientTask() { ... Send message to server's requests message queue Acquire binary semaphore ... } tServerTask() { ... Receive message from server's responses message queue Release binary semaphore ... } First Technology Transfer - Feb 2013
88
Broadcast Communication ●
A broadcast communication pattern involves ●
●
The broadcast sender sends a message to the message queue ●
●
A broadcast sending task, a message queue, a number of receiving tasks
Each of the receiving tasks receives the message from the message queue
Questions: ●
How might a kernel implementation support broadcast communication involving a message queue
●
How would a receiver task associate itself with a message queue?
●
Would it make sense for receiver tasks to block on an empty message queue?
●
●
●
What if a response was expected from 0 or 1 receiver tasks only - and a response from two or more receiver tasks was an error ? How would the above scenarios be modified / extended if sender and receiver tasks were running on different machines / processors? Suppose it was necessary to distinguish between ordinary messages and urgent messages?
First Technology Transfer - Feb 2013
89
PIPES ●
●
Pipes are kernel objects provided by operating systems such as Unix/Linux and Windows A pipe ● ●
●
Supports unidirectional stream oriented data exchange Is made up of two descriptors - that are returned when an instance of the pipe is created –
One for the reading end - data is read from the read descriptor
–
One for the writing end - data is written to the write descriptor
●
Data is held ( buffered ) in the pipe as an unstructured byte stream
●
Data is read from the pipe in fifo order
●
The reader process blocks when the pipe is empty
●
The writer process blocks when the pipe is full
Unlike a message queue a pipe ●
Cannot store multiple messages
●
The data in the pipe is not structured
●
The data in the pipe cannot be prioritised
●
Via the select operation a task can block an wait for a specified condition to become true on one or more pipes
First Technology Transfer - Feb 2013
90
PIPE - IPC
First Technology Transfer - Feb 2013
91
PIPE - Control Block - Creation ●
Pipe Creation and Pipe Control Blocks ●
Pipes can be created and destroyed dynamically
●
When a pipe instance is created the kernel creates a pipe control block
●
●
A pipe control block contains pipe specific information ( maintained by the kernel ) e.g. –
Size of pipe buffer - specified at creation time and then remains fixed
–
Current data byte count
–
Input and output position indicators
–
Two descriptors in file i/o space - which will be unique ●
These descriptors are returned to the task creating the pipe
●
Details vary from kernel to kernel
There are two task waiting lists associated with a pipe –
List of tasks waiting to write to the pipe ( blocked when pipe buffer is full )
–
List of tasks waiting to read from the pipe ( blocked when the pipe buffer is empty )
First Technology Transfer - Feb 2013
92
PIPE State Diagram
First Technology Transfer - Feb 2013
93
PIPE Operations ●
Create pipe
●
Destroy pipe
●
Read from pipe ●
Returns data to calling task
●
Calling task specifies how much data to read
●
●
Task may opt to block - waiting for remaining data to arrive - if amount of data requested is greater than what is available in the pipe
●
Pipe reads are destructive - data IS REMOVED FROM THE PIPE
●
Once data has been read it is not available to other readers
Write to pipe ● ●
●
●
Appends new data to existing byte stream in the pipe Task may choose to block - waiting for additional buffer space to become available - if the amount of data to be written is greater than the space available currently in the pipe buffer Because there are no message boundaries in a pipe it is not possible to determine the original producer of the data bytes Data written to a pipe CANNOT BE PRIORITISED - DO NOT USE A PIPE IF URGENT DATA NEEDS TO BE EXCHANGED BETWEEN TASKS !!!
First Technology Transfer - Feb 2013
94
PIPE Operations - ctd. ●
Pipe Control, Flush and Select Operations ( Typical ) ●
●
●
Control - the fcntl operation - used to alter the behaviour / attributes of the pipe e.g. - whether a task should block if a read operation is attempted on an empty pipe or a write operation is attempted on a full pipe Flush –
This command removes all data from the pipe and clears all conditions in the pipe so that the pipe reverts to the state it had when it was created
–
Useful for e.g. removing data that has been kept in the pipe for too long and is now out of date
Select - probably the main advantage of a pipe over a message queue –
Used to permit a task to block for some specified condition to occur in one or more pipes e.g. ●
Waiting for data to become available in a pipe(s)
●
Waiting for data to be emptied from a pipe(s)
●
–
Example - using select a task may be waiting to read from one of two pipes and write to a third
Select returns when data becomes available in one or other of the two pipes being read from, or when space becomes available in the pipe being written to
First Technology Transfer - Feb 2013
95
PIPE - Example ●
Example - using pipes for inter-task synchronisation
●
Two tasks ( A and B ) and two pipes ●
One pipe opened so that it is written to by A and read from by B
●
Other pipe opened so that it is read from by A and written to by B
●
A and B both issue a select operation on the pipes –
Task A can issue a non-blocking call to write to the pipe and perform other operations until the pipe becomes writeable ( because task B has read some data from the pipe ( i.e. task A can wait asynchronously for the data pipe to become writeable )
–
Task A can wait asynchronously for arrival of a transfer acknowledgement from task B
–
Task B can wait asynchronously for the arrival of a transfer acknowledgement from task A
–
Task B can wait for the other pipe to become writeable before sending an acknowledgement
First Technology Transfer - Feb 2013
96
Event Registers ●
An event register ●
Is associated with a task
●
Details are hardware and kernel specific
●
Is a collection or binary event flags
●
Typically 8, 16 or 32 bits Each bit in the event register is associated with some specific event
–
Each bit can either be set or cleared
●
Used by a task to check for the occurrence / non-occurrence of a particular event
●
Can be used by an ISR to inform a task that a particular event has occurred
●
●
–
A task performs conditional checks specified by a combination ( using ANDs and ORs ) of the event register bit flags
Event checking strategies can be ●
No wait
●
Wait indefinitely
●
Wait with timeout
First Technology Transfer - Feb 2013
97
Event Registers - ctd. ●
The event register control block is (typically) part of the task control block and (typically) contains the following fields ●
Wanted events register
●
Events the task wishes to receive
●
Received events –
●
Timeout value –
●
Set by task to indicate how long it wishes to wait for the arrival of the events it is "interested in"
Notification conditions –
●
Events that have "arrived" are recorded here
Specify (to the kernel) the conditions (occurring as a result of event arrivals) under which the task wishes to be woken up
The main event register operations ( typically ) are ●
Send - an event is sent to a task
●
Receive - used by a calling task to receive events from external sources –
A task can specify whether or not it wishes to wait, and,
–
If waiting, whether it wishes to wait indefinitely or only for a specified timeout period
First Technology Transfer - Feb 2013
98
Using Event Registers ●
●
●
No data is associated with an event when an event is sent through an event register Pending events in the event register do not change the execution state of the receiving task There is no mechanism for identifying the source of an event ●
Identifying protocols need to be agreed upon between senders and receivers - e.g. –
●
●
A particular task sends a particular event by setting a particular bit in the event register )
Events in the event register are not queued An event register cannot count the number of occurrences of an event while it is pending
First Technology Transfer - Feb 2013
99
Signals ●
A signal is a software interrupt ●
●
●
●
Generated in response to some event occurring Triggers the asynchronous execution ( in the target task ) of the associated signal handler routine ( as specified by the task for dealing with that signal )
The numbers and types of available signals are system and operating system dependent Typical types of events associated with signals ●
An attempt to perform an illegal operation during program execution –
●
●
●
e.g. a divide by zero
Notification from a task to another task that it is about to terminate Some user gesture - e.g. typing in a combination of characters corresponding to a "quit" command
A signal is identified by a signal number ( vector number ) - which is its position in the Signal Register / Vector Table
First Technology Transfer - Feb 2013
100
Signal Control Block ●
The signal control block is ( typically ) part of the task control block ●
●
●
Maintains a list of signals the task is prepared to handle ( catch ) A task can specify a signal handler for each signal it wishes to process, or, simply rely on a default handler A signal interrupt is often referred to as "raising a signal in the interrupted task" –
If a signal arrives while a task is processing another signal
–
The additional signal is placed in a signals pending list As soon as a task finishes processing a signal the next signal in the signals pending list can "raise a signal" on that task ●
●
●
It is possible for a task to partially process a signal and then to pass the signal on for further processing by the default handler It is possible for a task to block the delivery of a signal during certain "critical" stages in that task's processing –
The task tells the kernel to block certain signals by setting the appropriate values in the "blocked signals set" ●
The kernel will not deliver a signal listed in the "blocked signals set" until that signal is cleared from the set
First Technology Transfer - Feb 2013
101
Typical Operations on Signals ●
●
●
●
●
●
Catch - installs a handler for a specified signal ● The function pointer or offset to the asynchronous signal handler routine is entered into the corresponding element in the vector table Release - removes a previously installed handler ● A common practice is to restore the previous ( default ) handler in place of the handler that has been released Send - used by one task or ISR to send a signal to another task ● This is an explicit task level operation ● Some signals are sent automatically ( via the kernel ) when events such as memory access violations or floating point exceptions occur Ignore - used to instruct the kernel to not deliver certain signals to the task ● Some signals cannot be ignored and when they occur the kernel will call the default handler Block - used to protect critical sections of code by preventing signals from being delivered to the task ● The signal is not ignored ● Blocking can also be used to avoid nesting of signal processing e.g. when a signal arrives in the midst of ongoing signal processing Unblock - allows a previously blocked signal to pass - signal delivered immediately if already pending
First Technology Transfer - Feb 2013
102
Using Signals ●
Signals can be used ●
By an ISR To carry out less urgent processing associated with an interrupt event – To inform a task that some specific hardware event has occurred For synchronisation between tasks - but should be used sparingly, because –
●
– – –
Of overheads associated with the complexity of the signaling facility The signal alters the execution state of the target task Because signals occur asynchronously - a task that handles signals becomes non-deterministic
First Technology Transfer - Feb 2013
103
Using Signals - ctd. ●
Things to be aware of : ●
●
●
●
●
Where an implementation does not support queuing or counting of signals - multiple occurrences of a signal will overwrite each other Some implementations do not support delivery of information with the signal Some implementations do not support prioritisation of signals so that a more important signal may not necessarily be dealt with urgently Some implementations do not guarantee when an unblocked pending signal will be delivered to the target task Real- time signal handling extensions - implemented by some kernels
●
Prioritised delivery of signal - priority determined by signal number
●
Signal can carry additional information
●
Multiple occurrences of the same signal will be queued
First Technology Transfer - Feb 2013
104
Condition Variables ●
A condition variable ● ●
● ●
Used to allow one task to wait till some other task sets the shared resource to some required condition Typically deduced by evaluating some kind of predicate
When a task examines a condition variable it must have exclusive access to that variable ● ●
●
●
Is a kernel object associated with some shared resource
A mutex is therefore used in conjunction with a condition variable The task must first acquire the mutex before evaluating the predicate If the predicate evaluates to false the task blocks till the desired condition is attained
The operation of releasing the mutex and block-waiting for the condition is guaranteed by the kernel to be an atomic operation
First Technology Transfer - Feb 2013
105
Condition Variable Block ●
A condition variable block ●
Is set up when the condition variable is created
Stores ● Information associated with the condition variable ● Reference to the guarding mutex ● Reference to the task-waiting list Task awakening may be –
●
●
On a FIFO or
●
Priority based or
●
Some other kernel defined protocol
First Technology Transfer - Feb 2013
106
Condition Variable Operations ●
Create - create and initialise the condition variable
●
Wait - wait on a condition variable ●
To wait a task must first successfully acquire the guarding mutex –
●
Signal - signal the condition variable ●
Modify the condition variable to indicate some particular condition has been realised in the shared resource –
●
●
Puts task into the task-waiting queue and releases the associated mutex in a single atomic operation
Unblocks one of the tasks waiting on the condition variable
When the signal operation has completed the kernel reacquires the mutex associated with the condition variable on behalf of the unblocked task in one atomic operation
Broadcast - wakes up every task on the condition variable's task-waiting list ●
●
One task is chosen by the kernel and given the guarding mutex Other tasks are removed from the condition variable's task-waiting list and put on the task-waiting list of the condition variable's associated guarding mutex
First Technology Transfer - Feb 2013
107
Usage of Condition Variables ●
Pseudo Code Example - Using Condition Variables //Task 1: Lock Mutex Examine shared resource While ( shared resource is busy ) WAIT ( condition variable ) Mark shared resource as Busy Unlock Mutex //Task 2: Lock Mutex Mark shared resource as Free SIGNAL ( condition variable ) Unlock Mutex
●
Note: ●
A signal on a condition variable is lost when there is nothing waiting on it - hence –
A task should check for presence of desired condition before waiting on it
–
A task should check for presence of desired condition after a wakeup
First Technology Transfer - Feb 2013
108
Interrupts and Exceptions ●
●
An exception is an event that ●
Disrupts the normal execution of the processor
●
Forces the processor into execution of special instructions in a privileged state
Exceptions may be ●
Synchronous exceptions –
●
Generated as a result of execution of processor instructions (e.g. memory alignment exception, divide by zero exception)
Asynchronous exceptions ( normally called interrupts ) –
Raised by external events not directly related to the execution of processor instructions ●
●
Generally associated with hardware signals (e.g. processor reset, receipt of a packet by some network interface device)
Provide a communication mechanism between the hardware and an application currently running on the processor - used in the following contexts –
Handling of internal errors and special conditions
–
Dealing with hardware concurrency
–
Handling of service requests
First Technology Transfer - Feb 2013
109
Handling Special Conditions and Internal Errors ●
Exceptions - error conditions or special conditions
●
Special conditions ●
●
Exceptions generated by special instructions e.g. a TRAP instruction on some processors Allow a program to force the processor to move into a privileged execution mode in which it has access to a privileged instruction set e.g. –
●
Trace exception - generated by a break point feature ( on an architecture having such a feature ) - this exception is handled by the debugger agent ●
●
Instructions to disable external interrupts
Enables software breakpoints when using a host debugger and stepping through the code
For devices that perform device-specific operations in parallel with the core processor ●
●
An external interrupt can be used to inform the core processor when that device has finished a particular piece of work and is ready for additional work to be given to it An external interrupt can be used to alert/signal the core processor that the external hardware is requesting some service ( e.g. a network interface device has acquired a packet/frame that needs processing )
First Technology Transfer - Feb 2013
110
Programmable Interrupt Controllers ●
Many types Programmable Interrupt Controller (PIC) exist ●
All have the following common characteristics –
Prioritisation of multiple interrupt sources Ensuring the highest priority interrupt is presented to the CPU for processing Offloading the core CPU with the processing needed to determine the exact source of the interrupt ●
–
●
A typical PIC has ●
●
Some external interrupt request lines to which various devices and sensors can be attached The external source/device/sensor generates an interrupt by asserting a physical signal on its interrupt request line –
Each interrupt request line has a priority assigned to it ●
Important when designing and implementing ISRs that allow for nested interrupts
First Technology Transfer - Feb 2013
111
Programmable Interrupt Controllers - ctd. ●
A typical PIC has, also ●
●
●
●
A pin that is connected to one of the micro-processor's interrupt line One or more pins that can be used by the microprocessor to set or read values in the PIC registers When the microprocessor interrupt line is enabled –
The microprocessor obtains from the PIC the id of the interrupt request line that triggered the interrupt
–
Uses that id to invoke the corresponding interrupt handler by performing a lookup in the interrupt table
Details of how the interrupt entry point or offset are obtained are processor and hardware specific –
The interrupt handler can obtain device / sensor specific information by accessing the appropriate registers in the device / sensor
First Technology Transfer - Feb 2013
112
General Exception Types ●
Exceptions can be classified as ●
●
●
Asynchronous –
Maskable - can be blocked or enabled by software
–
Non-maskable - cannot be blocked by software and will always be acknowledged by the processor
–
A hardware reset exception is always non-maskable
–
A processor may also have a non-maskable interrupt request line (NMI)
Synchronous –
Precise exception - here the processor's program counter points to the exact offending instruction that caused the exceptions
–
Imprecise exception - the program counter does not point to the precise instruction that caused the exception - found e.g. in architectures that make use of heavy pipelining or use pre-fetch algorithms
Knowing the type of an exception helps when implementing the exception handler e.g. ●
How the system can recover from the exception
●
Whether it is at all possible to recover from the exception
First Technology Transfer - Feb 2013
113
Exceptions - ctd. ●
●
Typically exceptions are prioritised ( from highest to lowest priority ) ●
Asynchronous - non maskable
●
Synchronous - precise
●
Synchronous - imprecise
●
Asynchronous - maskable
Exceptions have processing priority over operating system objects - including ●
Tasks
●
Queues
●
Semaphores
First Technology Transfer - Feb 2013
114
Principles of Exception Handling ●
In general, when an exception or external interrupt is raised the processor ●
●
●
Saves the current processor state information Loads the exception or interrupt handling function address into the program counter
●
Transfers control to the handler function - which begins execution
●
Restores the processor state information after the handler function completes
●
Returns from the exception or interrupt and resumes previous execution
Typically - an interrupt handler ●
Switches to some exception frame or interrupt stack
●
Saves additional processor state information - as appropriate
●
Masks the current interrupt level - but still allows higher priority interrupts to occur
●
●
Performs ( the minimal amount necessary ) of processing needed to service the interrupt The remainder of the processing is then completed by some other dedicated task running in kernel space or in user space (details are operating system specific)
First Technology Transfer - Feb 2013
115
Installation of Exception Handlers ●
Installation of Exception Handlers ●
Requires knowledg of the structure and location of the exception and interrupt table (general exception table) –
●
●
● ●
●
The interrupt table is also called a vector table
An entry in the vector table points to the beginning of an exception service routine (ESR) or an interrupt service routine (ISR) Typically, the start up code for an embedded system installs the ESRs at system startup Typically, hardware device drivers install the ISRs at driver initialisation time if no explicit handler functions are specified for certain exceptions or interrupts the RTOS may install default handler functions (to ensure proper reception and return from exceptions) Some RTOSes provide mechanisms via which an embedded systems developer can overwrite the default handler function with one of their own, or ●
Insert further processing in addition to the default processing
First Technology Transfer - Feb 2013
116
Interrupts - Saving State Information ●
●
●
●
Saving processor state information during exception or interrupt handling ●
The details are hardware and operating system specific and
●
Typically involve some kind of stack
When an interrupt occurs the processor (typically) saves, at a minimum, the status register and the program counter The a kernel developer and interrupt handler developer needs to decide what further information needs to be saved - so that program execution can resume properly after the exception In a typical embedded operating system, when a task is created a task control block is created and a block of memory reserved for stack use ●
●
●
The stack pointer (SP) for a given task points to the top of the stack data in the stack memory area for that task Whenever a context switch occurs the active stack pointer is reinitialised to the SP for the active task
When an exception or interrupt occurs the processor uses whichever stack the SP for the currently active is pointing to store its minimum state information
First Technology Transfer - Feb 2013
117
Nested Exceptions ●
Where a higher priority interrupt service routine can interrupt a running lower priority service routine issues that need to be considered include ●
●
Considering if the application stack large enough to accommodate the maximum expected number of nested interrupts ? Thinking about how a stack overflow exception arising during exception handling to be dealt with ? –
If stack overflow is not detected ( e.g. because there is no bounds checking on the stack ) - data residing beyond the bounds of the stack may be overwritten ●
What if this is Task Control Block ( TCB ) data ? – These kinds of error may be sporadic and difficult to reproduce – Consider having an ESR or ISR use its own stack ● Such as stack is called an exception frame
First Technology Transfer - Feb 2013
118
Exception Handler Issues ●
●
●
Exception handlers are commonly written using a mixture of assembler and a high level programming language such as C or C++ An exception handler, typically, is made up of two parts ●
The first part runs in the exception or interrupt context
●
The second part runs in a task context
A common strategy when implementing ESRs or ISRs is for the ESR or ISR to allocate a block of memory for its own private stack (either statically or dynamically) before it installs itself into the system ●
●
The exception handler then saves the current stack pointer into some temporary memory location and reinitialises the stack pointer to point to its private stack and then begins processing Prior to returning the exception handler resets the stack pointer to the previously saved value
First Technology Transfer - Feb 2013
119
Masking Interrupts and ISRs ●
The three main ways of masking interrupts are ●
Disable a device so that it cannot assert additional interrupts –
Interrupts at all levels (from other devices) can still occur
–
Mask interrupts at equal or lower priority levels
–
Higher priority interrupts can still occur ●
●
The device can continue to generate interrupts - but they are ignored by the processor
Disable the global system-wide interrupt request line to the processor –
No interrupts ( whatever their priority level ) reach the processor ●
Equivalent to masking interrupts at the highest priority level
●
ISR wants to reduce the total number of interrupts raised by the device
●
ISR is non-reentrant
●
ISR needs to carry out some atomic operations
●
The above methods are used by ISRs for one or more of the following reasons
●
Where an architecture uses an interrupt mask register ( IMR ) - this will need to be saved by the ISR - which will set the bits of the IMR to what it needs, and then the saved values will be restored when the ISR returns.
First Technology Transfer - Feb 2013
120
Timing Exception Processing ●
●
●
Hardware designer needs to use the appropriate PIC priority level Programmer ( of the ISR ) needs to understand the timing requirements of each device when the ISR is running ●
With interrupts disabled
●
With equal or lower priority interrupts disabled
●
With all interrupts disabled
When interrupts are disabled thought needs to be given to interrupt miss situations ●
●
Important issue for devices using edge-triggering as a mechanism for asserting an interrupt
RTOS kernel scheduler cannot run when a running ISR disables all system interrupts ●
May therefore effect the time deadlines of other tasks
First Technology Transfer - Feb 2013
121
Exception Timing Issues ●
When analysing exception timing issues the following concepts ( terms ) are often used ●
●
●
Time interval between interrupts ( depends on interrupt frequency ) Interrupt latency - the time interval between the time when the interrupt is raised and the time when the ISR begins to execute depends on –
Time taken by the processor to acknowledge the interrupt and carry out initial housekeeping work
–
Whether a higher priority interrupt is active at the time
–
Whether the interrupt is disabled and the later re-enabled by software
Interrupt processing time equation : Response Time = Interrupt Latency + Interrupt Processing Time
First Technology Transfer - Feb 2013
122
Two Stage Interrupt Handling ●
Interrupt Processing may be split between ●
●
Interrupt context work - this part of the ISR –
Services the device - acknowledges the request and
–
Puts the device into a known operational state in which it can resume operation
Task context work - deals with the remainder of the processing of the interrupt e.g. parsing an input frame header(s) –
●
Possible advantages of splitting interrupt handling ●
Lower priority interrupts can be handled less urgently than more critical tasks
●
There is less chance of missing interrupts
●
●
Task context work may be implemented as some kind of daemon
Concurrency is improved because minimal servicing of devices means they can continue operations while previous requests are queued up for processing when the demands on the system are less
Consequences of splitting interrupt processing ●
Interrupt response time increases - because of scheduling delays and the possibility that the daemon task may need to yield to other higher priority tasks
First Technology Transfer - Feb 2013
123
Spurious Interrupts ●
A spurious interrupt is ●
A signal of short duration on one of the interrupt lines
Probably caused by some kind of signal "glitch" The possibility of spurious interrupts occurring needs to be considered carefully when working with devices that use level or edge triggering to assert an interrupt –
●
●
Dealing with spurious interrupts often involves use of techniques such as e.g. ●
Debouncing
●
Filtering
First Technology Transfer - Feb 2013
124
Timers and Timer Services ●
●
Timers and Timer Services are vital in the scheduling of future events without them operating system schedulers could not be implemented. Uses of timers include : ●
●
●
●
Round robin scheduling Scheduling data retransmission and protocol recovery in communications protocols Collection of system timing and performance information
Time sensitive activities in an embedded system may be driven by ●
Hard timers - derived from physical timer circuitry that directly interrupts the processor when the timer expires –
●
Used where timing precision and latency needs to be very predictable
Soft timers - software events scheduled via software –
Emphasis is on efficient scheduling of software events that do not require high timing precision
First Technology Transfer - Feb 2013
125
Types of Timer ●
Types of time include - Real Time Clocks, System Clocks and Interval Timers ●
●
Real Time Clock –
Tracks time, date, month, year
–
Commonly integrated with battery-powered DRAM
–
Independent of CPU and programmable interval timer
–
Real time can be maintained between system power cycles
System Clock ●
Performs essentially the same job as a Real Time Clock
●
Can track elapsed time since system power up
●
●
Initial value typically retrieved from real time clock at power up, or, set by the user Driven by the programmable interval timer
First Technology Transfer - Feb 2013
126
Programmable Interval Timer (PIT) ●
Programmable Interval Timer (PIT) ● Driven by a fixed frequency input clock source ● Contains a set of programmable timer control registers – Timer interrupt rate is the number of timer interrupts generated per second ● Set by setting a value specifying the appropriate scaling factor to be applied to the timer input clock pulses – Timer countdown value ● Determines when next timer interrupt occurs ● Loaded into the relevant control register ● Decremented every "scaled" clock cycle ● Timer generates an interrupt when this value reaches zero ● Depending on the type of clock there may be other registers that determine e.g. – Whether periodic timer interrupts are generated ● Whether the countdown value should be automatically reloaded for the next timer interrupt
First Technology Transfer - Feb 2013
127
Timer hardware initialisation ●
Normally performed as part of system startup ●
●
●
Reset and bring timer hardware to a known initial state Load correct value into timer control register that sets the timer interrupt frequency Initialise other timer control registers to appropriate values
●
Install timer interrupt service routine
●
Enable timer interrupt
First Technology Transfer - Feb 2013
128
Timer Interrupt Service Routine ●
A typical timer ISR ●
●
●
●
Updates the system clock Calls the registered kernel function to notify the passage of a preprogrammed period of time Acknowledges interrupt - reinitialises relevant timer control registers Returns from the interrupt
First Technology Transfer - Feb 2013
129
Soft Timer Handling ●
Pattern for Implementing a Soft-Timer Handling Facility
●
A soft-timer handling facility should
●
●
●
Permit an application to start a timer
●
Permit an application to stop or cancel a previously installed timer
●
Take care of maintaining the collection of application timers
Soft timer processing involves ●
Processing carried out within the timer ISR itself
●
Processing carried out within a task context
Typically the time resolution required by the soft timer facility is much less than the time interval between successive timer interrupt events e.g. ●
●
Timer interrupt events might be generated every 10 msecs and the time resolution for the soft timer facility may be 100 msec
A common strategy is to use some kind of counter in the timer ISR that is decremented on every timer interrupt event and unblocks a soft timer facility worker task when it counts down to zero
First Technology Transfer - Feb 2013
130
Soft Timer Handling - ctd. ●
●
The software timer facility maintains a table of count down values ( corresponding to a multiple of the soft timer time interval e.g. 100 msec ) and associated application timeout functions to be called when the count down value reaches zero. The worker task traverses this table and for each entry ● ●
●
●
Decrements the count down value When the count down value reaches zero invokes the associated application timeout function
Where a large number of application timeout functions is involved a linear collection data structure such as a table or linked list is not the most efficient - as entries are not ordered by countdown value. Delays to the actual running of the worker task ●
Time to complete the timer ISR
●
Time taken to schedule the worker task
●
Time before the worker task actually runs - it may, for example, be delayed from running by the execution of some other high priority task
First Technology Transfer - Feb 2013
131
Timing Wheel Pattern ●
●
Requirements for efficient insertion, deletion, cancellation and update of soft timers, are, to some extent mutually conflicting ... Where the number of application tasks is large then linear traversal of an array or a doubly linked list ( doubly linked because insertions are more efficient in doubly linked lists ) can be time ( and CPU cycle ) consuming.
●
One implementation optimisation strategy is the "Timing Wheel".
●
The timing wheel ●
●
Is a circular buffer Each slot represents a unit of time ( with respect to the precision of the software timer ) e.g. if this value is 100 msec then each slot represents the passing of 100 msec.
●
A clock dial pointer points to the slot corresponding to the current time
●
Slots further along represent the future
●
For every "soft timer tick" the clock dial pointer moves along to the next time slot
●
Each slot points to a doubly linked list of timeout event handlers (also called callback functions ( callbacks for short )) –
It is a list of events with the same expiration time
First Technology Transfer - Feb 2013
132
Timing Wheel Implementation
First Technology Transfer - Feb 2013
133
Timing Wheel Issues ●
Issues With The Timing Wheel Approach ●
Finite size of timing wheel (circular) buffer –
e.g. if time slots are at 100 msec intervals and the circular buffer contains 10 slots then applications can only be scheduled for up to 1000 msec ahead
–
Possible solution is to use a temporary buffer for "overflow condition" events and to make them schedulable when the clock dial has "gone round" the appropriate number of times
–
Use a hierarchical timing wheel e.g. ●
A wheel in units of 100 msec
●
A wheel in units of seconds
●
A wheel in units of minutes
●
Determining / setting bounds on soft-time handler invocation
●
Details are implementation and application design specific –
This is a "difficult" problem
–
One approach is to use modeling and simulation techniques (e.g. based on timed Petri Nets or Coloured Petri Nets)
First Technology Transfer - Feb 2013
134
Real Time I/O ●
●
From the programming perspective Input-Output ( I/O ) Operations in Real Time and Embedded Systems imply ●
Communicating with the device
●
Programming the device to initiate an I/O request
●
Performing transfer of data between the device and the system
●
Providing notification when the operation completes
This requires ●
●
Knowledge of hardware details of the device - register definitions and access methods Being able to locate the correct device when there are multiple instances of it on the system
●
Details of how the device is integrated with the rest of the system
●
Knowing how to handle errors that can occur during I/O operations
First Technology Transfer - Feb 2013
135
Real Time I/O ●
From the RTOS perspective I/O operations imply ●
Locating the correct device for the I/O request
●
Locating the correct device driver for that device
●
Issuing the appropriate request to the device driver
●
●
The RTOS may have to provide an abstraction that hide the device driver characteristics and specific details from application developers
Ideally, an application developer wants a simple and uniform way to communicate with all the different kinds of devices present in the system
First Technology Transfer - Feb 2013
136
Basic Principles of I/O ●
●
●
I/O subsystems are implemented on the basis of a layered model The I/O subsystem hides device specific information from the kernel and from user applications ●
Provides a uniform access method to the peripheral I/O devices of the system
●
Interacts with the device drivers which in turn
●
Deals with hardware and interrupt handler details
Devices may be ●
●
●
●
Port mapped - device address space is separate from memory address space Memory mapped - device address space is part of the system memory address space If direct memory access (DMA) is not available then data transfer between the device and the system involves –
Transfer of data from the device register to the processor register and then
–
Trom the processor register to memory
A DMA controller permits direct transfer of data from the device to memory ( and vice versa ) without using the processor
First Technology Transfer - Feb 2013
137
Character-Mode vs. Block-Mode Devices ●
In character-mode devices data is transferred a byte at a time ●
Typically in a serial fashion
●
Normally used for simple devices such as keypads or RS232 links
●
●
Drivers may buffer the data if transfer rate from system to device is faster than the device can handle
In block-mode devices data is transferred a block at a time (typical block sizes are 512 bytes or 1024 bytes) ●
Where the amount of data to be transferred is not sufficient to fill a block –
The block may be padded out to the appropriate size
–
Alternatively the driver may cache a number of smaller writes to fill up the block
First Technology Transfer - Feb 2013
138
Typical I/O Subsystem Implementation Pattern ●
●
I/O subsystem defines the API set ●
Device driver implements each function in the set
●
Device driver exports this set of functions to the I/O subsystem
●
Device driver –
Does the work needed to set up the device for use
–
Sets up the association between the I/O subsystem API and the corresponding device-specific I/O calls
A typical standard set of I/O functions is ●
create - create a virtual instance of an I/O device
●
destroy - delete a virtual instance of an I/O device
●
open - prepare an I/O device for use
●
close - indicate to a device that its services are no longer required - usually this initiates a set of device specific cleanup operations
●
read - read data from the I/O device
●
write - write data to the I/O device
●
ioctl - issue control commands to the I/O device
First Technology Transfer - Feb 2013
139
Subsystem API ●
A common strategy for mapping generic I/O subsystem functions to driver functions when implementing driver code in C is to ●
Define a data structure which is a collection of function pointers e.g. typedef struct { int (*Create)(void); int (*Open)(void); int (*Read)(void); int(*Write)(void); int (*Close)(void); int(*Ioctl)(void); int(*Destroy)(void); } IO_DRV_INTERFACE;
●
Then, a variable of this type can be defined and initialised so that the various function pointers point to the actual device specific functions.
First Technology Transfer - Feb 2013
140
Device Driver Table ●
Typically the I/O subsystem will contain a driver table - an array of structures ( records ) of the form typedef struct { int device_id; char device_name[32]; IO_DRV_INTERFACE * driver_methods; void * instance_specifc_data; } driver_table_record ;
First Technology Transfer - Feb 2013
141
Memory Management ●
●
●
●
●
Typically an embedded system RTOS will provide some memory management services - usually via system calls such as malloc() and free() A problem with applications that make frequent calls to malloc() and free() is memory fragmentation Embedded application developers will often implement custom memory management facilities on top of these basic services. In its most basic form, dynamic memory allocation takes place from a contiguous block of memory called the heap A memory management facility maintains information about the heap in a reserved (control block) area of memory which includes things such as ●
Starting address of the memory block
●
Total size of the memory block
●
Allocation table which tracks
●
Areas of memory in use
●
Area of memory that are free
●
Size of each free area of memory
First Technology Transfer - Feb 2013
142
Memory Management - ctd. ●
●
Memory is normally allocated in multiples of some fixed block size e.g. 32 bytes When a request for memory is made the smallest number of contiguous blocks that can satisfy that request is allocated. ●
●
●
A bit-string can be used to efficiently track which blocks of memory have been allocated and which are free
●
Each bit position corresponds to a corresponding block position
●
If the bit at that position is 0 then the block is free
●
If the bit at that position is 1 then the block is taken ( i.e. has been allocated )
A possible technique for handling memory fragmentation is to use some form of memory compaction to combine small free blocks into one larger block of contiguous memory - its disadvantages include ●
Need for block copying of data
●
Inability of an application to access data whilst it is being block copied
●
Requires that tasks which own the blocks access them using virtual addressing
Memory management needs to take architecture-specific memory alignment requirements into account ( e.g. multi-byte data items such as long integers may need to be aligned on an address that is a multiple of 4 )
First Technology Transfer - Feb 2013
143
Memory Manager - Requirements ●
Desirable Properties of an Efficient Memory Manager ●
●
●
Ability to rapidly determine if there is available a large enough block to satisfy the memory request - part of the malloc operation Ability to rapidly update internal control block memory management information - part of both the malloc and free operations Ability to rapidly determine if a newly freed block can be combined with its neighbouring free blocks to form a larger contiguous free block - part of the free operation
First Technology Transfer - Feb 2013
144
Basic Malloc and Free ●
A commonly used simple approach to the implementation of malloc and free is to
●
Use a fixed size static allocation array to implement the allocation map and
●
A heap to rapidly locate the largest available free block ●
●
Also implemented as a fixed size static array
Allocation map ●
●
The number of entries in the array is equal to the number of basic memory blocks ( e.g. if a basic memory block was 32 bytes and the amount of available memory was 1 MB then the array would have 32,768 entries The encoding scheme used is as follows : –
To indicate a range of contiguous free blocks a positive number (equal to the number of contiguous free blocks) is placed in the first and last entry representing the range of free blocks
–
To indicate a range of allocated blocks a negative number (the number of blocks in the range times -1 ) is places in the first entry representing the range, and a zero is placed in the last entry representing the range
–
The starting address of a block of bytes can be computed using the formula starting_address = offset + unit_size * index_in_allocation_map
First Technology Transfer - Feb 2013
145
A Heap ●
A heap is, logically, a binary tree in which the ordering rule is that the value of the key in the parent node is "greater than or equal to" the value in the child nodes ●
●
A consequence of this ordering property is that a "heap" binary tree can be implemented as an array Each node in the "heap" binary tree array is a data structure (record) that contains – –
●
The size of the free block represented by that node The starting index of that block in the allocation array
malloc always carves out memory to allocate out of the largest available free block
First Technology Transfer - Feb 2013
146
Malloc Algorithm - Outline ●
●
●
●
●
●
Examine the heap to see if a free block large enough for the number of bytes asked for in the allocation request exists Return an error indication to the caller e.g. a null pointer value) if there is no such block Fetch the starting allocation-array index of the free range from the top of the heap Update the allocation array by marking the newly allocated block If the entire block is used to satisfy the allocation then update the heap by deleting the largest node If part of the block is to be used then update the values for the node structure at the top of the heap and rearrange the heap (heapify) so that the currently (after allocation) largest free block is at the top of the heap
First Technology Transfer - Feb 2013
147
Fixed Size Memory Management ●
Where the range of sizes of memory block to be allocated does not vary widely and it is known that memory allocation can be satisfied by allocating one of number of fixed sized memory blocks ( e.g. 32 bytes, 64 bytes, 128 bytes ) then ●
●
●
Free memory blocks of a given size can be maintained as a linked list of free nodes Each list has an associated control structure (handle) that tracks e.g. the number of nodes on the free list
Advantages of this approach ● ●
●
It avoids memory fragmentation It can be more deterministic as compared to the heap - where the time taken by the "heapifying" operation will depend on the actual structure of the heap binary tree Free memory blocks are taken from and added to the front of the free list - a constant time operation
First Technology Transfer - Feb 2013
148
Blocking vs. Non-blocking Memory Allocation ●
●
●
In many embedded real time applications in which there is limited memory and multiple tasks are competing for that memory it may be that a temporary "memory exhaustion" may arise. To cope with such situations a good memory allocation design should allow for ●
Memory allocation with indefinite blocking
●
Memory allocation with blocking for some timeout period
●
Memory allocation with no blocking
One way of realising such a design is to implement a blocking memory allocation function via use of a mutex lock and a counting semaphore ●
As outlined in the following diagram :
First Technology Transfer - Feb 2013
149
Blocking Memory Allocation
First Technology Transfer - Feb 2013
150
Blocking Memory Allocation - Explained ●
●
●
●
Counting semaphore initialised to total number of available memory blocks when memory pool is created Memory blocks are freed and allocated from the head of the linked list The mutex lock is used to guarantee exclusive task access to both the freeblocks list and to the control structure For an allocation request ●
Task must first acquire the counting semaphore
●
If no blocks are available the task blocks on the counting semaphore
●
Task must next acquire the mutex lock
●
If another task is in the middle of acquiring a block then this task blocks
●
●
When the mutex lock is acquired the task retrieves the block from the free-blocks list
A task releases the counting semaphore when it has finished with the memory block
First Technology Transfer - Feb 2013
151
Memory Allocation - Deallocation ●
Pseudo code for memory allocation and memory deallocation : /* Memory Allocation */ Acquire ( counting_semaphore ) Lock ( mutex ) Retrieve memory block from pool Unlock ( mutex ) /* Memory Deallocation */ Lock ( mutex ) Release memory block back into the pool Unlock ( mutex ) Release ( counting_semaphore )
First Technology Transfer - Feb 2013
152
Synchronisation - Patterns and Strategies ●
The two main categories of synchronisation are ●
●
●
Resource synchronisation –
Concerned with safe access to some shared resource(s)
–
Makes use of synchronistion primitives such as mutexes and semaphores in the implementation of mutual exclusion algorithms
Activity synchronisation –
Concerned with situations in multi-threaded / multi-tasking applications where the collection of co-operating tasks needs to reach a certain state before they can proceed
–
A task in the collection will block till all the tasks have reached the required state
Barrier synchronisation –
Task posts its arrival at a barrier
–
Task waits for other participating tasks to reach that barrier
–
Task receives notification to proceed beyond the barrier
First Technology Transfer - Feb 2013
153
Synchronisation - Patterns and Strategies - ctd. ●
Rendezvous synchronisation ●
Involves uses of synchronisation and communication point (called an entry) –
One task defines an entry and makes it public
–
Other task calls the entry ( as an ordinary function call )
–
The issuer of the entry call is blocked if the call cannot be accepted
–
The task defining the entry ●
Accepts the call
●
Executes it
●
Returns the results to the caller
●
The issuer of the call establishes a rendezvous with the task defined in the entry
●
Note: In the rendezvous bi-directional movement of data is possible
●
●
Arguments passed into the entry call
●
Results returned from the entry call
A rendezvous that does not involve data passing (a simple rendezvous) can be implemented between two tasks by using two binary semaphores
First Technology Transfer - Feb 2013
154
Simple Rendezvous
First Technology Transfer - Feb 2013
155
Barrier Synchronisation ●
Pseudo code illustrating a way to implement barrier synchronisation typedef struct { mutex_type condition_var_type int int } barrier_type;
barrier_lock; barrier_condition; barrier_count; number_of_threads;
barrier_call( barrier_type * barr ) { lock_mutex(&(barr->barrier_lock)); barr->barrier_count++; if(barr->barrier_count < barr->number_of_threads) condition_wait(&(barr->barrier_condition),&(barr->barrier_lock)); else { barr->barrier_count = 0; condition_broadcast(&(barr->barrier_condition)); } unlock_mutex(&(barr->barrier_lock)); } First Technology Transfer - Feb 2013
156
Communication Patterns ●
●
One way of classifying communication is into ● Signal centric – All necessary information is conveyed in the event signal itself ● Data centric – Information is carried in the data transferred ● A combination of signal centric and data centric Another way of classifying communication is into ● Loosely coupled communication – Data producer does not require a response from the data consumer e.g. ● An ISR posting messages on a message queue ● Tightly coupled communication – Involves bidirectional transfer of data ● Data producer waits synchronously for a response to its data transfer before continuing execution, or ● Response is returned asynchronously while the producer continues processing e.g. – task A writes messages to a message queue read by task B – task B writes messages to a (different) message queue read by task A
First Technology Transfer - Feb 2013
157
Critical Section Design Issues ●
●
●
●
●
A critical section in a task is a fragment of code that accesses a shared resource. A competing critical section is a fragment of code in another task that accesses the same shared resource Critical sections need to be guarded by a suitable mutual exclusion mechanism to ensure that each task has exclusive use to the shared resource when it needs it (e.g. when writing to a shared area of memory) Where critical time deadlines have to be met the size and complexity of critical section code will be important Ideally a mutual exclusion mechanism should ●
●
●
Guarantee that only one task can enter its critical section at any given time Ensure that multiple competing tasks are granted fair access to the shared resource Ensure that a task executing in its critical section does not prevent another task from executing in a non-competing critical section
First Technology Transfer - Feb 2013
158
Common Activity Synchronisation Design Patterns ●
For any given RTOS or Embedded OS you should be able to implement (where the primitives are available , or, if necessary implement some primitives in terms of other primitives) the following design patterns. ●
●
●
●
For each implementation the behavioural implications of having the tasks at different priorities needs to be taken into account.
No. 1. Synchronising two tasks using a single binary semaphore ●
The initial value of the semaphore is 0
●
Task B uses the acquire operation on the shared semaphore
●
Task A uses the release operation on the shared semaphore
No. 2. Synchronising an ISR with a task using a single binary semaphore ●
The initial value of the semaphore is 0
●
The task uses the acquire operation on the shared semaphore
●
The ISR uses the release operation on the shared semaphore
Nos. 3 and 4 - variants of 1 and 2 but using event registers instead of the binary semaphore
First Technology Transfer - Feb 2013
159
Common Activity Synchronisation Design Patterns ●
●
No. 5. Synchronising an ISR with a task using a counting semaphore ●
Similar to 2. but using a counting semaphore
●
Combines the accumulation of event occurrences with event signalling
●
The task can run as long as the counting semaphore is non zero
No. 6. Simple Rendezvous with Data Passing ●
●
● ●
●
●
Involves two tasks ( task A and task B ) and two message queues ( message queue A and message queue B ) Each message queue can hold at most one message ( such structures are also called mailboxes in some operating systems ) Both message queues are initially empty When task A reaches the rendezvous it puts a message into message queue B and waits for a message to arrive on message queue A When task B reaches the rendezvous it puts data into message queue A and waits for data to arrive on message queue B
No. 7. Asynchronous event notification using signals ● ●
Task A generates a signal to task B Task B diverts from its normal execution path and executes its asynchronous signal handler routine
First Technology Transfer - Feb 2013
160
Common Resource Synchronisation Design Patterns ●
No. 1. Accessing shared memory via mutexes ●
●
●
●
Task A and task B share a common mutex and a common memory area Each task must first acquire the mutex before it can access the shared memory Each task must release the mutex when it has finished accessing the shared memory
No. 2. Accessing shared memory with interrupt locks ●
●
●
●
●
This pattern involves an interrupt service routine (ISR), a task and an interrupt lock An interrupt lock (supported by some processor architectures) is used to disable an interrupt and block all other interrupts at or below that level The task must acquire the interrupt lock before accessing the shared memory and release it afterwards This is in order to prevent the ISR disrupting its access to the shared memory The ISR does not need to be aware of the interrupt lock
First Technology Transfer - Feb 2013
161
Common Resource Synchronisation Design Patterns ●
No. 3. Accessing shared memory with preemption locks ●
●
●
●
●
This pattern involves two tasks a preemption lock and shared memory Preemption locking involves disabling the kernel scheduler so that it will not preempt the task that has taken out the preemption lock Each task is responsible for disabling preemption before it accesses The shared memory and for re-enabling preemption when it has finished accessing the shared memory Unlike the situation with binary semaphores and mutexes no waiting is involved
First Technology Transfer - Feb 2013
162
Common Resource Synchronisation Design Patterns ●
No. 4. Sharing Multiple Resource Instances via Counting Semaphores and Mutexes ●
In this pattern there are N tasks sharing M resources, one counting semaphore and one mutex e.g. –
●
●
●
●
N spoolers sharing M printers
The counting semaphore is initialised to the maximum number of available resources A task must acquire the counting semaphore before attempting to access a resource A mutex is used to provide a task with exclusive access to the shared resource control structure ( which e.g. tracks information about which resources are currently in use and which are available ) A task must acquire this mutex before –
Allocating a resource instance
–
Freeing a resource instance
First Technology Transfer - Feb 2013
163
Data Transfer With Flow Control ●
The aim of this pattern if for a consumer task to control the flow of data from the producer. ●
●
It involves a producer task, a consumer task, a counting semaphore, a data buffer For the producer to be able to write to the buffer it must acquire the counting semaphore –
●
●
●
The producer will block when the value of the counting semaphore is 0
The consumer is able to release the counting semaphore ( increase its count value ) Initially the counting semaphore is set to some permissible token value ( less than the maximum allowable token value ) The consumer is able to control the flow rate by increasing the value of the counting semaphore appropriately in relation to its ability to consume data.
First Technology Transfer - Feb 2013
164
Asynch Data - Multiple Sources ●
Handling the Asynchronous Reception of Data from Multiple Data Communication Channels ●
The pattern here involves Multiple ISRs, a semaphore, an interrupt lock and a daemon task Each ISR inserts its data into a corresponding message queue and performs a release operation on the semaphore –
●
●
The daemon task – – –
Blocks and waits on the semaphore ( acquire operation ) Takes out an interrupt lock ( this lock needs to protect against the various multiple interrupt sources ) Processes the available data
First Technology Transfer - Feb 2013
165
Asynch Data - Multiple Sources ●
Pseudo code snippet : while( acquire(Binary_semaphore)) disable(interrupts) for each message_queue get msg_queue_length for (msg_queue_length) retrieve message enable (interrupts) process message disable (interrupts ) end for end for enable (interrupts) end while
First Technology Transfer - Feb 2013
166
Implementing an Event-Register ●
Implementing an Event-Register Using a Shared Variable, an Interrupt Lock and a Semaphore ● The interrupt lock is used to guard the shared variable ( ISRs can generate events through the shared variable ) ● The semaphore blocks the task waiting on the desired event Event_receive(wanted_events) { task_cb.wanted_events = wanted_events while(TRUE) acquire(task_cb.event_semaphore) disable(interrupts) events = wanted_events XOR task_cb.received_events task_cb.wanted_events = task_cb.wanted_events AND ( NOT events ) enable (interrupts) if( events is not empty ) return (events) end if end while }
First Technology Transfer - Feb 2013
167
Implementing an Event-Register - ctd. Event_send(events) { disable(interrupts) task_cb.received_events = task_cb.received_events OR events enable interrupts release (task_cb.event_semaphore) }
First Technology Transfer - Feb 2013
168
Handling Multiple Data and Event Inputs ●
●
In this pattern the daemon task has multiple data input sources ( tasks generating input data) and multiple event input sources ( ISRs generating input data ) A simple example might be a daemon that processes data from an I/O device and has a periodic timer ●
Used for recovery if the device is stuck in an inconsistent state –
●
The design pattern described here uses ●
●
[in some systems this functionality is provided by a watchdog timer]
An event register - with a given input being assigned to a given bit in the event register
●
A counting semaphore for event accumulation - for each event input source
●
A message queue for each data input source
In this design pattern ●
●
The data producer puts a message in its message queue and sets the corresponding bit in the event register The ISR increments its counting semaphore and sets its corresponding bit in the event register
First Technology Transfer - Feb 2013
169
Handling Multiple Data and Event Inputs - ctd. ●
Pseudo code for the daemon for one timer ISR and one data producer: while( have_events = wait for events from event_register ) if ( have_events & DATA_EVENT ) while( fetch message from message_queue ) process message end while end if if ( have_events & TIMER_EVENT ) counter = 0 disable ( interrupts ) while ( acquire ( counting_semaphore ) ) counter = counter + 1 end while enable ( interrupts ) if ( counter > MAX_COUNT ) do_recovery else handle_tick_event end if end if end while
First Technology Transfer - Feb 2013
170
Sending Urgent / High Priority Data Between Tasks ●
●
●
●
●
●
In this design pattern urgent data is placed into an urgent message queue The remaining data is placed into a normal message queue When a data producer task or ISR has regular data it places that data into its normal data message queue When a data producer task or ISR has urgent data to handle it places that data into an urgent message queue and signals the consumer task. The consumer task's urgent data signal handler retrieves urgent data from the urgent data message queue and deals with it This pattern can be further enhanced by adding flow control mechanisms to control the flow of urgent data.
First Technology Transfer - Feb 2013
171
Deadlocks ●
●
A Deadlock is a state in which multiple concurrent threads of execution in a system are permanently locked because they have resource requirements that cannot be satisfied. The conditions necessary for deadlock to arise are ●
●
Mutual exclusion - a resource can only be accessed by one task at a time No preemption - a non preemptible resource cannot be forcibly removed from the task holding that resource –
●
●
It only becomes available if the task gives it up voluntarily
Hold and wait - a task holds on to resources it has already acquired while waiting to acquire further resources Circular wait - a circular chain of two or more tasks exists where each task holds one or more resources required by the next task in the chain
First Technology Transfer - Feb 2013
172
Deadlocks - ctd. ●
Whether deadlock will occur or not is influenced by the resource request model - common ones being ●
Single resource request model –
●
●
●
Task can have at most one outstanding resource request at a time
AND resource request model –
Task can have multiple outstanding resource requests
–
Task blocked until all the outstanding resource requests are granted
OR resource request model –
Task can have multiple outstanding resource requests
–
Task resumes execution as soon as one or more of the resources requested become available
AND-OR resource request model - which is a combination of the previous two
First Technology Transfer - Feb 2013
173
Deadlock Detection ●
A stable deadlock is one that can only be resolved by some external influence ●
●
Many RTOS come with a deadlock detection mechanism - a global algorithm that is run periodically ●
●
Arises when no task in the deadlocked set expects a timeout or an abort that will eliminate the deadlock
It detects deadlocks in the system by examining the current state of resource allocation and the pending resource requests
●
It is intrusive on the execution of the tasks involved in the deadlock
●
The tasks involved in the deadlock are not "deadlock aware"
A temporal deadlock is one where one or more tasks of the deadlocked set either times out or aborts as a result of timing constraints. ●
When a task times out of aborts it –
Frees up any resources it was holding at the time ●
●
This might result in freeing up resources that were causing the deadlock
The task is "deadlock aware"
First Technology Transfer - Feb 2013
174
Deadlock Avoidance ●
This is an algorithm deployed by the resource allocation system ●
●
●
The algorithm predicts whether the current allocation request will eventually lead to a deadlock in the future if it is granted Each time a resource request is made the system tests to see if granting this request will allow the remaining resources to be granted to different tasks in subsequent allocations so that all tasks can run to completion For deadlock avoidance to work each task needs to be able to estimate its maximum resource requirements per resource type in advance –
Difficult to achieve in highly dynamic systems
–
Manageable in systems with predictable operating environments
First Technology Transfer - Feb 2013
175
Deadlock Prevention ●
Deadlock prevention consists of a set of constraints and requirements that are built into the system such that a resource request that might lead to a deadlock is not permitted. ●
These constraints and requirements are designed to eliminate one or more of the conditions that might lead to deadlock e.g. –
Elimination of hold-and-wait
A task must acquire all the resources it needs before starting execution Drawbacks ●
●
●
●
●
Difficult to know all resources needed when system is highly dynamic All resources must be released at once - hence must hold on to resources till the task finishes Because resources are held for a long time and cannot be assigned to other tasks this makes for inefficient use of resources
First Technology Transfer - Feb 2013
176
Deadlock Prevention - ctd. ●
●
Elimination of no-preemption ●
Task must release already acquired resources if a new request is denied
●
Task must then start requesting required resource all over again
Drawbacks ●
●
When re-starting resource acquisition task must start from the beginning or from some well-defined checkpoint
●
Partially complete work is nullified
●
Task might never complete
Eliminating circular wait deadlock conditions ●
●
Requires an ordering of resources - so that if task holds resource number i then the next resource it attempts to acquire must be for resource number j where j > i Resources are organised in a hierarchical structure and a task is permitted to acquire additional resources while holding other resources, –
Providing these additional resources are higher in the hierarchy than any of the resources currently held
First Technology Transfer - Feb 2013
177
Priority Inversion ●
●
Priority inversion is a situation that occurs where a lower priority task is executing and blocking a higher priority task from executing because the lower priority task is holding a resource required by the higher priority task. Priority inversion can become even more problematic when the low priority task holding the "needed resource" is itself pre-empted by another higher priority task ( e.g. an intermediate level priority task ). ●
●
When will the lower priority task finish with the "needed" resource so that the higher priority task can acquire it and run ?
One approach to the problem of priority inversion is to raise (temporarily) the priority of the task holding the "needed resource" to the same priority level as the higher priority task that is blocked. ●
This is the basis of the Priority Inheritance Protocol whose rules governing the situation where a task T is requesting a resource R are as follows –
R1. If R is in use T is blocked
–
R2. If R is free then R is allocated to T
–
R3. When a task of higher priority than T requests the resource R then the execution priority of T is raised to that of the requesting task
–
R4. Task T returns to its previous priority when it releases R
First Technology Transfer - Feb 2013
178
The Ceiling Priority Protocol ●
●
●
The Priority Inheritance Protocol does not eliminate deadlock. ● The Ceiling Priority Protocol represents an attempt to improve on the Priority Inheritance Protocol. Here ● The priority of every task is known ● The resources required by every task are known ● For any given resource the priority ceiling of that resource is the highest priority of all possible tasks that might need to acquire that resource The rules governing the situation where a task T is requesting a resource R are as follows ● R1. If R is in use T is blocked ● R2. If R is free then R is allocated to T. – T's execution priority is raised to the priority ceiling of R if that is higher. – At any given time T's execution priority is equal to the highest priority ceiling of all the resources it holds ● R3. T's priority is assigned the next-highest priority ceiling of the remaining resources when T releases a resource with the highest priority ceiling ● R4. Task T returns to its previous priority when it has releases all resources
First Technology Transfer - Feb 2013
179
The Priority Ceiling Protocol ●
Here ● ●
●
●
● ●
●
●
The priority of every task is known The resources required by every task are known before that task starts executing The current priority ceiling for a running system is the highest priority ceiling of all resources in use at that time
The rules governing the situation where a task T is requesting a resource R are as follows R1. If R is in use T is blocked R2. If R is free and the priority of T is higher than the current priority ceiling then R is allocated to T R3. If the current priority ceiling belongs to one of the resources that T currently holds, then R is allocated to T, otherwise T is blocked R4. The task that blocks T inherits T's priority if it is higher and ●
●
Executes at this priority until it releases every resource whose priority ceiling is higher than or equal to T's priority . The task then returns to its previous priority
First Technology Transfer - Feb 2013
180
The Priority Ceiling Protocol - ctd. ●
In the Priority Ceiling Protocol the possible causes for a task being blocked are therefore ●
●
●
●
Direct resource contention - the requested resource is currently in use Priority inheritance blocking - the blocking task has inherited a higher priority and its current execution priority is higher than that of the requesting task Priority ceiling blocking - a task cannot block itself if it holds a resource that has defined the current priority ceiling
The Priority Ceiling Protocol imposes an ordering on the acquisition of resources. ●
A circular wait condition is not possible because –
●
None of the tasks holding resources can inherit a higher priority than T, and then preempt T and request a resource that T holds.
The key characteristics of the priority ceiling protocol are therefore ●
A requesting task can be blocked by only one task - the blocking interval is thus limited to at most the duration of a single critical section
●
Transitive blocking is not possible
●
Deadlock can never occur
First Technology Transfer - Feb 2013
181
Foundations of ARM CortexM Embedded C Programming
First Technology Transfer - Feb 2013
182
Overview ●
●
Prerequisites ●
Familiarity with the C Programming Language
●
Some experience of working with embedded systems
The course will cover ●
Architecture and Instruction Set of the ARM Cortex M3 –
●
The ARM Cortex M4 features can be considered as a superset of those of the ARM Cortex M3
Writing C code to initialise and use common on chip peripherals and devices –
GPIO
–
Timers
–
UART
–
A/D
●
Implementing interrupt handles
●
Standard techniques for testing and debugging code
First Technology Transfer - Feb 2013
183
Chapter 1 Overview of the ARM Cortex M3 History and Architecture
First Technology Transfer - Feb 2013
184
ARM – Cortex M3 ●
ARM does not make processors it designs processor architectures and licences the Intellectual Property (IP) to chip manufacturers such as ST, Atmel, TI, NXP ●
●
●
The ARM family of processors spans the range from cheap and power efficient devices to high performance devices, including multi-core devices The target market of the Cortex-M3 core is the field, traditionally, served by 8- or 16bit controllers, where ●
●
●
The chip manufacturers then design and implement real processors by adding e.g. various peripherals such as I2C, SPI, UART, CAN …
Cheap yet powerful controllers with low power consumption are required
On ARM7 systems manufacturers had to add the basic peripherals such as, the interrupt controller, systick timer and support for sleep modes. This Cortex-M3 core design comes with a standardized microcontroller core which ●
Provides a core design with the interrupt system, 24-bit SysTick timer,debug system and memory map included.
First Technology Transfer - Feb 2013
185
Cortex M3 - Architecture ●
The Cortex family has three main profiles ●
The A profile for high end applications –
● ●
●
Processors targeted at complex OS and user applications
The R profile, targeted at real time applications The M profile, targeted at cost-sensitive embedded system microcontroller applications
The Cortex-M3 is a Harvard architecture ●
It has separate Code and Data buses
●
The architecture inside the Cortex-M3 is a ARMv7- M RISC processor
●
The M core is relatively small, having only 33’000 logic cells
●
This reduces the size of the chip and means that cheaper manufacturing processes using 0.35μm technology can be used
First Technology Transfer - Feb 2013
186
Cortex M3 - Interrupts ●
●
The Cortex-M3 core provides a Nested Vector Interrupt Controller (NVIC). The NVIC provides a standardised interrupt structure with dedicated interrupt vectors for up to 240 peripheral sources ●
●
The NVIC provides fast interrupt handling. ●
●
●
Each interrupt source can be individually prioritized
The time taken from receiving an interrupt to starting the first line of code in the interrupt service routine is twelve cycles.
All interrupts can be prioritized on three levels: pre-empting, sub-priority and hardware-priority. ●
The first two levels can be configured via the software
●
The number of bits for priority level setting is 4
The design of the Cortex is such that interrupt service routines (ISR) can be written fully in C ●
Interrupt routines do not require assembler code
First Technology Transfer - Feb 2013
187
Cortex Instruction Set ●
ARM7 and ARM9 CPUs have two instruction sets ●
32-bit ARM and
●
16-bit Thumb
●
●
The Cortex family is designed to support only the 16-bit Thumb-2 instruction set. ●
●
When running code containing mixed instructions it is necessary to explicitly switch from one instruction set to another as necessary
Simplifies assembler coding as there is no longer a need for switching between two instruction sets.
The Thumb-2 instruction was designed to be feature rich, and C/C++ compiler friendly
First Technology Transfer - Feb 2013
188
Cortex CPU ●
The Cortex core is a 32-bit RISC CPU core ●
●
Has a richer instruction set when compared with earlier ARM7/9 processors architectures –
Good integer maths support
–
Improved bit manipulation
–
’harder’ real-time performance
Pipeline ●
The Cortex CPU can execute most instructions in a single cycle, similarly to ARM7 and ARM9 CPUs by –
Using a three stage pipeline. ●
●
Multiplication ●
●
Whilst one instruction is being executed, the next isbeing decoded and a third is being fetched from memory.
A 32-bit multiplication can be executed in one cycle as the Cortex-M3 has a hardware multiplication unit.
The Cortex-M3 architecture is such that there is no longer a need to implement startup code in assembler – it can, now, be done in C.
First Technology Transfer - Feb 2013
189
Cortex M3 – Debug Interface ●
The Cortex-M3 has a Debug Access Port (DAP) which can be used in ●
JTAG mode, or
●
In SerialWire Debug Mode –
Requires only two lines
Clock and data. ARM7 processors had only two Hardware-Breakpoints. ●
●
●
The Cortex-M3 has ●
8 breakpoints, and, additionally
●
Fault Conditions and Exceptions can be set. –
●
Faults and Exceptions can be supervised without the having to set breakpoints.
The debug interface works in Sleep-mode.
First Technology Transfer - Feb 2013
190
Example Cortex M3 Processor ●
The LPC1768 from NXP is a fairly typical Cortex M3 processor ●
Its CPU clock can run at up to 100 MHz
●
512kB on-chip Flash ROM with enhanced Flash Memory Accelerator,
●
In-System Programming (ISP) and In-Application Programming (IAP),
●
64kB RAM
●
Eight channel General purpose DMA controller,
●
Ethernet 10/100 MAC with dedicated DMA,
●
USB 2.0 full-speed Device controller and Host/OTG controller with DMA,
●
CAN 2.0B with two channels
●
Four UARTs, one with full Modem interface,
●
Three I2C serial interfaces
●
Three SPI/SSP serial interfaces
●
One I2S interface,
First Technology Transfer - Feb 2013
191
Example Cortex M3 Processor – ctd. ● ● ● ● ● ● ● ● ● ● ● ● ● ● ●
General purpose I/O pins 12-bit ADC with 8 channels 10-bit DAC, Four 32-bit Timers with capture/compare, Standard PWM Timer block, Motor control PWM for three-phase Motor control Quadrature Encoder, Watchdog Timer Real Time Clock with optional Battery backup, System Tick Timer Repetitive Interrupt Timer Brown-out detect circuit, Power-On Reset, Power Management Unit, Wakeup Interrupt Controller, Crystal oscillator, 4MHz internal RC oscillator, PLL, JTAG and Serial Wire Debug/Trace Port with ETM
First Technology Transfer - Feb 2013
192
Cortex M3 - Schematic Cortex-M3
Instruction Bus
Register Bank ALU
Trace Interface
Decoder
Instruction Fetch Unit
Interrupts
Interrupt Controller (NVIC)
Processor Core System
Memory Protection Unit
Data bus
Debug Interface
Memory system And peripherals
Private Peripherals
Debug
}
First Technology Transfer - Feb 2013
Trace
Memory Interface
Bus Interconnect
Code Memory
Debug System
Optional
193
Cortex-M3 Registers ●
The Cortex-M3, like most RISC processors has a set of general purpose registers ●
●
As well as three registers R13 - R15 that are used for ●
●
R0 - R12
Stack Pointer R13 - actually there are two of these and they are banked (only one is visible at a time) –
R13 (MSP) - Main Stack Pointer
–
R13 (PSP) - Process Stack Pointer
●
Link Register (LR) - R14
●
Program Counter (PC) - R15
Special registers ●
Program registers
●
Interrupt mask registers
●
Control register
First Technology Transfer - Feb 2013
194
Special Registers ●
Program status registers - xPSX - have bits (flags) providing status information) ●
●
Arithmetic and logic processing flags –
zero flag
–
carry flag
●
Execution status
●
Current executing interrupt number
Interrupt mask registers ●
●
●
●
PRIMASK - Used to disable all interrupts except the nomaskable interrupt (NMI) and hard fault FAULTMASK - Used to disable all interrupts except the NMI BASEPRI - Used to disable all interrupts having a priority level equal to or less than some specified priority level CONTROL - Used to define privilege status and stack pointer selection
First Technology Transfer - Feb 2013
195
Cortex M3 - Registers
First Technology Transfer - Feb 2013
196
Operation Modes ●
Support for code safety is provided by controlling the processor state
●
A Cortex-M3 processor has ●
●
Two modes –
Thread mode - running a normal processing thread
–
Handler mode - running an exception handler ●
Interrupt handler
●
System exception handler
Two privilege levels –
Privileged level ●
●
–
Processor has access to all memory ranges (unless barred by Memory Protection Unit (MPU) settings - if the processor has an MPU Has access to all instructions supported by the processor
User program ● ●
Cannot change to a privileged state by writing to the control register To change privilege level it has to trigger an exception and the exception handler changes privilege level
First Technology Transfer - Feb 2013
197
Operation Modes - ctd. ● ●
●
Exception handlers can only run in privileged mode When a processor leaves the reset state it is running a regular program (thread mode) and is in the privileged state An exception handler can change the privilege level of a user thread ●
●
●
●
Normally when an exception handler has completed its work the processor will return to its previous state When an exception handler changes the state e.g. from privileged to user, by writing to the Control register then the processor when it returns, returns to the newly configured state
A privileged user process can change its state to user by writing to the control register The ability to switch between privileged and user levels is useful when implementing operating systems ●
It provides a means of protecting the operating system from “rogue” applications
First Technology Transfer - Feb 2013
198
Operation Mode FSM
First Technology Transfer - Feb 2013
199
Nested Vectored Interrupt Controller (NVIC) ●
The Nested Vectored Interrupt Controller (NVIC) allows nesting of interrupts with higher priorities. i.e. ●
If a higher priority interrupt occurs whilst a lower priority interrupt handler is executing then, –
●
●
The lower priority interrupt is preempted and the higher priority interrupt executes next.
Certain priorities such as the reset exception which is the highest (lowest number) priority, are fixed Other exception handler priorities are programmable and can be changed dynamically, i.e at run time, ●
To be able to change priorities dynamically requires that the interrupt vector is relocated from flash to ram and that it is then patched with the new handler.
First Technology Transfer - Feb 2013
200
Nested Vectored Interrupt Controller (NVIC) ●
The NVIC takes care of stacking (pushing registers onto the stack) and un-stacking (popping registers from the stack) at the hardware level, hence ●
●
Vectored means that when an interrupt is asserted its number is known to the processor and is used to index into the interrupt vector to obtain the handler address directly ●
●
An exception handler can be implemented as a regular C function
Contrast this with a shared handler which requires code for enumerating devices to discover which device interrupted the processor.
When devising prioritisation strategies careful analysis and design is required, a simpler solution is usually preferable to a complex one ●
Working with just three priority levels e.g. low, medium and high is not quite as straightforward as one might think
First Technology Transfer - Feb 2013
201
NVIC - Tail Chaining ●
When two interrupts arrive at the same time, or a lower priority interrupt occurs while executing a same or higher priority interrupt i.e. non pre-empting, ● ●
●
The higher priority interrupt executes first while the other remains pending and As soon as the higher priority interrupt finishes executing the pending interrupt is executed immediately, i.e. tail-chained to the first one, without un-stacking and then stacking the registers again which is not necessary because –
The contents of the stack have not changed
–
This speeds up the execution of subsequent interrupt handlers.
Note : ●
Due to the Harvard architecture, the stacking of the registers can take place simultaneously with the fetches of the interrupt vector and isr code.
First Technology Transfer - Feb 2013
202
NVIC - Late Arrival ●
●
●
When a high priority interrupt is asserted while the processor is entering a lower priority interrupt, and the assert occurs between stacking of registers and before the execution of the first handler instruction, ●
The higher priority interrupt vector is fetched
●
Stacking is allowed to finish, and as soon as it has done so, –
The processor executes the higher priority interrupt immediately, then, when it finishes executing it,
–
The lower priority interrupt is tail-chained to the higher priority interrupt and allowed to execute.
Similarly, if the interrupt occurs while un-stacking is taking place, the un-stacking is abandoned and and the lower priority interrupt is tail-chained to the higher priority interrupt and allowed to execute. These scenarios are illustrated on the next slide
First Technology Transfer - Feb 2013
203
NVIC - Late Arrival and Pre-emption - Illustrated Late Arrival
Pre-emption
First Technology Transfer - Feb 2013
204
Bit Banding ●
A Bit-Band region makes it possible to perform atomic bit manipulation through another memory region called the alias region ●
Each bit in the Bit-Band region can be addressed through a 32-bit aligned address in the alias region, i.e. –
●
Each word in the Bit-Band is mapped to 32 addresses in the alias region.
Bit-Banding can speed up a read-modify-write operation e.g. ●
●
●
In order to set a bit bit in some device register, for instance, to enable interrupts, the normal procedure would be to perform a read-modify-write operation i.e. –
Read a word, then
–
Mask it and then
Where there are concurrency issues it would also be necessary to ensure that the operation would be atomic A code snippet showing the kind of code that would be written is shown on the next slide
First Technology Transfer - Feb 2013
205
Bit Band Mapping
First Technology Transfer - Feb 2013
206
Bit Banding - ctd. #define DEVICE_BASE_ADDR #define ENABLE_INT_MASK
((uint32_t*)0x2007C000) (0x01)
void enable_int() { // tag for exclusive access // wait while it's locked while (__LDREXW(DEVICE_BASE_ADDR)); uint32_t i = *DEVICE_BASE_ADDR; i |= ENABLE_INT_MASK; __STREXW(i, DEVICE_BASE_ADDR);
//exclusive write
} ●
Note: LDREX and STREX are the newer ARM ●
These instructions do not lock the bus
First Technology Transfer - Feb 2013
207
Bit Banding - ctd. ●
Using bit-banding the code snippet shown previously could be re-written as follows
#define DEVICE_BASE_ALIAS
((uint32_t*)0x22000000)
void enable_int() { *DEVICE_BASE_ALIAS = 1; }
First Technology Transfer - Feb 2013
208
Cortex M3 - Memory Map ●
The ARM Cortez-M3 memory map is predefined ●
●
●
Built-in peripherals can be accessed via standard memory access instructions
32 bit addressing spans a 4 GB memory space ●
In practice Cortex-M3 systems will have considerably less than 4 GB of memory
●
The 4GB memory space is divided into specified address ranges –
CODE
–
SRAM
–
Peripherals
–
External RAM
–
External device
–
System level
The Cortex-M3 internal bus infrastructure is optimised for memory usage
First Technology Transfer - Feb 2013
209
Cortex M3 - Memory Map
First Technology Transfer - Feb 2013
210
Cortex M3 - Memory Map - Interfaces
First Technology Transfer - Feb 2013
211
Memory Processor Regions - Permissions WR - Write Through WBWA - Write Back Write Allocate XN - Execute Never Note: Private Peripheral Bus and System space at 0xE0000000 - 0xFFFFFFFF are permanently XN. The MPU cannot change this.
First Technology Transfer - Feb 2013
212
Bus Interface ● ● ●
The Cortex-M3 has several bus interfaces Bus interfaces support instruction fetches and data access concurrently Main bus interfaces ● Code memory buses – I-Code bus - for instruction fetches and vector fetches from code memory space – D-code bus - for data and debug access to code memory space ● Data access has higher priority than debug access ● System bus – Instruction and vector fetches as well as data and debug access to System memory space – Arbitration order is ● Data accesses ● Instruction and vector fetches ● Debug ● Private peripheral bus – Data and debug access to External Peripheral memory space
First Technology Transfer - Feb 2013
213
Chapter 2 Programming the Cortex-M3 in C
First Technology Transfer - Feb 2013
214
Typical Application Build ●
A project, typically, involves compiling several modules ( mostly C modules, but, maybe, also some assembler modules) ●
●
●
●
●
This produces a collection of object (.o) files
The various object files are linked, together with library (archive) files to produce an executable image file in an appropriate format e.g. .axf or .elf ●
AXF - ARM Executable Format - a specialised form of ELF
●
ELF - Executable and Linking Format
Where the memory map is complex the linking process can be controlled via a scatter-loading script When programming the Cortex-M3 in C it is common to find that Cortex-M3 microcontroller manufacturers supply device driver libraries, implemented in C, for controlling the various peripherals ARM has developed a Cortex Microcontroller Software Interface Standard (CMSIS)
First Technology Transfer - Feb 2013
215
Cortex-M3 IDEs ●
The more widely used commercial ARM Cortex-M3 development tools are those from ●
Keil - now part of ARM
●
IAR
●
Code-Red - from NXP, and targetted at NXP Cortex-M3 designs –
●
●
This IDE is Eclipse based
Most of these IDEs come with a various example programs demonstrating use of various peripherals Developers of Cortex-M3 based development and evaluation boards also provide examples targetted at their particular boards ● ●
●
Stellaris - now part of TI Embedded Artists - a Swedish company that makes a variety of ARM based teaching and prototyping boards - they work quite closely with NXP Olimex - a Bulgarian company that makes a wide variety of inexpensive embedded systems boards - including ARM Cortex-M3 boards
First Technology Transfer - Feb 2013
216
A Simple Cortex-M3 C Program ●
●
●
●
This example is taken from the NXP - Code Red example set for the NXP LPC1768 processor (which is closely related to the NXP LPC1769 processor) It consists of two files ●
cr_startup_lpc176x.c
●
IOtest.c
It is a variation on the classical embedded system “hello world” example that involves flashing one or more LEDs Variations on this example are ●
Flashing LEDs using a “timed while loop”
●
Flashing LEDs by polling for a Timer Overflow event flag
●
Flashing LEDs by using a Timer Interrupt
First Technology Transfer - Feb 2013
217
cr_startup_lpc176x.c ●
This kind of file is typical of embedded Cortex-M3 applications
●
Typically it ●
Sets up the Vector table –
In simple programs most of the exception handlers will be implemented as dummy functions
●
Calls SystemInit() if CMSIS is being used
●
Specifies the entry point
●
–
calls __main if the RedLib library is used
–
calls main otherwise
Finally ends with a “fail safe” infinite while loop
while (1) { ; }
First Technology Transfer - Feb 2013
218
IOtest.c ●
Manipulating GPIO involves ●
●
●
Setting the IO Port pins to be either inputs or outpus Writing 1 or 0 to the Port pins to which the LEDs are attached to turn them on or off
Blinking involves - repeatedly ●
Turning an LED on
●
Waiting for a while
●
Turning an LED off
●
Waiting for a while
First Technology Transfer - Feb 2013
219
IOtest.c - Code #include "lpc17xx.h" #include "type.h" int main (void) { uint32_t i, j; /* SystemClockUpdate() updates the SystemFrequency variable */ SystemClockUpdate(); LPC_GPIO2->FIODIR = 0x000000FF; LPC_GPIO2->FIOCLR = 0x000000FF;
}
/* P2.xx defined as Outputs */ /* turn off all the LEDs */
while(1) { for(i = 0; i < 8; i++) { LPC_GPIO2->FIOSET = 1 0; j--); } LPC_GPIO2->FIOCLR = 0x000000FF; for(j = 1000000; j > 0; j--); }
First Technology Transfer - Feb 2013
220
Accessing Memory-Mapped Registers in C ●
Strategies for accessing memory mapped registers in C code include ● Defining a macro to convert address values into C pointers – There are various ways of doing this SYSTICK Timer Registers
SYSTICK Timer Registers
First Technology Transfer - Feb 2013
221
Accessing Memory-Mapped Registers in C - ctd. ●
Defining the registers as a data structure and then defining a pointer to that structure SYSTICK Timer Registers
First Technology Transfer - Feb 2013
222
Accessing Memory-Mapped Registers in C - ctd. ●
Using a data structure, and defining the base address of the peripheral using a scatter linker script during the linking stage SYSTICK Timer Registers
First Technology Transfer - Feb 2013
223
CMSIS - Background ●
The motivation underlying CMSIS was to improve ●
Software usability
●
Software inter-operability –
●
●
Of ARM Microcontroller software
The key idea was to ●
Provide a standardised software interface for Cortex-M3 processor features
●
Provide a standardised set of common system and I/O functions
The goals were ●
●
To improve software portability and reusability To make it easier for software solution suppliers to develop products that could work seamlessly with device driver libraries from different silicon vendors
●
Speed up the software development process
●
Make it possible to use embedded software on multiple compiler toolchains
●
Minimise device driver compatibility issues when sourcing software from multiple providers
First Technology Transfer - Feb 2013
224
CMSIS - Areas of Standardisation ●
Hardware Abstraction Layer (HAL) for Cortex-M3 processor registers ●
Standardised definitions for NVIC, System Control Block registers, SYSTICK register, MPU registers ...
●
Standardised system exception names
●
Standardised header file organisation method
●
Common system initialisation method ●
●
Standardised intrinsic functions ●
●
●
Via a vendor provided SystemInit() function An intrinsic function is one that produces instructions (assembler code) that cannot be generated ib IEC/ISO C
Common communication access functions for UART, Ethernet, SPI ... Standardised method for embedded software to determine the system clock frequency ●
The device driver code defines and initialises a variable called SystemFrequency –
An embedded OS cn set u the SYSTICK unit based on the system clock frequency
First Technology Transfer - Feb 2013
225
CMSIS Organisation ●
The structure of CMSIS follows a layered approach
●
Core Peripheral Access Layer
●
●
●
●
Name and address definitions
●
Helper functions for accessing core registers and core peripherals
Middleware Access Layer ●
Common peripheral access methods
●
Standardised communication interfaces for UART, Ethernet, SPI ...
MCU specific Device Peripheral Access Layer ●
Name and address definitions
●
Driver code for accessing peripherals
MCU specific access functions for peripherals ●
Optional additional helper functions for peripherals
First Technology Transfer - Feb 2013
226
CMSIS Structure
First Technology Transfer - Feb 2013
227
CMSIS Use ●
CMSIS is incoporated into the device driver library ●
CMSIS can be used in projects without any special setup requirement
●
For any given MCU device the device vendor provides a header file, which –
Includes any additional header files needed by the device driver library
Including the Core Peripheral Access Layer The various files are ●
●
●
●
●
●
core_cm3.h - which contains –
Peripheral register definitions and access functions for the Cortex-M3 processor peripherals
–
CMSIS intrinsic function prototypes
core_cm3.c - contains implementations of the intrinsic functions system_.h - contains microcontroller specific interrupt number and peripheral register definitions system_.c - contains the SystemInit function for the particular processor
First Technology Transfer - Feb 2013
228
CMSIS Use - ctd.
First Technology Transfer - Feb 2013
229
Programming with FreeRTOS
First Technology Transfer - Feb 2013
230
Overview ●
●
●
This module follows very much the printed and online documentation for FreeRTOS, including the various books and the FreeRTOS manual written by Richard Barry. It is assumed that you have copies of these to hand Rather than going through all the details to be found in the above resources the approach taken here will be to ●
Explore the FreeRTOS API
●
Overview FreeRTOS configuration and setup issues
●
●
Implement and adapt the various code snippets to be found in the FreeRTOS manual, documentation and books Examine, build and run some of the demonstrator programs on a variety of ARM Cortex M4 boards using two IDEs –
IAR's IAR Embedded Workbench IDE - Proprietary compiler with a Microsoft Visual Studio based front end.
–
Atmel's Atmel Visual Studio 6 - compiler based on the GNU GCC compiler
First Technology Transfer - Feb 2013
231
Strategy ●
The strategy followed will be an object oriented strategy ... ●
FreeRTOS resources can be considered as –
Instances of some resource class which, in effect
Defines attributes ● Defines methods for manipulating and using resource instances The key FreeRTOS resources are ●
●
●
●
●
Tasks - Task API
●
Queues - Queue API
●
Semaphores - Semaphore API
In addition it will be necessary to cover ●
Memory management
●
Interrupt management
●
Working with ARM MPU (Memory Protection Unit)
You can download the FreeRTOS Cortex M3 examples from ●
http://www.freertos.org/Documentation/code/source-code-for-Cortex-M3-editionexamples-using-IAR-and-stellaris.zip
First Technology Transfer - Feb 2013
232
Background ●
●
Richard Barry's books and the online FreeRTOS documentation provide very detailed descriptions of the various APIs ... ● Rather than repeating all the details here you should go and access them from the appropriate sources You are also encouraged to explore the FreeRTOS source code as some of the details can only be found out from inspecting the sources ● A cross-referencing web site (built using OpenGrok) can be found at http://code.metager.de/source/xref/freertos/ ● Tools for searching and cross-referencing code : – The Silver Searcher - http://code.metager.de/source/xref/freertos/ – OpenGrok - http://www.opensolaris.org/os/project/opengrok/ – openhrok - extends the OpenGrok project to index snippets, which are files that are referenced by other files. Search results take into account the number of times a Snippet has been referenced svn checkout http://openhrok.googlecode.com/svn/trunk/ openhrok-read-only
CScope - http://cscope.sourceforge.net/ You are also encouraged to try out different compilers and compare the results e.g. ● IAR ● GCC based - e.g. Atmel Studio 6 ● Keil –
●
First Technology Transfer - Feb 2013
233
FreeRTOS - Code Overview ●
The two-volume series “The Architecture of Open Source Applications” contains a really good overview of the architecture of FreeRTOS from the code - organisation, data structure, API and algorithms point of view ●
●
The next few slides are (shamelessly) based on the contents of that article ●
●
Download the original for your own use
Check that you can reach http://code.metager.de/source/xref/freertos/ ●
●
http://www.aosabook.org/en/freertos.html
If you have time then install OpenGrok / openhrok ...
As we go through the next few slides the intention is that you should explore the source code ●
You should also dip “into and out of” the source code during the course also –
Making notes, as well as updating these slides (where necessary)
First Technology Transfer - Feb 2013
234
FreeRTOS The Task API
First Technology Transfer - Feb 2013
235
Task API ●
This is probably the largest and most complex API in FreeRTOS
●
It contains ●
API functions for creating and managing tasks
●
API functions for interacting with and managing the scheduler
●
Most of the underlying source code is platform independent C
●
Platform dependent details involve things such as ●
Configuring the timer systems - that drive the scheduler
●
Configuring the memory subsystem –
MPU (Memory Protection Unit) - if present on the target processor, and, if being used
●
Details of how tasks interact with interrupts
●
Details of how the ARM Cortex port of FreeRTOS uses CMSIS
●
Writing proprietary device drivers - where necessary
First Technology Transfer - Feb 2013
236
FreeRTOS - Architecture ●
●
Small application - minimum FreeRTOS core ●
Three source (.c) files and a small set of header files
●
Around 9000 lines of code
●
Typical binary code image is under 10KB.
Three main areas: tasks, communication, and hardware interfacing. ●
●
–
A task is a user-defined C function with a given priority.
–
tasks.c and task.h do all the heavy lifting for creating, scheduling, and maintaining tasks.
Communication: Accounts for about 40% of the core code ●
●
●
Tasks: Accounts for around 50% of the core source code
Deals with communication. queue.c and queue.h handle FreeRTOS communication. Tasks and interrupts use queues to send data to each other and to signal the use of critical resources using semaphores and mutexes.
The core code is mostly hardware-independent ●
About 6% of the FreeRTOS core code acts a shim between the hardwareindependent FreeRTOS core and the hardware-dependent code.
First Technology Transfer - Feb 2013
237
Hardware Dependent Code ●
FreeRTOS is highly configurable by design ●
●
●
Can be built as a single CPU, bare-bones RTOS, supporting only a few tasks, or Can be built as a richly functional multicore system with TCP/IP, a file system, and USB.
Configuration options are selected in FreeRTOSConfig.h by setting various #defines. ●
Clock speed, heap size, mutexes, and API subsets are all configurable in this file e.g.
#defne #defne #defne #defne #defne
confgMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 5 ) confgCPU_CLOCK_HZ ( 12000000UL ) confgTICK_RATE_HZ ( ( portTickType ) 1000 ) confgMINIMAL_STACK_SIZE ( ( unsigned short ) 100 ) confgTOTAL_HEAP_SIZE ( ( size_t ) ( 4 * 1024 ) )
First Technology Transfer - Feb 2013
238
Hardware Dependent Code - ctd. ●
●
Hardware-dependent code lives in separate files for each compiler toolchain and CPU architecture e.g. For the IAR compiler on an ARM Cortex-M3 chip ●
● ●
●
●
The hardware-dependent code lives in the FreeRTOS/Source/portable/IAR/ARM_CM3/ directory. portmacro.h - declares all of the hardware-specific functions port.c and portasm.s - contain all of the actual hardwaredependent code. The hardware-independent header file portable.h #include's the correct portmacro.h file at compile time. FreeRTOS calls the hardware-specific functions using #define'd functions declared in portmacro.h
First Technology Transfer - Feb 2013
239
Hardware Dependent Function - Example ●
Mini excercise : ● Check to see whether the following is really true .. ● The hardware-independent file tasks.c frequently needs to enter a critical section of code to prevent preemption. – The details of entering a critical section are architecture specific – tasks.c hides the architecture specific details by calling the global macro portENTER_CRITICAL() ● ●
How is this macro implemented ? Note: If e.g. building FreeRTOS using the IAR compiler targetting an ARM Cortex-M3 chip - the macro is defined in FreeRTOS/Source/portable/IAR/ARM_CM3/portmacro.h which defines portENTER_CRITICAL() as follows : #defne portENTER_CRITICAL()
●
●
●
vPortEnterCritical()
vPortEnterCritical() is actually defined in FreeRTOS/Source/portable/IAR/ARM_CM3/port.c The port.c file is hardware-dependent, and contains code that understands the IAR compiler and the Cortex-M3 chip. vPortEnterCritical() enters the critical section using this hardwarespecific implementation and returns to the hardware-independent tasks.c
First Technology Transfer - Feb 2013
240
Hardware Dependent Function - Example - ctd. ●
Mini excercise ctd. ● Refresh your knowledge of the diff command ● Use the diff command to explore the difference between ARM Cortex M3 and ARM Cortex M4 ports of FreeRTOS by examining the differences between the following files – portasm.x
●
●
–
port.c
–
portmacro.h
Note: Microsoft Visual Studio (Team Foundation Server) edition has quite extensive GUI diff capabilities
Note: FreeRTOS implements hardware-dependent functionality with C preprocessor #defne macros ●
●
Used carefully it is possible to implement error free code The major advantage is that expanding code inline reduces function call overheads
First Technology Transfer - Feb 2013
241
Task Scheduling - Task Priority ●
Each FreeRTOS task has a, user assigned, priority value associated with it ●
●
Values can range between 0 (the lowest priority) and the compiletime value of confgMAX_PRIORITIES-1 (the highest priority).
FreeRTOS uses a "ready list" to keep track of all tasks that are currently ready to run. Implemented as an array of task lists like this: static xList pxReadyTasksLists[confgMAX_PRIORITIES]; –
pxReadyTasksLists[0] is a list of all ready priority 0 tasks,
–
pxReadyTasksLists[1] is a list of all ready priority 1 tasks ... up to
–
pxReadyTasksLists[confgMAX_PRIORITIES-1]
First Technology Transfer - Feb 2013
242
Task Scheduling - Lessons from the Trenches ●
Read the following FreeRTOS FAQ Discussion thread : ●
●
●
The context was porting an IAR built Cortex M3 example to so as to compile it using gcc http://www.freertos.org/FreeRTOS_Support_Forum_Archive/April_2012/freert os_Round_Robin_Scheduling_Questions_5228020.html Note the various points made about using taskDelay() and taskYield() –
●
We will come back to these later
Note also the final part of the discussion
I found the source of the problem and it's nothing to do with the OS. ... I'm afraid I had accidentally not placed my global variables in the BSS section. (The globals were getting placed in COMMON and I hadn't added that section into my bss section.) The reason there was a thousands:1 ratio was just that the variables were not getting initialized to 0. Once I fxed this, the ratio is very close to 1:1. Sorry! I keep relearning that I should really investigate thoroughly before asking a question. First Technology Transfer - Feb 2013
243
FreeRTOS - System Tick ●
The heartbeat of a FreeRTOS system is the system tick. ● FreeRTOS configures the system to generate a periodic tick interrupt. ● The tick interrupt frequency is a configurable parameter – Typically in the millisecond range. –
Every time the tick interrupt fires, the vTaskSwitchContext() function is called ●
vTaskSwitchContext() selects the highest-priority ready task and puts it in the pxCurrentTCB variable as follows :
/* Find the highest-priority queue that contains ready tasks. */ while ( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopReadyPriority ] ) ) ) { confgASSERT( uxTopReadyPriority ); --uxTopReadyPriority; } /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the tasks of the same priority get an equal share of the processor time. */ listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); First Technology Transfer - Feb 2013
244
FreeRTOS - System Tick - ctd. ●
Before the while loop starts, uxTopReadyPriority is guaranteed to be greater than or equal to the priority of the highest-priority ready task. ● The while() loop starts at priority level uxTopReadyPriority and walks down through the pxReadyTasksLists[] array to find the highest-priority level with ready tasks. ●
●
At that point pxCurrentTCB points to the highest-priority task, and ●
●
listGET_OWNER_OF_NEXT_ENTRY() then grabs the next ready task from that priority level's ready list. When vTaskSwitchContext() returns the hardware-dependent code starts running that task.
The above code is at the absolute heart of FreeRTOS. ●
The remaining 8900+ lines of FreeRTOS are the “supporting cast”
First Technology Transfer - Feb 2013
245
FreeRTOS - Ready List
First Technology Transfer - Feb 2013
246
Task Control Block ●
The TCB is defined in tasks.c
typedef struct tskTaskControlBlock { volatile portSTACK_TYPE *pxTopOfStack; /* Points location of last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE STRUCT. */
xListItem xGenericListItem; /* List item used to place the TCB in ready and blocked queues. */ xListItem xEventListItem; /* List item used to place the TCB in event lists.*/ unsigned portBASE_TYPE uxPriority; /* The priority task - 0 is the lowest priority. */ portSTACK_TYPE *pxStack; /* Points to the start of the stack. */ signed char pcTaskName[ confgMAX_TASK_NAME_LEN ]; /* Descriptive name of task Facilitates debugging only. */ #if ( portSTACK_GROWTH > 0 ) portSTACK_TYPE *pxEndOfStack; /* Used for stack overfow checking on architectures where the stack grows up from low memory. */
#endif #if ( confgUSE_MUTEXES == 1 ) unsigned portBASE_TYPE uxBasePriority; /* The priority last assigned to the task used by the priority inheritance mechanism. */ #endif } tskTCB;
First Technology Transfer - Feb 2013
247
Task Control BLock - ctd. ●
pxStack - holds the starting address of the stack
●
pxTopOfStack - holds the address of the current stack top
●
pxEndOfStack points to the end of the stack ●
Used to check for stack overflow in those systems where the stack grows "up" to higher addresses. –
●
●
If the stack grows "down" to lower addresses then stack overflow is checked by comparing the current top of stack against the start of stack memory in pxStack
uxPriority stores the priority currently assigned to the task its initial priority is the priority assigned to the task when first created uxBasePriority - used to remember the original priority whilst the task is temporarily elevated to the "inherited" priority in priority inheritance
First Technology Transfer - Feb 2013
248
Task Control BLock - ctd. ●
The xListItem member variables xGenericListItem and xEventListItem are used in the implementation of the various FreeRTOS scheduling lists ●
When a task is inserted into a list –
●
●
FreeRTOS inserts a pointer to either the TCB's xGenericListItem or xEventListItem
A task can be in one of four states: running, ready to run, suspended, or blocked. FreeRTOS does not store the state value in a separate member variable ●
●
FreeRTOS tracks task state implicitly by putting tasks in the appropriate list: ready list, suspended list, blocked list The presence of a task in a particular list indicates the task's state. –
As a task changes from one state to another, FreeRTOS simply moves it from one list to another.
First Technology Transfer - Feb 2013
249
Process State Diagram
First Technology Transfer - Feb 2013
250
Task Creation and Setup ●
A task is created by calling the xTaskCreate() function ●
●
●
A new TCB object is allocated to store the name, priority, and other task attraibutes Memory for the amount of stack requested is allocated and the address of the start of the stack memory is saved in the TCB's pxStack member.
The stack is initialized so that it will appear as if the new task is already running and was interrupted by a context switch. ●
This allows the scheduler to treat the newly created task in exactly the same way as it treats tasks that have been running for a while –
●
The scheduler does not need any special case code for handling new tasks.
The implementation details are architecture dependent e.g. the code for an ARM Cortex-M3 processor implementation is shown on the next few slides
First Technology Transfer - Feb 2013
251
Task Creation and Setup - ctd. unsigned int *pxPortInitialiseStack( unsigned int *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ) { /* Simulate the stack frame as it would be created by a context switch interrupt. */ pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; *pxTopOfStack = ( portSTACK_TYPE ) pxCode; /* PC */ pxTopOfStack--; *pxTopOfStack = 0; /* LR */ pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ *pxTopOfStack = ( portSTACK_TYPE ) pvParameters; /* R0 */ pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
}
return pxTopOfStack;
First Technology Transfer - Feb 2013
252
Task Creation and Setup - ctd. ●
The ARM Cortex-M3 processor pushes registers on to the stack when a task is interrupted. ●
pxPortInitialiseStack() modifies the stack so that look as if the registers were pushed even though the task has not actually started running –
Known values are stored to the stack for the ARM registers xPSR, PC, LR, and R0.
–
The remaining registers R1 -- R12 get stack space allocated for them by decrementing the top of stack pointer, but no specific data is stored in the stack for those registers.
–
The ARM architecture documentation states that those registers are undefined at reset, so a (non-buggy) program will not rely on ant known values
First Technology Transfer - Feb 2013
253
Task Creation and Setup - ctd. ●
After the stack is set up FreeRTOS disables interrupts prior to ●
●
If this is the first task to ever be created ● ●
●
●
Modifying the the ready lists and other scheduler structures FreeRTOS initializes the scheduler's task lists The lists for tracking tasks that have been suspended, killed, and delayed are also initialised at this point
After any first-time initialization has been completed the new task is added to the ready list at its specified priority level Task creation is completed by re-enabling nterrupts
First Technology Transfer - Feb 2013
254
FreeRTOS Lists ●
FreeRTOS lists are quite complex structures :
First Technology Transfer - Feb 2013
255
List Structures ●
The FreeRTOS list is a standard circular doubly linked list with some extra attributes
struct xLIST_ITEM { portTickType xItemValue;
/* The value being listed. In most cases this is used to sort the list in descending order. */ volatile struct xLIST_ITEM * pxNext; /* Pointer to next xListItem in the list. */ volatile struct xLIST_ITEM * pxPrevious; /* Pointer to previous xListItem in the list. */ void * pvOwner; /* Pointer to the object (normally a TCB) that contains the list item. There is therefore a two-way link between the object containing the list item and the list item itself. */ void * pvContainer; /* Pointer to the list in which this list item is placed (if any). */
}; typedef struct xLIST { volatile unsigned portBASE_TYPE uxNumberOfItems; volatile xListItem * pxIndex; /* Used to walk through the list. Points to the last item returned by a call to pvListGetOwnerOfNextEntry (). */ volatile xMiniListItem xListEnd; /* List item that contains the maximum possible item value, meaning it is always at the end of the list and is therefore used as a marker. */ } xList;
First Technology Transfer - Feb 2013
256
List Structures - ctd. ●
The list element xItemValue is the usually the priority of the task being tracked or a timer value for event scheduling. ●
Lists are kept in high-to-low priority order i.e. –
The highest-priority xItemValue (the largest number) is at the front of the list and
–
The lowest priority xItemValue (the smallest number) is at the end of the list.
●
The pxNext and pxPrevious pointers are standard linked list pointers.
●
pvOwner is a pointer to the owner of the list element. ●
Usually a pointer to a task's TCB object.
●
pvOwner makes task switching fast in vTaskSwitchContext() –
Once the highest-priority task's list element is found in pxReadyTasksLists[], that list element's pvOwner pointer leads us directly to the TCB needed to schedule the task.
First Technology Transfer - Feb 2013
257
List Structures - ctd. ●
●
pvContainer points to the list that the item is in The list handle structure, xList, can be used to quickly determine if a list item is in a particular list ●
uxNumberOfItems holds the size of the list
●
All new lists are initialized to contain a single element - the xListEnd element –
xListEnd.xItemValue is a sentinel value which is equal to the largest value for the xItemValue variable: ● ● ●
●
●
0xffff when portTickType is a 16-bit value and 0xffffffff when portTickType is a 32-bit value The insertion algorithm ensures that xListEnd is always the last item in the list.
Since lists are sorted high-to-low, the xListEnd element is used as a marker for the start of the list. Since the list is circular, this xListEnd element is also a marker for the end of the list.
First Technology Transfer - Feb 2013
258
Mini Exercise ●
●
Examine the function listGET_OWNER_OF_NEXT_ENTRY() ●
How does it do its work ?
●
Where is it called from ?
●
How does it do wrap around detection ?
Examine the function vTaskSwitchContext() ●
Which lists does it manipulate and how ?
●
When is it called ?
First Technology Transfer - Feb 2013
259
Basic Multi-Tasking ●
The basic pattern is very simple ●
●
Its the details that are hard
Basic pattern : ●
●
Define the functions that will form the basis of the various tasks Create task instances Possible task instance combinations ● Each task runs a unique function and has a unique priority ● Multiple tasks running the same function and multiple tasks running at the same priority level are possible Start up the task scheduler –
●
First Technology Transfer - Feb 2013
260
Basic Multi-Tasking ●
How does one go about assigning work to different tasks ?
●
How does one go about assigning priorities to tasks ?
●
●
How does one decide how much stack space a given task should have ? Is it a good idea to share memory between tasks ? ●
● ●
Is a “Blackboard Pattern” approach suitable for embedded systems ? What about an “Observer Pattern” ?
Some common multi-tasking scenarios ●
Producer - Consumer
●
Readers and Writers
●
Workcrew pattern
●
Task pools
First Technology Transfer - Feb 2013
261
Basic Multi-Tasking Template ●
For actual details for different processors and a variety of examples study the various ports and example projects that come with the FreeRTOS download, or that are available from the chip vendors / IDE vendors (Keil, IAR, Rowley, ...)
/* #includes and #defnes section */ ... /* The task function function prototypes */ void vTask1( void *pvParameters ); void vTask2( void *pvParameters ); int main( void ) { /* Call appropriate confguration and initialisation functions */ ... /* Create the specifed tasks. */ ... xTaskCreate( vTask1, /* Pointer to the function that implements the task. */ "Task 1", /* Text name for the task. This is to facilitate debugging only. */ 254, /* Stack depth in words. */ NULL, /* NULL iff not using the task parameter. */ 1, /* Task priority level */ NULL ); /* NULL if not using the task handle. */ /* Create the other tasks similarly */ xTaskCreate( vTask2, "Task 2", 254, NULL, 1, NULL ); vTaskStartScheduler(); /* Tasks now executing */ for( ;; ) ; /* Safety net ... */ } First Technology Transfer - Feb 2013
262
General Words of Advice ●
It is a good idea to start a new FreeRTOS project by basing it on one of the provided pre-configured demos. ●
●
This should ensure that –
The new project includes all the necessary source and header files, and
–
That it installs the necessary interrupt service routine
A FreeRTOS application will start up and execute just like a non-RTOS application until vTaskStartScheduler() is called. ●
●
●
vTaskStartScheduler() is normally called from the application's main() function. The RTOS only controls the execution sequencing after vTaskStartScheduler() has been called. Get the code for the various tasks executing correctly (correct start up code, correct linker configuration, etc.) on the chosen target before attempting to use any RTOS functionality.
First Technology Transfer - Feb 2013
263
FreeRTOS Source Files ●
●
●
● ●
●
At a minimum, the following source files have to be included in every FreeRTOS project: ●
FreeRTOS/Source/tasks.c
●
FreeRTOS/Source/queue.c
●
FreeRTOS/Source/list.c
●
FreeRTOS/Source/portable/[compiler]/[architecture]/port.c.
●
FreeRTOS/Source/portable/MemMang/heap_x.c where 'x' is 1, 2, 3 or 4.
If the directory that contains the port.c file also contains an assembly language file, then the assembly language file has to be used also. Where software timer functionality is required then include FreeRTOS/Source/timers.c If using co-routine functionality, include FreeRTOS/Source/croutine.c The directories for the following header files must be in the compiler's include path ●
FreeRTOS/Source/include
●
FreeRTOS/Source/portable/[compiler]/[architecture]
Depending on the port, it may also be necessary for the same directories to be in the assembler's include path.
First Technology Transfer - Feb 2013
264
Configuration File ●
Every project must have a FreeRTOSConfg.h ●
●
It is pecific to the application, not the RTOS, and
–
Should be placed in an application directory
Note: An application will not link if confgTOTAL_HEAP_SIZE is set too high.
The definition confgMINIMAL_STACK_SIZE sets the size of the stack used by the idle task. ●
●
–
If heap_1, heap_2, or heap_4 is included in the project, then, in FreeRTOSConfg.h use the definition confgTOTAL_HEAP_SIZE to dimension the FreeRTOS heap ●
●
FreeRTOSConfg.h tailors the RTOS kernel to the application being built.
If confgMINIMAL_STACK_SIZE is set too low, then the idle task will generate stack overflows
Interrupt Vectors : ●
The method used to install the interrupt handlers provided by the RTOS port is dependent on the port and compiler being used –
Use the “official” demo application for the port being used as a starting point
First Technology Transfer - Feb 2013
265
SAM4SXPLAINED - Starter Lab ●
●
●
The SAM4SXPLAINED board is a low cost CortexM4 evaluation board produced by Atmel for evaluating their ARM CortexM4 processor technology Atmel's own IDE Atmel Studio 6 uses a gcc based compiler and a Microsoft Visual Studio based front end. Atmel provides a whole lot of examples via its ASF (Atmel Software Framework) resources ●
●
●
These come bundled with Atmel Studio 6, or can be downloaded separately The separately downloaded examples contain IAR projects as well as GCC projects
It is often a good idea, when starting to new embedded systems to have a number of toolkits and compilers available ●
●
Sometimes, problems arising with one toolchain do not arise with a different toolchain and vice versa At some point, typically, one or other of the toolchains is selected as the basis for the final product
First Technology Transfer - Feb 2013
266
SAM4SXPLAINED - Starter Lab - Part 1 ●
Part 1- will use Atmel Studio 6 as the IDE ●
●
This is a free IDE and the philosophy is –
If it is good enough for Atmel it is probably good enough for me
–
It should be easier to get the Atmel examples running using this IDE as opposed to some other IDE e.g. IAR, Keil, Attolic
The SAM4S explained board contains a JTAG subsystem that uses Segger technology –
●
●
To be able to download code to the target board and also to be able to debug code it is necessary to download and install the appropriate tools and drivers from Segger
On examining the SAM4SXPLAINED board you will observe that it has two USB interfaces ●
A USB interface to the JTAG subsystem
●
A USB interface for USB device applications developed on the target
The exercise involves ●
Compiling and running a simple example that flashes some LEDs –
The frequency of LED flashing changes when different sections of the touch slider are touched
●
Compiling a FreeRTOS example that programs the board to function as a disk driver
●
Implementing a FreeRTOS based LED flashing application
First Technology Transfer - Feb 2013
267
SAM4SXPLAINED - Starter Lab - Part 1 ●
In Atmel Studio 6 create and run two SAM4SXPLAINED ASF projects ●
The first project to create is the getting started project ... this basically utilises two LEDs and the touch slider .. Depending on where you touch the slider the rate of LED blinking will change The second project is the basic FreeRTOS project which involves programming the device so that when connected as a device to the PC it will appear as a disk drive .. –
●
– – –
Test out the device In which kind of memory is the filesystem created ? What happens when the program is reset ?
First Technology Transfer - Feb 2013
268
SAM4SXPLAINED - Starter Lab - Part 1 ●
●
Now you know that you can get LEDs to flash, and a FreeRTOS project to compile. Your challenge is to create a FreeRTOS application as follows ●
●
Implement a function that flashes an LED using a busy wait approach Create two tasks One task flashes one LED – The other task flashes the other LED The parameter passed into the task create call specifies which LED to flash and at what rate. –
●
First Technology Transfer - Feb 2013
269
SAM4SXPLAINED - Starter Lab - Part 2 ●
●
●
The Atmel ASF examples downloaded separately from Atmel Studio 6 contain several IAR examples for the SAM4SXPLAINED board ●
A flashing LED example
●
A FreeRTOS example
The goal is to get both these examples to compile and run, and then To complete the same challenge as posed in Part 1, except, this time using the IAR IDE
First Technology Transfer - Feb 2013
270
Task Management Explored in Greater Detail
First Technology Transfer - Feb 2013
271
Task States and Transitions ●
The basic state diagram is shown below :
First Technology Transfer - Feb 2013
272
Task Priorities ●
Conceptual outline of task priority lists ...
First Technology Transfer - Feb 2013
273
xTaskCreate ●
Tasks are created via xTaskCreate - which has the following function prototype portBASE_TYPE xTaskCreate ( pdTASK_CODE pvTaskCode, const portCHAR * const pcName, unsigned portSHORT usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pvCreatedTask );
●
pvTaskCode - Pointer to the task entry function. ●
●
Tasks must be implemented to never return (i.e. continuous loop).
pcName - Descriptive name for the task. ●
Used mainly to facilitate debugging –
Max length defined by confgMAX_TASK_NAME_LEN
First Technology Transfer - Feb 2013
274
xTaskCreate - ctd. ●
usStackDepth - size of the task stack ● Specified as the number of variables the stack can hold - not the number of bytes e.g. – If the stack is 32 bits wide and usStackDepth is defined as 100, 400 bytes will be allocated for stack storage. ● Stack depth multiplied by stack width must not exceed the maximum value that can be contained in a variable of type size_t
●
pvParameters - Pointer used as the parameter for the task being created.
●
uxPriority - The priority at which the task should run.
●
Systems that include MPU support can optionally create tasks in a privileged (system) mode by setting bit portPRIVILEGE_BIT of the priority parameter e.. ●
●
●
To create a privileged task at priority 2 the uxPriority parameter should be set to ( 2 | portPRIVILEGE_BIT ).
pvCreatedTask - Used to pass back a handle by which the created task can be referenced. xTaskCreate returns ●
pdPASS if the task was successfully created and added to a ready list
●
An error code defined in the file projdefs. h otherwise
First Technology Transfer - Feb 2013
275
xTaskCreate - Template Snippet The basic xTaskCreate usage pattern is shown below :
●
/* Task work function */ void vTaskCode( void * pvParameters ) { for( ;; ) { /* Task work ... */ } } /* Function to create a task .. */ void vOtherFunction( void ) { static unsigned char ucParameterToPass; xTaskHandle xHandle; /* Note that the parameter ucParameterToPass must exist for the lifetime of the task hence declared as static */ xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle ); /* Use the handle to delete the task. */ vTaskDelete( xHandle ); } First Technology Transfer - Feb 2013
276
Idle Task ●
●
The Idle Task is created automatically when the RTOS scheduler is started at the lowest possible priority The idle task is responsible for freeing memory allocated by the RTOS to tasks that have since been deleted ●
Applications that invoke vTaskDelete() should ensure that the idle task is not starved of processing time when it is called upon to free up resources –
●
It is possible for application tasks to share the idle task priority (tskIDLE_PRIORITY) ●
Enabled by setting the confgIDLE_SHOULD_YIELD configuration parameter to 1 –
●
The idle task can legitimately be starved of microcontroller time under all other conditions
In this case the idle task will yield immediately should any other task at the idle priority be ready to run
An alternative to having several tasks running at idle priority is the following: ●
Set configIDLE_SHOULD_YIELD to 0.
●
Use an idle hook in place of separate tasks at the idle priority.
●
Create all application tasks at a priority greater than the idle priority.
First Technology Transfer - Feb 2013
277
Idle Task Hook ●
●
An idle task hook is a function that is called during each cycle of the idle task. It is a mechanism that can be used to add extra functionality to the idle task ●
●
Because there must always be at least one task that is ready to run the hook function must not call any API functions that could cause the idle task to block e.g. –
vTaskDelay(), or a queue or semaphore function with a block time
–
Note: co-routines can block within the hook function.
To create an idle hook: ●
Set confgUSE_IDLE_HOOK to 1 in FreeRTOSConfg.h
●
Define a function that has the following name and prototype: –
●
void vApplicationIdleHook( void );
A common use case of the idle hook function is to put the microcontroller CPU into a power saving mode.
First Technology Transfer - Feb 2013
278
Task Hook ●
Task hook functions are invoked via a call to portBASE_TYPE xTaskCallApplicationTaskHook ( xTaskHandle xTask, void *pvParameter ); ●
●
confgUSE_APPLICATION_TASK_TAG must be defined as 1 for this function to be available A 'tag' value can be assigned to each task. –
Normally the value is for the use of the application only and the RTOS kernel does not access it.
–
However, it is possible to use the tag to assign a hook (or callback) function to a task - the hook function being executed by callin xTaskCallApplicationTaskHook().
–
Each task can define its own callback, or none at all.
First Technology Transfer - Feb 2013
279
Task Hook ●
●
Although it is possible to use the first function parameter to call the hook function of any task, the most common use of a task hook function is with the FreeRTOS trace hook macros Task hook functions must have type pdTASK_HOOK_CODE, i.e. –
Take a void * parameter, and return a value of type portBASE_TYPE
–
The void * parameter can be used to pass any information into the hook function.
–
The parameters have the following meanings : ●
●
xTask - the handle of the task whose hook function is being called. – Passing NULL as xTask will call the hook function associated with the currently executing task. pvParameter - the value to pass to the hook function – It can be a pointer to a structure, or a numeric value.
First Technology Transfer - Feb 2013
280
Task Hook - Template Code Snippet ●
The following code fragment illustrates the task hook usage pattern : /* Defne the callback function e.g. */ static portBASE_TYPE prvExampleTaskHook( void * pvParameter ) { /* Perform some action - e.g. logging a value, updating the task state, outputting some value, etc. */ return 0; } /* Defne the task that sets prvExampleTaskHook as its hook/tag value by registering the Task Hook at the start of this task function e.g. */ void vSomeTask( void *pvParameters ) { /* Register our callback function. */ vTaskSetApplicationTaskTag( NULL, ExampleTaskHook );
}
for( ;; ) { /*Task work ... */ }
First Technology Transfer - Feb 2013
281
Task Management APIs ●
●
●
The Task Management API functions can be grouped into three parts Creation functions
●
Utility functions ●
tskIDLE_PRIORITY
●
xTaskGetTickCount
●
xTaskCreate
●
uxTaskGetNumberOfTasks
●
vTaskDelete
●
vTaskList
●
vTaskGetRunTimeStats
Control functions ●
vTaskDelay
●
vTaskStartTrace
●
vTaskDelayUntil
●
ulTaskEndTrace
●
uxTaskPriorityGet
●
uxTaskGetStackHighWaterMark
●
vTaskPrioritySet
●
vTaskSetApplicationTaskTag
●
vTaskSuspend
●
xTaskGetApplicationTaskTag
●
vTaskResume
●
xTaskCallApplicationTaskHook
●
xTaskResumeFromIS
First Technology Transfer - Feb 2013
282
vTaskDelete() ●
xTaskCreate() and vTaskDelete() have already been covered
●
void vTaskDelete( xTaskHandle xTask ); ●
●
●
●
●
INCLUDE_vTaskDelete must be defined as 1 for the function to be available Removes a task from the RTOS kernels management The task being deleted is removed from all ready, blocked, suspended and event lists. Note: Memory allocated by the task code is not automatically freed, and should be freed before the task is deleted. Note: The demo example death.c demonstrates the –
Dynamic creation of tasks (at run time)
–
Deletion of tasks
–
The passing of parameters to tasks
First Technology Transfer - Feb 2013
283
vTaskDelay() ●
void vTaskDelay( portTickType xTicksToDelay ); ●
INCLUDE_vTaskDelay must be defined as 1 for the function to be available. –
Delays a task for the specified number of ticks. ● The actual time that the task remains blocked depends on the tick rate. The constant portTICK_RATE_MS can be used to calculate the real time delay from the tick rate – To a resolution of one tick period. Specifies a time at which the task wishes to unblock relative to the time at which vTaskDelay() is called Does not provide a good method of controlling the frequency of a cyclical task as ● The execution path taken through the code, and other task and interrupt activity, will effect the frequency at which vTaskDelay() gets called ●
– –
Better to use vTaskDelayUntil() , which specifies an absolute time (rather than a relative time) at which the calling task should unblock. The example flash.c demonstrates ● Delaying, as well as ● Passing parameters and creating tasks ●
●
First Technology Transfer - Feb 2013
284
vTaskDelayUntil() ●
void vTaskDelayUntil( portTickType *pxPreviousWakeTime, portTickType xTimeIncrement ); ●
INCLUDE_vTaskDelayUntil must be defined as 1 for the function to be available.
●
Delays a task until a specified time. –
Can be used by cyclical tasks to ensure a constant execution frequency.
–
Specifies the absolute (exact) time at which it wishes to unblock.
–
Will return immediately (without blocking) if used to specify a wake time that is already in the past. ●
A task using vTaskDelayUntil() to execute periodically must re-calculate its required wake time if the periodic execution is halted for any reason (e.g. if the task is temporarily placed into the Suspended state) causing the task to miss one or more periodic executions. – Detect by checking the variable passed by reference as the pxPreviousWakeTime parameter against the current tick count – Must not be called while the RTOS scheduler has been suspended by a call to vTaskSuspendAll()
First Technology Transfer - Feb 2013
285
vTaskDelayUntil() - ctd. ●
Parameters: ●
pxPreviousWakeTime - pointer to variable holding the time at which the task was last unblocked. – The variable must be initialised with the current time prior to its first use ●
●
Thereafter the variable is automatically updated within vTaskDelayUntil()
xTimeIncrement –
- the cycle time period.
The task will be unblocked at time (*pxPreviousWakeTime + xTimeIncrement).
–
Calling vTaskDelayUntil with the same xTimeIncrement parameter value will result in a task running with a fixed interval period e.g.
// Perform an action every 10 ticks. void vTaskFunction( void * pvParameters ) { portTickType xLastWakeTime; const portTickType xFrequency = 10; // Initialise xLastWakeTime with the current time. xLastWakeTime = xTaskGetTickCount(); for( ;; ) { // Wait for next cycle. vTaskDelayUntil( &xLastWakeTime, xFrequency ); // Do something ... } } First Technology Transfer - Feb 2013
286
Suspending and Resuming Tasks ●
Tasks can be suspended and resumed as necessary
●
void vTaskSuspend( xTaskHandle xTaskToSuspend ); ●
●
Suspends the task – A suspended a task will not get any more processing time Calls to vTaskSuspend are not accumulative - i.e. –
● ●
Calling vTaskSuspend () twice on the same task still only requires one call to vTaskResume () to ready the suspended task.
INCLUDE_vTaskSuspend must be defined as 1 for this function to be available
void vTaskResume( xTaskHandle xTaskToResume ); Resumes the task ● INCLUDE_vTaskSuspend must be defined as 1 for this function to be available The dynamic.c example demonstrates ●
●
●
● ● ●
Passing parameters into a task Dynamically changing priorities Suspending tasks Suspending the RTOS scheduler
First Technology Transfer - Feb 2013
287
Resuming a Task from an ISR ● ●
●
ISR code should be of short duration and must not block There are Use Cases for which resuming a task from an ISR makes sense e.g ● If an emergency button - which triggers an edge level based interrupt on the corresponding I/O pin is pressed then the task that carries out the emergency shutdown procedure should be called immediately – There is no reason to schedule such a task otherwise portBASE_TYPE xTaskResumeFromISR( xTaskHandle xTaskToResume ); ● ● ●
Resumes a suspended task Can be called from within an ISR Returns pdTRUE if resuming the task should result in a context switch, otherwise pdFALSE –
●
●
●
Used by the ISR to determine if a context switch may be required following the ISR.
INCLUDE_vTaskSuspend and INCLUDE_xTaskResumeFromISR must both be defined as 1 for this function to be available.
Note: A task that has been suspended by one of more calls to vTaskSuspend() will be made available for running again by a single call to xTaskResumeFromISR(). Note: xTaskResumeFromISR() should not be used to synchronise a task with an interrupt if there is a possibility that the interrupt could arrive prior to the task being suspended ● Under such circumstances a semaphore should be used as a synchronisation mechanism
First Technology Transfer - Feb 2013
288
Resuming a Task from an ISR - ctd. ●
The following code template snippet illustrates a typical code pattern for resuming a task from an ISR xTaskHandle xHandle; void vAFunction( void ) { // Create a task, storing the handle. xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); // ... Rest of code. } void vTaskCode( void *pvParameters ) { // The task being suspended and resumed. for( ;; ) { // ... Perform some function here. vTaskSuspend( NULL ); // The task suspends itself. // The task is now suspended, so will not continue until the ISR resumes it. } } void vAnExampleISR( void ) { portBASE_TYPE xYieldRequired; xYieldRequired = xTaskResumeFromISR( xHandle ); // Resume the suspended task. if( xYieldRequired == pdTRUE ) { // Switch context so the ISR returns to a different task details depend on the port // NOTE: How this is done depends on the port you are using. Check portYIELD_FROM_ISR(); } }
First Technology Transfer - Feb 2013
289
Collecting Run Time Information ●
●
●
volatile portTickType xTaskGetTickCount( void ); ●
Cannot be called from an ISR
●
Returns - the number of ticks since vTaskStartScheduler was called.
volatile portTickType xTaskGetTickCountFromISR( void ); ●
Can be called from an ISR.
●
Returns - the number of ticks since vTaskStartScheduler was called.
unsigned portBASE_TYPE uxTaskGetNumberOfTasks( void ); ● ●
●
Returns - the number of tasks that the RTOS kernel is currently managing. Ready tasks + blocked tasks + suspended tasks + tasks deleted but not yet freed by the idle task
void vTaskList( portCHAR *pcWriteBuffer ); ●
Lists all the current tasks, along with their current state and stack usage high water mark. –
Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or suspended ('S').
●
confgUSE_TRACE_FACILITY must be defined as 1 for this function to be available
●
Disables interrupts for its duration. –
●
Not intended for normal application runtime use but as a debug aid.
Parameter - pcWriteBuffer - a buffer into which the details will be written, in ascii form –
Should be large enough to contain the output - approx, 40 bytes per task OK
First Technology Transfer - Feb 2013
290
Collecting Run Time Information - ctd. ●
void vTaskGetRunTimeStats( portCHAR *pcWriteBuffer ); ●
●
confgGENERATE_RUN_TIME_STATS must be defined as 1 for this function to be available. The application must provide definitions for –
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ●
–
Configures a peripheral timer/counter
portGET_RUN_TIME_COUNTER_VALUE
Returns the timers current count value respectively. – The counter should be running at at least 10 times the frequency of the tick count. Will disable interrupts for its duration. ●
●
●
●
●
Intended as a debug aid.
Setting confgGENERATE_RUN_TIME_STATS to 1 will result in a total accumulated execution time being stored for each task. The pcWriteBuffer - must be large enough to hold the (ascii text) output ●
Approx 40 bytes per task OK
First Technology Transfer - Feb 2013
291
Tracing Tasks ●
Task tracing used to make use of the functions vTaskStartTrace and turned off with ulTaskEndTrace ●
●
●
Currently task tracing should make use of the Trace Hook Macros which can be used to e.g. –
Set a digital output to indicate which task is executing - allowing e.g. a logic analyzer to be used to view and record the task execution sequence and timing.
–
Set an analogue output to a voltage that represents which task is executing allowing an oscilloscope to be used to view and record the task execution sequence and timing.
–
Log task execution sequences, task timing, RTOS kernel events and API calls for offline analysis.
–
Integrate RTOS kernel events into third party debuggers.
Macros called from within interrupts, especially the tick interrupt, must be fast and use little stack space Macro definitions must occur before the inclusion of FreeRTOS.h.
First Technology Transfer - Feb 2013
292
Task Tags ●
Task tags are simply labels associated with tasks ●
●
The getter and setter methods are, respectively –
xTaskGetApplicationTaskTag
–
vTaskSetApplicationTaskTag
A 'tag' value can be assigned to each task. –
●
A 'tag' is for the use of the application only ●
It is not used by the RTOS
●
It is typically used in various RTOS trace macros
void vTaskSetApplicationTaskTag( xTaskHandle xTask, pdTASK_HOOK_CODE pxTagValue ); ●
confgUSE_APPLICATION_TASK_TAG must be defined as 1 for this function to be available
●
Takes two arguments –
xTask - the handle of the task to which a tag value is being assigned. ●
–
Passing xTask as NULL causes the tag to be assigned to the calling task.
pxTagValue - the value being assigned to the task tag. ●
Of type pdTASK_HOOK_CODE to permit a function pointer to be assigned as the tag, although any value can actually be assigned.
First Technology Transfer - Feb 2013
293
Task Tags ●
pdTASK_HOOK_CODE xTaskGetApplicationTaskTag( xTaskHandle xTask ); ●
●
●
confgUSE_APPLICATION_TASK_TAG must be defined as 1 for this function to be available. Returns the ‘tag’ value associated with a task. – The meaning and use of the tag value is defined by the application writer. Takes one argument - xTask - the handle of the task being queried. –
A task can query its own tag value by using NULL as the parameter value.
void vATask( void *pvParameters ) { vTaskSetApplicationTaskTag( NULL, ( void * ) 1 ); for( ;; ) { /* Rest of task code goes here. */ } } void vAFunction( void ) { xTaskHandle xHandle; int iReturnedTaskHandle; if( xTaskCreate ( vATask, "Demo task", STACK_SIZE, NULL, TASK_PRIORITY, &xHandle ) == pdPASS ) { vTaskDelay( 100 ); iReturnedTaskHandle = ( int )xTaskGetApplicationTaskTag( xHandle ); } } First Technology Transfer - Feb 2013
294
Setting Up Logging with Tags Simple example below ● Sets an application task tag ● Defines a tracing macro to output a voltage associated with the task
●
}
void vTask1( void *pvParameters ) { /* First task sets its tag value to 1. */ vTaskSetApplicationTaskTag( NULL, ( void * ) 1 ); /*This task to be represented byvoltage scale of 1*/ for( ;; ) { /* Task code goes here. */ }
/* Second task sets its tag value to 2. */ void vTask1( void *pvParameters ) { vTaskSetApplicationTaskTag( NULL, ( void * ) 2 ); /*This task to be represented byvoltage scale of 2*/ for( ;; ) { /* Task code goes here. */ } } /* Defne the traceTASK_SWITCHED_IN() macro to output the voltage associated with the task being selected to run on port 0. */ #defne traceTASK_SWITCHED_IN() vSetAnalogueOutput( 0, ( int ) pxCurrentTCB->pxTaskTag ) First Technology Transfer - Feb 2013
295
Getting the Stack Watermark ●
unsigned portBASE_TYPE uxTaskGetStackHighWaterMark ( xTaskHandle xTask ); ●
Returns the minimum amount of remaining stack space that was available to the task since the task started executing i.e. –
The amount of stack that remained unused when the task stack was at its greatest (deepest) value. ●
●
●
INCLUDE_uxTaskGetStackHighWaterMark must be defined as 1 for this function to be available. Takes one argument, xTask - the handle of the task being queried. –
●
The stack 'high water mark'.
A task can query its own high water mark by passing NULL as the xTask parameter.
Return value is the high water mark in words (for example, on a 32 bit machine a return value of 1 would indicate that 4 bytes of stack were unused). –
A return value is zero then the task has likely overflowed its stack.
–
A value is close to zero means the task has come close to overflowing its stack.
First Technology Transfer - Feb 2013
296
Task Hooks ●
Task hooks are, typically, used in code tracing of applications ●
Typically via trace hook macros
●
portBASE_TYPE xTaskCallApplicationTaskHook( xTaskHandle xTask, void *pvParameter ); –
confgUSE_APPLICATION_TASK_TAG must be defined as 1 for this function to be available
–
A 'tag' value can be used to assign a hook (or callback) function to a task ●
The hook function is executed by calling xTaskCallApplicationTaskHook().
Each task can define its own callback – Task callbacks are optional Task hook functions must have type pdTASK_HOOK_CODE, i.e. –
–
●
Take a void * parameter, and return a value of type portBASE_TYPE.
The void * parameter can be used to pass any information into the hook function Takes two arguments –
–
●
xTask - the handle of the task whose hook function is being called. Passing NULL as xTask will call the hook function assocaited with the currently executing task. pvParameter - the value to pass to the hook function –
●
First Technology Transfer - Feb 2013
297
Task Hook - ctd. ●
A mini-template code snippet is shown below :
/* Defne the callback function Assign it as the task tag. Note: The callback function - this must have type pdTASK_HOOK_CODE */ static portBASE_TYPE prvExampleTaskHook( void * pvParameter ) { /* Do something appropriate e.g. output some information */ ... return 0; } /* Now defne the task that sets prvExampleTaskHook as its hook/tag value - in effect “register the task callback function” */ void vAnotherTask( void *pvParameters ) { vTaskSetApplicationTaskTag( NULL, prvExampleTaskHook ); /* Register callback function */ for( ;; ) { /* Work done by the task */ } } /* Example use of the hook (callback) - get the RTOS kernel to call the hook function of each task that is being switched out during a reschedule. */ #defne traceTASK_SWITCHED_OUT() xTaskCallApplicationTaskHook( pxCurrentTCB, 0 )
First Technology Transfer - Feb 2013
298
Exercise ●
●
●
Explore the FreeRTOS documentation describing trace hook macros and their uses Modify the examples you have been working on earlier to include some trace functionality Devise a mechanism to turn tracing on and off dynamically - covering the possible options listed below ●
Turn all tracing on and off
●
Turn tracing on and off selectively
First Technology Transfer - Feb 2013
299
FreeRTOS Inter-task Communication Queues, Mutexes and Semaphores
First Technology Transfer - Feb 2013
300
Queues ●
Queues are the primary form of intertask communication in FreeRTOS ●
●
●
●
Used to send messages between tasks, and between interrupts and tasks. Typically used as thread safe FIFO (First In First Out) buffers with new data being sent to the back of the queue Fixed size messages - message size defined at queue creation time Messages placed in the queue by copying, not by reference –
● ●
If required the messages can be references
Queue operations are blocking with a timeout The kernel takes care of allocating the memory used as the queue storage area
First Technology Transfer - Feb 2013
301
Queues - ctd. ●
●
Variable sized messages can be sent by defining queues to hold structures that contain –
A member that points to the queued message, and
–
Another member that holds the size of the queued message.
A single queue can be used to receive different message types, and messages from multiple locations –
Define the queue to hold a structure that has ● ●
●
A member that holds the message type, and Another member that holds the message data (or a pointer to the message data).
●
How the data is interpreted depends on the message type.
●
This approach is the basis for implementing event driven applications
Queues are well suited for use in a memory protected environment. –
A task that is restricted to a protected memory area can pass data to a task that is restricted to a different protected memory area because invoking the RTOS by calling the queue send function will raise the microcontroller privilege level.
–
The queue storage area is only accessed by the RTOS (with full privileges).
First Technology Transfer - Feb 2013
302
Blocking on Queues ●
Queue API functions permit a blocking time to be specified.
●
When a task attempts to read from an empty queue ●
The task will be placed into the Blocked state (so it is not consuming any CPU time and other tasks can run) until –
●
When a task attempts to write to a full queue ●
The task will be placed into the Blocked state (so it is not consuming any CPU time and other tasks can run) until –
●
●
Either data becomes available on the queue, or the block time expires.
Either space becomes available in the queue, or the block time expires.
If more than one task blocks on the same queue then the task with the highest priority will be the task that is unblocked first. Note: Interrupts must NOT use API functions that do not end in "FromISR".
First Technology Transfer - Feb 2013
303
Queue Management APIs ●
There are 4 main groups of API methods
●
Creation methods
●
●
Fully featured methods ●
xQueueSend
●
xQueueCreate
●
xQueueSendToBack
●
vQueueDelete
●
xQueueSendToFront
●
xQueueReceive
Light Weight methods ●
xQueueSendFromISR
●
xQueuePeek
●
xQueueSendToBackFromISR
●
uxQueueMessgesWaiting
●
xQueueSendToFrontFromISR
●
xQueueReceiveFromISR
●
●
●
uxQueueMessagesWaitingFromISR xQueueIsQueueEmptyFromISR xQueueIsQueueFullFromISR
First Technology Transfer - Feb 2013
●
Alternative - use where an alternative implementation is available ●
xQueueAltSendToBack
●
xQueueAltSendToFront
●
xQueueAltReceive
●
xQueueAltPeek
304
Creating and Deleting Queues ● ●
The queue API provides the ,expected, create and delete methods xQueueHandle xQueueCreate ( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ); ● Creates a new queue instance. – Allocates the storage required by the new queue – Returns a handle for the queue ● 0 if the queue cannot be created – Takes two arguments ● ●
uxQueueLength - maximum number of items that the queue can contain. uxItemSize - number of bytes each item in the queue will require. – Items are queued by copy, not by reference – Each item on the queue must be the same size –
●
void vQueueDelete( xQueueHandle xQueue ); ● Deletes a queue - freeing all the memory allocated for storing of items placed on the queue ●
Takes one argument - xQueue - a handle to the queue to be deleted
First Technology Transfer - Feb 2013
305
Sending and Reading Messages ●
The FreeRTOS queue API provides a fairly standard set of methods for interacting with queues ●
●
●
●
Sending, sending to front, sending to back, destructive reading (reading) and non-destructive reading (peaking), as well as Non-blocking receiving and sending methods to be used from ISRs Because queues are shared resources thread safe use is associated with the careful use of semaphores and mutexes
The next few slides will review the sensing and receiving API methods
First Technology Transfer - Feb 2013
306
Basic Sending - xQueueSend ●
portBASE_TYPE xQueueSend( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait ); ● This is a macro that calls xQueueGenericSend() – Included for backward compatibility with versions of FreeRTOS that did not include the xQueueSendToFront() and xQueueSendToBack() macros Equivalent to xQueueSendToBack() Posts an item on a queue. The item is queued by copy, not by reference. Must not be called from an interrupt service routine Takes the following parameters –
● ● ● ●
–
xQueue - handle to the queue on which the item is to be posted.
–
pvItemToQueue - pointer to the item that is to be placed on the queue
–
xTicksToWait - maximum amount of time the task should block ●
●
●
Will return immediately if the queue is full and xTicksToWait is set to 0. – Time is defined in tick periods If INCLUDE_vTaskSuspend is set to '1' then specifying the block time as portMAX_DELAY will cause the task to block indefinitely (without a timeout).
Returns pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL
First Technology Transfer - Feb 2013
307
Basic Sending - xQueueSend - ctd. ●
The following template / code snippet demonstrates the basic approach to working with queues
struct AMessage { portCHAR ucMessageID; portCHAR ucData[ 20 ]; } xMessage; unsigned portLONG ulVar = 10UL; void vATask( void *pvParameters ) { xQueueHandle xQueue1, xQueue2; struct AMessage *pxMessage; // Create a queue capable of containing 10 unsigned long values. xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) ); // Create a queue capable of containing 10 pointers to AMessage structures. xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); // ... if( xQueue1 != 0 ) { // Send an unsigned long. Wait for 10 ticks for space to become available if necessary. if( xQueueSend( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) != pdPASS ) { // Failed to post the message, even after 10 ticks. } } if( xQueue2 != 0 ) { // Send a pointer to a struct AMessage object. Don't block if the queue is already full. pxMessage = & xMessage; xQueueSend( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 ); } // ... Rest of task code. } First Technology Transfer - Feb 2013
308
Sending to Front / Back of a Queue ●
●
FreeRTOS provides an xQueueGenericSend() method which can be called to add queue elements to either the front or back of the queue xQueueSendToFront() and xQueueSendToBack() are macros that call xQueueGenericSend() ●
They both have similar macro definitions - the resulting function call returns a value of type portBASE_TYPE – –
The value is pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL The parameters are ●
xQueue - handle to the queue on which the item is to be posted.
●
pvItemToQueue - pointer to the item to be placed on the queue
●
●
xTicksToWait - the maximum amount of time the task should block waiting for space to become available on the queue, should it already be full. – The call will return immediately if this is set to 0 – The time is defined in tick periods If INCLUDE_vTaskSuspend is set to '1' then specifying the block time as portMAX_DELAY will cause the task to block indefinitely (i.e. without a timeout)
First Technology Transfer - Feb 2013
309
Sending to Back of Queue ●
The following code snippet/template illustrate the general pattern of use :
struct AMessage { portCHAR ucMessageID; portCHAR ucData[ 20 ]; } xMessage; unsigned portLONG ulVar = 10UL; void vATask( void *pvParameters ) { xQueueHandle xQueue1, xQueue2; struct AMessage *pxMessage; xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) ); xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); // ... if( xQueue1 != 0 ) { if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) != pdPASS ) { // Failed to post the message, even after 10 ticks. } } if( xQueue2 != 0 ) { pxMessage = & xMessage; xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 ); } // ... Rest of task code. } First Technology Transfer - Feb 2013
310
Reading a Message ●
●
FreeRTOS implements a xQueueGenericReceive() function that can be called upon to perform a ●
A Destructive read of a message - use the macro xQueueReceive()
●
A non-destructive read (the message remains in the queue) - use the macro xQueuePeek()
portBASE_TYPE xQueueReceive( xQueueHandle xQueue, void *pvBuffer, portTickType xTicksToWait ); ● ● ● ●
Receives an item from a queue. The item is received by copy into a suitable sized buffer Must not be used in an interrupt service routine Takes three parameters –
xQueue
–
pvBuffer - pointer to the buffer into which the received item will be copied.
–
xTicksToWait - maximum amount of time the task should block waiting for an item if the queue is empty when the call is made ●
● ●
●
- handle to the queue from which the item is to be received.
Setting xTicksToWait to 0 will cause the function to return immediately if the queue is empty. The time is defined in tick periods
If INCLUDE_vTaskSuspend is set to '1' then specifying the block time as portMAX_DELAY will cause the task to block indefinitely Return value is pdTRUE if an item was successfully received from the queue, otherwise pdFALSE
First Technology Transfer - Feb 2013
311
Reading a Message - Example ●
The basic pattern of reading and writing messages is shown below
struct AMessage { portCHAR ucMessageID; portCHAR ucData[ 20 ]; } xMessage; xQueueHandle xQueue; void vATask( void *pvParameters ) { // Task to create a queue and post a value. struct AMessage *pxMessage; xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) ); if( xQueue == 0 ) { // Failed to create the queue. } // ... // Send a pointer to a struct AMessage object. Don't block if the queue is already full. pxMessage = & xMessage; xQueueSend( xQueue, ( void * ) &pxMessage, ( portTickType ) 0 ); // ... Rest of task code. } void vADifferentTask( void *pvParameters ) { // Task to receive from the queue. struct AMessage *pxRxedMessage; if( xQueue != 0 ) { // Receive a message created queue. Block for 10 ticks if a queue empty if( xQueueReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) ) { / / pcRxedMessage now points to the struct AMessage variable posted by vATask. } } // ... Rest of task code. } First Technology Transfer - Feb 2013
312
Peeking at a Message ●
portBASE_TYPE xQueuePeek( xQueueHandle xQueue, void *pvBuffer, portTickType xTicksToWait ); ●
Similar to xQueueReceive() except that an item is received from the queue, but is not removed from it
●
Questions: Under what circumstances might you xQueuePeek ?
●
Exercise: ●
Implement a simple producer task that writes to a queue in a bursty way –
●
Implement a simple consumer task that reads from a queue –
●
Lots of writes then nothing for a while then more writes A low priority task that pauses for a while after every item has been read
Have one LED light up when the writer is writing and another LED light up when the reader is reading
First Technology Transfer - Feb 2013
313
Monitoring Queues ●
●
Explore the documentation for the following functions ●
uxQueueMessagesWaiting()
●
uxQueueMessagesWaitingFromISR()
●
xQueueIsQueueFullFromISR()
●
xQueueIsQueueEmptyFromISR()
●
xQueueSendFromISR()
●
xQueueSendToBackFromISR()
●
xQueueSendToFrontFromISR()
●
xQueueReceiveFromISR()
Modify the simple producer-consumer example so that ● High priority message get added to the front of the queue ● The producer slows down its rate of sending messages when the buffer is nearly full ● Devise some kind of simple “I am alive protocol” which can be used by the producer or consumer they are still running even though the buffer has been empty for a long time (consumer side issue), or full for a long time (producer side issue) ● Implement a timer interrupt handler routine that write some data to a queue on every timer interrupt ● Implement a timer interrupt handler routine that reads some data from a queue on every timer interrupt
First Technology Transfer - Feb 2013
314
Examples to Run and Study ●
Run the following FreeRTOS examples
●
PollQ.c - which demonstrates
●
●
Inter-task communications
●
Manually yielding processor time
●
Polling a queue for space to write
●
Polling a queue for space to read
●
Pre-emption
●
Creating tasks
BlockQ.c - which demonstrates ●
Inter-task communications
●
Blocking on queue reads
●
Blocking on queue writes
●
Passing parameters into a task
●
Pre-emption
●
Creating tasks
First Technology Transfer - Feb 2013
315
Queue Sets ●
Queue sets implement a mechanism which allows an RTOS task to block (pend) on a read operation from multiple RTOS queues or semaphores simultaneously ●
●
A queue set must be explicitly created via a call to xQueueCreateSet() before it can be used. ●
●
●
Note: An alternative method is to use Blocking on Multiple Objects
Once created, standard FreeRTOS queues and semaphores can be added to the set via calls to xQueueAddToSet() xQueueSelectFromSet() can be used to discover which, if any, of the queues or semaphores contained in the set is in a state where a queue read or semaphore take operation would be successful.
Notes: ●
●
Blocking on a queue set that contains a mutex will not cause the mutex holder to inherit the priority of the blocked task. A receive (in the case of a queue) or take (in the case of a semaphore) operation must not be performed on a member of a queue set –
Unless a call to xQueueSelectFromSet() has first returned a handle to that set member.
First Technology Transfer - Feb 2013
316
FreeRTOS Semaphores and Mutexes
First Technology Transfer - Feb 2013
317
Mutexes and Semaphores ●
Binary semaphores and mutexes are very similar ●
There are a few subtle differences that need to be understood –
Mutexes include a priority inheritance mechanism Are the better choice for implementing simple mutual exclusion Binary semaphores do not include a priority inheritance mechanism ●
–
Are the better choice for implementing synchronisation – Whether it be ● Between tasks or ● Between tasks and an interrupt A binary semaphore need not be given back once obtained ●
–
●
Hence, task synchronisation can be implemented by one task/interrupt continuously 'giving' the semaphore while another continuously 'takes' the semaphore
First Technology Transfer - Feb 2013
318
Mutexes and Semaphores - ctd. ●
The priority of a task that 'takes' a mutex can potentially be raised if another task of higher priority attempts to obtain the same mutex. –
The task that owns the mutex 'inherits' the priority of the task attempting to 'take' the same mutex.
–
This means the mutex must always be 'given' back
Otherwise the higher priority task will never be able to obtain the mutex, and – The lower priority task will never 'disinherit' the priority Both mutex and binary semaphores are assigned to variables of type xSemaphoreHandle and can be used in any API function that takes a parameter of this type ●
●
First Technology Transfer - Feb 2013
319
Counting Semaphore ●
The two main uses of counting semaphores are : ● Counting events. – In this use case an event handler will 'give' a semaphore each time an event occurs (incrementing the semaphore count value), and – A handler task will 'take' a semaphore each time it processes an event (decrementing the semaphore count value). – Providing the initial count value is zero - the count value will be the difference between the number of events that have occurred and the number that have been processed. ● Resource management. – In this use case the count value indicates the number of resource instances available. – To obtain control of a resource a task must first obtain a semaphore decrementing the semaphore count value. – When the count value reaches zero there are no available resources left – When a task finishes with the resource it 'gives' the semaphore back incrementing the semaphore count value. – The initial count value should be set to the maximum count value ● Corresponding to the situation where all the resources are available.
First Technology Transfer - Feb 2013
320
Recursive Mutex ●
●
A recursive mutex, when used recursively, can be 'taken' repeatedly by its owner. The mutex does not become available again until the owner has called xSemaphoreGiveRecursive() for each successful 'take' request e.g. ●
●
●
●
●
If a task successfully 'takes' the same mutex 3 times then the mutex will not be available to any other task until the owning task has 'given' the mutex back exactly 3 times.
Macro that implements a recursive mutex by using the existing queue mechanism. A recursive mutex uses a priority inheritance mechanism Hence, a task 'taking' a recursive mutex MUST ALWAYS 'give' that mutex back once it is no longer required. Mutex type semaphores cannot be used from within interrupt service routines.
First Technology Transfer - Feb 2013
321
Semaphores and Mutexes ●
●
In FreeRTOS the API methods for manipulating queues and semaphores/mutexes are macros that call down to the underlying functions in the queue framework implementation The queue and semaphore/mutex APIs are, in a certain sense, interoperable ●
●
●
Binary semaphore/mutex - is in effect, a queue that can hold 1 item Counting semaphore - is, in effect, a queue that can hold n items (each of data size 0)
It is possible to extend the API methods by defining new macros on the queue's implementation APIs ●
If you do this be sure to test these macros out thoroughly
First Technology Transfer - Feb 2013
322
Semaphore/Mutex Management APIs ●
●
The FreeRTOS Semaphore/Mutex management API methods can be broken down into 4 categories Creation ●
●
●
vSemaphoreCreateBinary xSemaphoreCreateCounting
●
xSemaphoreCreateMutex
●
xSemaphoreCreateRecursiveMutex
●
Light Weight ●
●
xSemaphoreGiveFromISR
Alternative - Used where an alternative implentation is present ●
xSemaphoreAltTake
●
xSemaphoreAltGive
Fully Featured ●
xSemaphoreTake
●
xSemaphoreTakeRecursive
●
xSemaphoreGive
●
xSemaphoreGiveRecursive
First Technology Transfer - Feb 2013
323
Taking and Giving of Semaphores ●
The following code template / snippet illustrates a typical usage pattern : xSemaphoreHandle xSemaphore = NULL; void vATask( void * pvParameters ) { // Create mutex the semaphore to guard a shared resource. xSemaphore = xSemaphoreCreateMutex(); } void vAnotherTask( void * pvParameters ) { // ... Do other things. if( xSemaphore != NULL ) { if( xSemaphoreTake( xSemaphore, ( portTickType ) 10 ) == pdTRUE ) { // Obtained the semaphore and can now access the shared resource. // ... // Release the semaphore - when fnished using the shared resources xSemaphoreGive( xSemaphore ); } else { // Could not obtain the semaphore - cannot access the shared resource safely. } } }
First Technology Transfer - Feb 2013
324
Exercise ●
Implementation of a Readers and Writers use case
●
The goal is to ●
Have a critical section where multiple writers are present but only one writer at a time can write
●
Multiple readers are present and multiple readers can read concurrently
●
Fairness of access for both readers and writers is requiredl
First Technology Transfer - Feb 2013
325
Interrupts and Resource Management
First Technology Transfer - Feb 2013
326
Event Driven Applications ●
Real world systems typically have to handle events originating from multiple sources ●
●
●
Different events may have different reponse time requirements and have different processing overheads Many of the issues may well be considered during the Requirements Analysis phase of a project - possibly also involving Use Case Analysis
Issues ●
●
Which events must be detected and how ? –
Interrupts ?
–
Polling ?
Interrupt handling –
In the interrupt handler or,
–
Split into top half and bottom work
Top half - critical work - done in the interrupt handler ● Bottom half - deferred interrupt handling work Communicating events from the interrupt handler to the main application code ●
●
First Technology Transfer - Feb 2013
327
Deferred Interrupt Handling ●
●
The approaches described by Richard Barry (see Using the FreeRTOS Real Time Kernel - Cortex M3 Edition) cover ●
Using binary semaphores for ISR - Task synchronisation
●
Using counting semaphores for ISR - Task synchronisation
The task code may also be structured around “active objects” an abstraction based on the combination of a queue with a state machine application that process events as they are place on the queue ●
www.state-machine.com/freertos/QDK_FreeRTOS.pdf
First Technology Transfer - Feb 2013
328
Binary Semaphore Synchronisation ●
Strategy for time critical event handling ●
●
●
Set the event handler task priority such the the handler task will always pre-empt any other tasks in the system Implement the ISR (the Cortex M3 architecture and FreeRTOS both allow ISRs to be implemented entirely in C) in such a way that the ISR returns directly to the handler task when the ISR completes executing Implement the handler task so that it blocks (take) on a semaphore that will be unblocked by the ISR (ISR performs a give operation on the semaphore before completing execution) –
●
The ISR uses a special form of xSemaphoreGive, namely, xSemaphoreGiveFromISR
The example given in the FreeRTOS book uses a software generated interrupt generated every 500 msec by a trivially simple periodic task ●
●
The handler task synchronises with the timer interrupt The interrupt handler, written in C, performs a give on the synchronising semaphore, clears the corresponding interrupt flag and performs a return from the interrupt handler
First Technology Transfer - Feb 2013
329
The Periodic and Handler Tasks ●
The code for the periodic task is : static void vPeriodicTask(void *pvParemeters) { for (;;) { vTaskDelay( 500 / portTICK_RATE_MS ); vPrintString(“Periodic task - generating interrupt\n”); mainTRIGGER_INTERRUPT(); // Sets a bit in the interrupt controller's // Set Pending register vPrintString(“Periodic task - interrupt generated\n”); }
●
The code for the handler task is : static void vHandlerTask(void *pvParemeters) { xSemaphoreTake(xBinarySemaphore, 0); for (;;) { xSemaphoreTake(xBinarySemaphore, portMAX_DELAY); vPrintString(“Handler task - event processing\n”); }
First Technology Transfer - Feb 2013
330
Interrupt Handler and main() ●
The code for the software interrupt handler is : void vSoftwareInterruptHandler (void) { portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; xSemaphoreGiveFromISR ( xBinarySemaphore, &xHigherPriorityTaskWoken ); mainCLEAR_INTERRUPT; portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); // portEND_SWITCHING_ISR() is part of the CortexM3 port layer // Never call taskYield() from an ISR }
●
The code for main() is : int main(void) { vSetupEnvironment(); vSemaphoreCreateBinary( xBinarySemaphore); if (xBinarySemaphore != NULL) { prvSetupSoftwareInterrupt(); // Enable software interrupt and set // its priority xTaskCreate( vHandlerTask, “Handler”, 240, NULL, 1, NULL ); vTaskStartScheduler(); } for( ;; ) ; }
First Technology Transfer - Feb 2013
331
Counting Semaphore Synchronisation ●
A counting semaphore can be used to count events ●
●
●
●
Any subsequent events that occur till the latched event is processed will be missed This is not the case with the counting semaphore approach
The previous binary semaphore demo can be adapted to one using a counting semaphore as follows ●
●
●
A binary semaphore can be sued to latch at most one interrupt event
Create a counting semaphore in place of the binary semaphore Simulate multiple events occurring at high frequency by getting the interrupt service routine to 'give' the semaphore more than once per interrupt
The modified code for main() and for the interrupt handler is shown on the next slide
First Technology Transfer - Feb 2013
332
Interrupt Handler and main() ●
The code for the software interrupt handler is : void vSoftwareInterruptHandler (void) { portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; xSemaphoreGiveFromISR ( xCountingSemaphore, &xHigherPriorityTaskWoken ); xSemaphoreGiveFromISR ( xCountingSemaphore, &xHigherPriorityTaskWoken ); xSemaphoreGiveFromISR ( xCountingSemaphore, &xHigherPriorityTaskWoken ); mainCLEAR_INTERRUPT; portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); // portEND_SWITCHING_ISR() is part of the CortexM3 port layer // Never call taskYield() from an ISR }
●
The code for main() is : int main(void) { vSetupEnvironment(); xCountingSemaphore = xSemaphoreCreateCounting( 10, 0); if (xCountingSemaphore != NULL) { prvSetupSoftwareInterrupt(); // Enable software interrupt and set // its priority xTaskCreate( vHandlerTask, “Handler”, 240, NULL, 1, NULL ); vTaskStartScheduler(); } for( ;; ) ; }
First Technology Transfer - Feb 2013
333
Queues Within an ISR ●
●
●
●
Whereas semaphores can be used to communicate events, queues can be used to both communicate events and to transfer data FreeRTOS provides the xQueueSendToFrontFromISR() and the xQueueSendToBackFromISR() API methods for just this purpose The example in the FreeRTOS book ● Uses a periodic task that – Sends 5 numbers to a queue every 200 milliseconds – Generate a software interrupt only after all 5 values have been sent ● The interrupt service routine – Calls xQueueReceiveFromISR() repeatedly to remove all the values from the queue – The last two bits of each integer value are used as an index into an array of strings and a pointer to the string at the given index position is written to another queue using xQueueSendFromISR() ● The task receiving the string pointers from the interrupt service routine blocks on the queue until a message arrives – It prints out strings as it receives them The code snippets are shown on the next few slides.
First Technology Transfer - Feb 2013
334
vIntegerGenerator static void vIntegerGenerator( void *pvParameters ) { portTickType xLastExecutionTime; unsigned portLONG ulValueToSend = 0; int i; /* Initialize the variable used by the call to vTaskDelayUntil(). */ xLastExecutionTime = xTaskGetTickCount(); for( ;; ) { /* Periodic task - executes every 200ms. */ vTaskDelayUntil( &xLastExecutionTime, 200 / portTICK_RATE_MS ); for( i = 0; i < 5; i++ ) { xQueueSendToBack( xIntegerQueue, &ulValueToSend, 0 ); ulValueToSend++; }
}
}
/* Force an interrupt */ vPrintString( "Generator task - About to generate an interrupt.\n" ); mainTRIGGER_INTERRUPT(); vPrintString( "Generator task - Interrupt generated.\n\n" );
First Technology Transfer - Feb 2013
335
vSoftwareInterruptHandler void vSoftwareInterruptHandler( void ) { portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; static unsigned long ulReceivedNumber; static const char *pcStrings[] = { "String 0\n", "String 1\n", "String 2\n", "String 3\n" }; /* Loop until the queue is empty. */ while( xQueueReceiveFromISR( xIntegerQueue, &ulReceivedNumber, &xHigherPriorityTaskWoken ) != errQUEUE_EMPTY ) { ulReceivedNumber &= 0x03; xQueueSendToBackFromISR( xStringQueue, &pcStrings[ ulReceivedNumber ], &xHigherPriorityTaskWoken ); } mainCLEAR_INTERRUPT(); portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); }
First Technology Transfer - Feb 2013
336
vStringPrinter and main() static void vStringPrinter( void *pvParameters ) { char *pcString; for( ;; ) { /* Block on the queue to wait for data to arrive. */ xQueueReceive( xStringQueue, &pcString, portMAX_DELAY ); vPrintString( pcString ); } } int main( void ) { SysCtlClockSet( SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ ); xIntegerQueue = xQueueCreate( 10, sizeof( unsigned long ) ); xStringQueue = xQueueCreate( 10, sizeof( char * ) ); /* Enable the software interrupt and set its priority. */ prvSetupSoftwareInterrupt(); /* Create the task to pass integers to the interrupt service routine (priority 1) */ xTaskCreate( vIntegerGenerator, "IntGen", 240, NULL, 1, NULL ); /* Create the task that prints out the strings sent from the interrupt service routine (priority 2) */ xTaskCreate( vStringPrinter, "String", 240, NULL, 2, NULL ); vTaskStartScheduler(); for( ;; ) ; } First Technology Transfer - Feb 2013
337
Critical Sections ●
FreeRTOS provides two approaches for implementing critical sections ●
●
●
Basic critical section realisation using the taskENTER_CRITICAL() and taskEXIT_CRITICAL() macros Suspending (Locking) the scheduler
The basic critical section realisation macros ●
Work by disabling interrupts up to the interrupt priority set by confgMAX_SYSCALL_INTERRUPT_PRIORITY –
Pre-emptive context switches can occur only from within an interrupt
–
As long as interrupts remain disabled the task that called taskENTER_CRITICAL() will remain in the Running state until it exits the critical section
–
Critical sections can become nested
–
The kernel keeps a count of nesting depth Critical sections need to be kept short ●
–
Each call to taskENTER_CRITICAL() must be paired with a corresponding call to taskEXIT_CRITICAL()
First Technology Transfer - Feb 2013
338
Critical Sections - ctd. ●
Creating critical sections by suspending the scheduler ●
Should be considered where the critical section code is not particularly short –
●
In this case disabling interrupts might not be an acceptable approach
The scheduler is suspended by invoking vTaskSuspendAll() –
This prevents context switching, but leaves interrupts enabled
–
FreeRTOS API functions should not be called whilst the scheduler is disabled
–
Calls to vTaskSuspendAll() and xTaskResumeAll() can be nested ● ●
The kernel keeps a count of nexting depth Scheduler resumed only when nesting depth returns to zero.
First Technology Transfer - Feb 2013
339
Gatekeeper Tasks ●
A gatekeeper task is one that has sole ownership of a resource ●
●
Only the gatekeeper task can access the resource directly Any other tasking requiring that particular resource must use the services provided by the gatekeeper task e.g. –
If a gatekeeper task has been implemented to manage access to standard out then ●
●
● ●
●
A task wishing to send a message does not call a print function directly – It sends a message to the gatekeeper The gatekeeper can use a FreeRTOS queue to serialise access to the terminal When not writing messages the gatekeeper task is blocked When a message arrives, it unblocks and writhe the message to the terminal Because interrupts can send to queues – An ISR can also use the services of a gatekeeper task
First Technology Transfer - Feb 2013
340
Gatekeeper Tasks - ctd. ●
●
The FreeRTOS gatekeeper task demo uses a tick hook function to write out a message every 200 ticks The relevant code fragments are shown on the next few slides static void prvStdioGatekeeperTask( void *pvParameters ) { char *pcMessageToPrint; static char cBuffer[ mainMAX_MSG_LEN ];
}
/* This is the only task that is allowed to write to the terminal output. Any other task wanting to write to the output does not access the terminal directly, but instead sends the output to this task. */ for( ;; ) { /* Wait for a message to arrive. */ xQueueReceive( xPrintQueue, &pcMessageToPrint, portMAX_DELAY ); /* There is no need to check the return value as the task will block indefnitely and only run again when a message has arrived. */ sprintf( cBuffer, "%s", pcMessageToPrint ); printf( cBuffer ); /* Now simply go back to wait for the next message. */ }
First Technology Transfer - Feb 2013
341
Gatekeeper Tasks - ctd. ●
prvPrintTask static void prvPrintTask( void *pvParameters ) { int iIndexToString; /* Two instances of this task are created so the index to the string the task will send to the gatekeeper task is passed in the task parameter. */ iIndexToString = ( int ) pvParameters; for( ;; ) { /* Print out the string, by passing the string to the gatekeeper task on the queue. The queue is created before the scheduler is started so will already exist by the time this task executes. */ xQueueSendToBack( xPrintQueue, &( pcStringsToPrint[ iIndexToString ] ), 0 );
}
}
/* Wait a pseudo random time. In a more secure application a version of rand() that is known to be re-entrant should be used - or calls to rand() should be protected using a critical section. */ vTaskDelay( ( rand() & 0x1FF ) );
First Technology Transfer - Feb 2013
342
Gatekeeper Tasks - ctd. vApplicationTickHook
●
void vApplicationTickHook( void ) { static int iCount = 0; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; /* Print out a message every 200 ticks. The message is not written out directly, but sent to the gatekeeper task. */ iCount++; if( iCount >= 200 ) { /* The last parameter (xHigherPriorityTaskWoken) is not actually used but must still be supplied. */ xQueueSendToFrontFromISR( xPrintQueue, &( pcStringsToPrint[ 2 ] ), &xHigherPriorityTaskWoken );
}
}
/* Reset the count ready to print out the string again in 200 ticks time. */ iCount = 0;
First Technology Transfer - Feb 2013
343
Gatekeeper Tasks - ctd. ●
main()
static char *pcStringsToPrint[] = { "Task 1 ****************************************************\n", "Task 2 ----------------------------------------------------\n", "Message printed from the tick hook interrupt ##############\n" }; xQueueHandle xPrintQueue; int main( void ) { SysCtlClockSet( SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ ); xPrintQueue = xQueueCreate( 5, sizeof( char * ) ); srand( 567 ); if( xPrintQueue != NULL ) { /* Tasks created at different priorities so some pre-emption will occur. 4th parameter is index into list of strings to print */ xTaskCreate( prvPrintTask, "Print1", 240, ( void * ) 0, 1, NULL ); xTaskCreate( prvPrintTask, "Print2", 240, ( void * ) 1, 2, NULL ); /* Create the gatekeeper task - the only task permitted to access standard out. */ xTaskCreate( prvStdioGatekeeperTask, "Gatekeeper", 240, NULL, 0, NULL ); vTaskStartScheduler(); } for( ;; ) ; } First Technology Transfer - Feb 2013
344
View more...
Comments