Freertos Arm Cortex

September 29, 2017 | Author: shabid.ali373 | Category: Computer Architecture, Technology, Computing, Computer Engineering, Areas Of Computer Science
Share Embed Donate


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

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF