UW LaTeX Thesis Ece254 Manual

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 119

DownloadUW LaTeX Thesis  Ece254 Manual
Open PDF In BrowserView PDF
Electrical and Computer Engineering
(ECE) Operating Systems and
System Programming ECE254
Laboratory Manual
by

Yiqing Huang
Paul A.S. Ward
Jeff Zarnett

Electrical and Computer Engineering Department
University of Waterloo

Waterloo, Ontario, Canada, May 15, 2017

c Y. Huang, P.A.S. Ward and J. Zarnett 2016

Contents
List of Tables

vii

List of Figures

x

Preface

1

I

1

II

Lab Administration
Lab Projects

6

1 Introduction to ARM RL-RTX Kernel and Application Programming

7

1.1

Objective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7

1.2

Starter Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7

1.3

Pre-lab Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8

1.4

Warm-up Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8

1.4.1

Build and Run the HelloWorld Application . . . . . . . . . . . . . .

8

Real-time Executive Exercises . . . . . . . . . . . . . . . . . . . . . . . . .

13

1.5.1

Manual of RL-RTX . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

1.5.2

Creating a Real-time Executive Application . . . . . . . . . . . . .

14

1.5.3

Building an RL-RTX Library for Cortex-M3 . . . . . . . . . . . . .

18

1.5.4

Creating a Multi-project Workspace . . . . . . . . . . . . . . . . . .

21

1.5.5

Making an RTX Application with a Self-built RTX Library . . . . .

23

1.5

ii

1.6

1.7

1.8

Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

25

1.6.1

Questions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

25

1.6.2

Programming Project Description . . . . . . . . . . . . . . . . . . .

26

1.6.3

Adding a New Function to the RTX Library . . . . . . . . . . . . .

26

1.6.4

Using the Newly Created RTX Function . . . . . . . . . . . . . . .

28

Deliverables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

1.7.1

Pre-lab Deliverables

. . . . . . . . . . . . . . . . . . . . . . . . . .

29

1.7.2

Post-lab Deliverables . . . . . . . . . . . . . . . . . . . . . . . . . .

29

Marking Rubric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

2 Task Management in ARM RL-RTX

30

2.1

Objective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

30

2.2

Starter files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

30

2.3

Pre-lab Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

2.4

Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

2.4.1

Questions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

2.4.2

Programming Project . . . . . . . . . . . . . . . . . . . . . . . . . .

33

2.4.3

Source Code File Organization Convention . . . . . . . . . . . . . .

36

2.4.4

Third-party Testing . . . . . . . . . . . . . . . . . . . . . . . . . . .

37

Deliverables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

37

2.5.1

Pre-Lab Deliverables . . . . . . . . . . . . . . . . . . . . . . . . . .

37

2.5.2

Post-Lab Deliverables . . . . . . . . . . . . . . . . . . . . . . . . . .

38

Marking Rubric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

38

2.5

2.6

3 Inter-task Communication and Concurrency

40

3.1

Objective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

3.2

Starter Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

3.3

Pre-lab Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

3.4

Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

42

3.5

Deliverables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

iii

3.6

3.5.1

Pre-lab Deliverables

. . . . . . . . . . . . . . . . . . . . . . . . . .

45

3.5.2

Post-lab Deliverables . . . . . . . . . . . . . . . . . . . . . . . . . .

45

Report Marking Rubric . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

4 Memory Management

47

4.1

Objective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

4.2

Starter Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

4.3

Pre-lab Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

4.4

Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

48

4.4.1

Programming Project . . . . . . . . . . . . . . . . . . . . . . . . . .

48

4.4.2

Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

52

4.4.3

Third-party Testing and Source Code File Organization . . . . . . .

52

4.5

4.6

III

Deliverable

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

53

4.5.1

Pre-Lab Deliverables . . . . . . . . . . . . . . . . . . . . . . . . . .

53

4.5.2

Post-Lab Deliverables . . . . . . . . . . . . . . . . . . . . . . . . . .

53

Marking Rubric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

53

Development Environment Quick Reference Guide

1 Keil Software Development Tools
1.1

1.2

55
56

Creating an Application in µVision4 IDE . . . . . . . . . . . . . . . . . .

56

1.1.1

Create a New Project . . . . . . . . . . . . . . . . . . . . . . . . . .

57

1.1.2

Managing Project Components . . . . . . . . . . . . . . . . . . . .

57

1.1.3

Build and Download . . . . . . . . . . . . . . . . . . . . . . . . . .

61

Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

63

1.2.1

Simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

64

1.2.2

Configure In-Memory Execution Using ULINK Cortex Debugger . .

64

iv

2 Programming MCB1700

67

2.1

The Thumb-2 Instruction Set Architecture . . . . . . . . . . . . . . . . . .

67

2.2

ARM Architecture Procedure Call Standard (AAPCS) . . . . . . . . . . .

67

2.3

Cortex Microcontroller Software Interface Standard (CMSIS) . . . . . . . .

70

2.3.1

CMSIS files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71

2.3.2

Cortex-M Core Peripherals . . . . . . . . . . . . . . . . . . . . . . .

71

2.3.3

System Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

2.3.4

Intrinsic Functions . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

2.3.5

Vendor Peripherals . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

2.4

Accessing C Symbols from Assembly . . . . . . . . . . . . . . . . . . . . .

74

2.5

SVC Programming: Writing an RTX API Function . . . . . . . . . . . . .

76

3 Introduction to ECE Linux Programming Environment

79

3.1

Linux Hardware Environment . . . . . . . . . . . . . . . . . . . . . . . . .

79

3.2

How to Connect to Linux Servers . . . . . . . . . . . . . . . . . . . . . . .

79

3.3

Work Environment Setup

. . . . . . . . . . . . . . . . . . . . . . . . . . .

80

3.3.1

Setting up Remote Linux Graphic Support . . . . . . . . . . . . . .

80

3.3.2

Mapping Linux Account on Nexus . . . . . . . . . . . . . . . . . . .

81

Basic Software Development Tools . . . . . . . . . . . . . . . . . . . . . . .

83

3.4.1

Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

83

3.4.2

C Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

84

3.4.3

Debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

84

More on Development Tools . . . . . . . . . . . . . . . . . . . . . . . . . .

85

3.5.1

How to Automate Build . . . . . . . . . . . . . . . . . . . . . . . .

85

3.5.2

Version Control Software . . . . . . . . . . . . . . . . . . . . . . . .

87

3.5.3

Integrated Development Environment . . . . . . . . . . . . . . . . .

88

Man Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

88

3.4

3.5

3.6

A Forms

90

B MDK-ARM Installation

92
v

C Keil MCB1700 Hardware Environment

94

C.1 MCB1700 Board Overview . . . . . . . . . . . . . . . . . . . . . . . . . . .

94

C.2 Cortex-M3 Processor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

94

C.2.1 Registers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

97

C.2.2 Processor mode and privilege levels . . . . . . . . . . . . . . . . . .

99

C.2.3 Stacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
C.3 Memory Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
C.4 Exceptions and Interrupts . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
C.4.1 Vector Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
C.4.2 Exception Entry . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
C.4.3 EXC RETURN Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
C.4.4 Exception Return . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
C.5 Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
References

106

vi

List of Tables
1

Project Deliverable Weight of the Lab Grade, Scheduled Lab Sessions and
Deadlines. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3

1.1

Lab1 Marking Rubric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

2.1

Lab2 Marking Rubric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

39

3.1

Timing measurement data table for given (N, B, P, C) values. . . . . . . .

46

3.2

Lab3 Marking Rubric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

46

4.1

Lab4 Marking Rubric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

54

2.1

Assembler instruction examples . . . . . . . . . . . . . . . . . . . . . . . .

68

2.2

Core Registers and AAPCS Usage . . . . . . . . . . . . . . . . . . . . . . .

69

2.3

CMSIS intrinsic functions . . . . . . . . . . . . . . . . . . . . . . . . . . .

74

3.1

Programming Steps and Tools . . . . . . . . . . . . . . . . . . . . . . . . .

83

C.1 Summary of processor mode, execution privilege level, and stack use options 100
C.2 LPC1768 Memory Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
C.3 LPC1768 Exception and Interrupt Table . . . . . . . . . . . . . . . . . . . 102
C.4 EXC RETURN bit fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
C.5 EXC RETURN Values on Cortex-M3 . . . . . . . . . . . . . . . . . . . . . . . 104

vii

List of Figures
1.1

Keil IDE: List of Targets . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8

1.2

Keil IDE: Target Option Button . . . . . . . . . . . . . . . . . . . . . . . .

9

1.3

Keil IDE: Target Default Memory Map Configuration . . . . . . . . . . . .

9

1.4

Keil IDE: Simulator Debugger Configuration . . . . . . . . . . . . . . . . .

10

1.5

Keil IDE: Build Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

1.6

Keil IDE: Start/Stop Debug Session Button . . . . . . . . . . . . . . . . .

10

1.7

Keil IDE: Evaluation Mode Code Size Warning Dialog Box . . . . . . . . .

10

1.8

Keil IDE: Enable UART Window View . . . . . . . . . . . . . . . . . . . .

11

1.9

Keil IDE: UART Window View and Run Button . . . . . . . . . . . . . . .

11

1.10 Keil IDE: Target In-Memory Execution Memory Map Configuration . . . .

12

1.11 Keil IDE: ULINK2/ME Cortex Debugger Configuration . . . . . . . . . . .

12

1.12 Keil IDE: RL-RTX Manual . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

1.13 Keil IDE: Using RTX Kernel . . . . . . . . . . . . . . . . . . . . . . . . . .

17

1.14 Keil IDE: RTX HelloWorld Project Files . . . . . . . . . . . . . . . . . . .

17

1.15 Configuring RTX Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18

1.16 RTX Library Source Files for Cortex-M3 . . . . . . . . . . . . . . . . . . .

19

1.17 RTX Library Project Components for Cortex-M3 . . . . . . . . . . . . . .

20

1.18 RTX Library Project Optimization Level Setting

. . . . . . . . . . . . . .

21

1.19 Keil IDE: Create New Multi-Project Worksapce . . . . . . . . . . . . . . .

21

1.20 Keil IDE: Naming a New Multi-Project Worksapce . . . . . . . . . . . . .

22

1.21 Keil IDE: Adding a µVision Project into Worksapce . . . . . . . . . . . .

22

1.22 Keil IDE: Adding RTX CM Lib.uvproj into Worksapce . . . . . . . . . . . .

23

viii

1.23 Keil IDE: Workspace with Two Projects . . . . . . . . . . . . . . . . . . .

23

1.24 Keil IDE: Batch Build . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

24

1.25 Keil IDE: Set an Active Project . . . . . . . . . . . . . . . . . . . . . . . .

24

1.26 Keil IDE: Removing Linkage with Stocked RTX Library . . . . . . . . . .

25

1.27 Keil IDE: Adding Your Own RTX Library . . . . . . . . . . . . . . . . . .

25

2.1

Keil IDE: Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

2.2

Keil IDE: Location of Test Specification File. . . . . . . . . . . . . . . . . .

38

1.1

Keil IDE: Create a New Project . . . . . . . . . . . . . . . . . . . . . . . .

57

1.2

Keil IDE: Choose MCU

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

58

1.3

Keil IDE: Copy Startup Code . . . . . . . . . . . . . . . . . . . . . . . . .

58

1.4

Keil IDE: A default new project . . . . . . . . . . . . . . . . . . . . . . . .

58

1.5

Keil IDE: Manage Project Components . . . . . . . . . . . . . . . . . . . .

59

1.6

Keil IDE: Manage Components Window . . . . . . . . . . . . . . . . . . .

59

1.7

Keil IDE: Updated Project Profile . . . . . . . . . . . . . . . . . . . . . . .

60

1.8

Keil IDE: Add Source File to Source Group . . . . . . . . . . . . . . . . .

60

1.9

Keil IDE: Updated Project Profile . . . . . . . . . . . . . . . . . . . . . . .

60

1.10 Keil IDE: Create New File . . . . . . . . . . . . . . . . . . . . . . . . . . .

61

1.11 Keil IDE: Final Project Setting . . . . . . . . . . . . . . . . . . . . . . . .

61

1.12 Keil IDE: Selecting Output Folder . . . . . . . . . . . . . . . . . . . . . . .

62

1.13 Keil IDE: Build Target . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

62

1.14 Keil IDE: Build Target . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

63

1.15 Keil IDE: Download Target to Flash . . . . . . . . . . . . . . . . . . . . .

63

1.16 Keil IDE: Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

65

1.17 Keil IDE: Using Simulator for Debugging . . . . . . . . . . . . . . . . . . .

66

1.18 Keil IDE: Using ULINK Cortex Debugger . . . . . . . . . . . . . . . . . .

66

1.19 Keil IDE: Configure for In-Memory Execution . . . . . . . . . . . . . . . .

66

2.1

Role of CMSIS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

70

2.2

CMSIS Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71

ix

2.3

CMSIS Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

2.4

CMSIS NVIC Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

2.5

SVC as a Gateway for OS Functions [8] . . . . . . . . . . . . . . . . . . . .

76

3.1

Invoking Terminal Clients on an ECE Nexus PC

. . . . . . . . . . . . . .

80

3.2

Invoking Xming on an ECE Nexus Computer . . . . . . . . . . . . . . . .

81

3.3

SSH Secure Shell Client X11 Setting . . . . . . . . . . . . . . . . . . . . .

81

3.4

PuTTY X11 Forwarding Setting . . . . . . . . . . . . . . . . . . . . . . . .

82

3.5

SSH Secure Shell Client X11 Setting . . . . . . . . . . . . . . . . . . . . .

82

B.1 MDK-ARM Installation Steps: Choose Example Projects . . . . . . . . . .

92

B.2 MDK-ARM Installation Steps: Finish . . . . . . . . . . . . . . . . . . . . .

93

B.3 MDK-ARM Installation Steps: ULINK Pro Driver . . . . . . . . . . . . . .

93

C.1 MCB1700 Board Components . . . . . . . . . . . . . . . . . . . . . . . . .

95

C.2 MCB1700 Board Block Diagram . . . . . . . . . . . . . . . . . . . . . . . .

95

C.3 LPC1768 Block Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . .

96

C.4 Simplified Cortex-M3 Block Diagram . . . . . . . . . . . . . . . . . . . . .

97

C.5 Cortex-M3 Registers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

98

C.6 Cortex-M3 Operating Mode and Privilege Level . . . . . . . . . . . . . . .

99

C.7 Cortex-M3 Exception Stack Frame . . . . . . . . . . . . . . . . . . . . . . 103

x

Preface
Two operating systems are used in laboratories. One is the ARM RL-RTX that supports
ARM Cortex-M3 processors on Keil MCB1700 boards. The second is the general purpose
operating system Linux that supports Intel/AMD processors on personal computers.
The ARM RL-RTX, a real-time operating system library, is for practising the operating
system kernel programming aspect of the course. The Linux computing environment is for
practising system programming aspect of the course.
The first purpose of this document is to provide the descriptions of each laboratory
project. The second purpose of this document is a quick reference guide of the relevant
development tools for completing laboratory projects.

Who Should Read This Lab Manual?
This lab manual is written for students who are taking Electrical and Computer Engineering (ECE) Operating Systems and System programming course ECE254 in the University
of Waterloo.

What is in This Lab Manual?
This manual is divided into three parts.
Part I describes the lab administration policies
Part II is a set of course laboratory projects as follows.
• Lab1: Introduction to ARM RL-RTX kernel and application programming
• Lab2: Task management in ARM RL-RTX
• Lab3: Inter-task communication and concurrency in Linux
1

• Lab4: Memory management in Linux
Part III is the reference guide of the development tools for ARM RL-RTX and Linux.
The main topics are as follows.
• Keil MCB1700 Development Hardware Environment and Software Tools
– Keil MCB1700 hardware environment
– Keil MCB1700 software development Tools
– Programming MCB1700 with ARM RL-RTX
∗ Building an RTX application
∗ Building a customized ARM RL-RTX library
∗ Creating an application with customized ARM RL-RTX library
– Programming MCB1700
• Software Development Tools on Linux
– Linux hardware environment
– Editors
– Compiler
– Debugger
– Utility to automate build
– Utility for version control

Acknowledgments
We would like to sincerely thank our students who took ECE254 and MTE2411 courses in
the past three years. They provided constructive feedback every term to make the manual
more useful to address problems that students would encounter when working on each lab
assignment.
Special thank goes to Thomas Reidemeister who shared his prototyping work in SE3502
course project on Keil MCB1700 boards with us.
1

MTE241 is the course number for Introduction to Real-time Systems in the University of Waterloo
Mechatronics Engineering program.
2
SE350 is the course number for Operating Systems in the University of Waterloo Software Engineering
Program.

2

We are grateful to teaching assistants Bo Zhu, Nabil Drawil, Pei Wang, Shasha Zhu,
Zheng Wu and Zack Newsham who provided valuable feedback and improved lab tutorials.
Professor Ajit Singh, Professor Rodolfo Pellizzoni, Carlos Moreno and Douglas W.
Harder in the Electrical and Computer Engineering department have provided valuable
laboratory project improvement feedback. Professor Pellizzoni proof-read the entire manual meticulously. Douglas W. Harder also carefully proof-read descriptions of part of the
labs. We warmly acknowledge their contributions.
The project and manual won’t be possible without lab facilities. Thank Roger Sanderson for providing us with lab tools and resources. Our gratitude also goes out to Eric Praetzel who sets up the Keil boards in lab and maintains the Keil software on Nexus machines;
Laura Winger who managed to customize the boards so that we have the neat plastic cover
to protect our hardware. We appreciate that Bernie Roehl and Rasoul Keshavarzi-Valdani
have shared their valuable Keil board experiences with us. Bob Boy from ARM always
answers our questions in a detailed and timely manner. Thank everyone who has helped.

3

Part I
Lab Administration

1

Lab Administration Policy
Group Lab Policy
• Group Size. All labs are done in a group of two. A size of three is only considered
in a lab section that has an odd number of students and only one group is allowed to
have a size of three. All group of three requests are processed on a first-come firstserved basis. A group size of one is not permitted except that your group is split up.
There is no workload reduction if you do the labs individually. Everyone in the group
normally gets the same mark. The Learn at URL http://learn.uwaterloo.ca is
used to signup for groups. The lab group signup is due by 10:00pm on the First
Friday of the academic term. Late group sign-up is not accepted and will result in
losing the entire lab sign-up mark, which is 2% of the total lab grade.
• Group Split-up. If you notice workload imbalance, try to solve it as soon as possible
within your group or split-up the group as the last resort. Group split-up is only
allowed once. You are allowed to join a one member group after the split-up. But
you are not allowed to split up from the newly formed group again. There is one
grace day deduction penalty to be applied to each member in the old group. We
highly recommend everyone to stay with your group members as much as possible,
for the ability to do team work will be an important skill in your future career. Please
choose your lab partners carefully. A copy of the code and documentation completed
before the group split-up will be given to each individual in the group.
• Group Split-up Deadline. To split from your group for a particular lab, you need
to notify the lab instructor in writing and sign the group slip-up form (see Appendix).
Labn (n=1,2,3,4) group split-up form needs to be submitted to the lab instructor by
4:30pm Thursday in the week that Labn has scheduled lab sessions. If you are late
to submit the split-up form, then you need to finish Labn as a group and submit
your split-up form during the week where Lab(n+1) has scheduled sessions and split
starting from Lab(n+1).

2

Deliverable
Group Sign-up
LAB1
LAB2
LAB3
LAB4

Weight
2%
8%
30%
30%
30%

Lab Session Week
Week 1
Week 1
Weeks 3 and 5
Weeks 7 and 9
Week 11

Deadline
10:00pm Friday in Week 1
10:00pm Wednesday in Week
10:00pm Wednesday in Week
10:00pm Wednesday in Week
10:00pm Wednesday in Week

2
6
10
12

Table 1: Project Deliverable Weight of the Lab Grade, Scheduled Lab Sessions and Deadlines.

Lab Assignments Grading and Deadline Policy
Labs are graded by lab TAs based on the rubric listed in each lab. The weight of each lab
towards your final lab grade is listed in Table 1.
• Lab Assignment Preparation and Due Dates. Students are required to prepare
the lab well before they come to the schedule lab session. Pre-lab deliverable for each
lab is due before the scheduled lab session starts. During the scheduled lab session,
we either provide in lab help or conduct lab assignment evaluation or do both at the
same time.
The detailed deadlines of post-lab deliverables are displayed in Table 1.
• Lab Assignment Late Submissions. Late submission is accepted within five days
after the deadline of the lab. No late submission is accepted five days after the lab
deadline. There are five grace days 3 that can be used for some post-lab deliverables
late submissions 4 . A group split-up will consume one grace day. After all grace days
are consumed, a 10% per day late submission penalty will be applied. However if it
is five days after the lab deadline, no late submission is accepted.
• Lab Re-grading. To initiate a re-grading process, contact the grading TA in charge
first. The re-grading is a rigid process. The entire lab will be re-graded. Your new
grades may be lower, unchanged or higher than the original grade received. If you
are still not satisfied with the grades received after the re-grading, escalate your case
to the lab instructor to request a review and the lab instructor will finalize the case.
3

Grace days are calendar days. Days in weekends are counted.
A post-lab deliverable that does not accept a late submission will be clearly stated in the lab assignment
description. Normally grace days are for lab reports. Labs whose evaluation involves demonstrations do
not accept late submissions of the code.
4

3

Lab Repeating Policy
For a student who repeats the course, labs need to be re-done with a new lab partner.
Simply turning in the old lab code is not allowed. We understand that the student may
choose a similar route to the solution chosen last time the course was taken. However it
should not be identical. The labs will be done a second time, we expect that the student
will improve the older solutions. Also the new lab partner should be contributing equally,
which will also lead to differences in the solutions.
Note that the policy is course specific to the discretion of the course instructor and the
lab instructor.

Lab Assignments Solution Internet Policy
It is not permitted to post your lab assignment solution source code or lab report on the
internet freely for public to access. For example, it is not acceptable to host a public repository on GitHub that contains your lab assignment solutions. A warning with instructions
to take the lab assignment solutions off the internet will be sent out upon the first offence.
If no action is taken from the offender within twenty-four hours, then a lab grade zero will
automatically be assigned to the offender.

Seeking Help Outside Scheduled Lab Hours
• Discussion Forum. We recommend students to use the Learn discussion forum to
ask the teaching team questions instead of sending individual emails to lab teaching
staff. For questions related to lab projects, our target response time is one business
day before the deadline of the particular lab in question. 5 . After the deadline, there
is no guarantee on the response time.
• Office Hours. The Learn system calendar gives the office hour details.
• Appointments. Students can also make appointments with lab teaching staff should
their problems are not resolved by discussion forum or during office hours. The
appointment booking is by email.
To make the appointment efficient and effective, when requesting an appointment,
please specify three preferred times and roughly how long the appointment needs to
5

Our past experiences show that the number of questions spike when deadline is close. The teaching
staff will not be able to guarantee one business day response time when workload is above average, though
we always try our best to provide timely response.

4

be. On average, an appointment is fifteen minutes per project group. Please also
summarize the main questions to be asked in your appointment requesting email. If
a question requires teaching staff to look at a code fragment, please bring a laptop
with necessary development software installed.
Please note that teaching staff will not debug student’s program for the student. Debugging is part of the exercise of finishing a programming assignment. Teaching staff
will be able to demonstrate how to use the debugger and provide case specific debugging tips. Teaching staff will not give direct solution to a lab assignment. Guidances
and hints will be provided to help students to find the solution by themselves.

Lab Facility After Hour Access Policy
After hour access to the lab will be given to the class when we start to use the Keil boards
in lab. However please be advised that the after hour access is a privilege. Students
are required to keep the lab equipment and furniture in good conditions to maintain this
privilege.
No food or drink is allowed in the lab (water is permitted). Please be informed that you
may share the lab with other classes. When resources become too tight, certain cooperation
is required such as taking turns to use the stations in the lab.

5

Part II
Lab Projects

6

Lab 1
Introduction to ARM RL-RTX
Kernel and Application
Programming
1.1

Objective

This Lab is to introduce the Keil µVision4 IDE and ARM RL-RTX development. Students will build the ARM RL-RTX from source. After this lab, students will have a good
understanding of the following:
• How to create a µVision RTX project;
• How to build an RL-RTX library from source;
• How to create an application with self-built RTX Library;
• How to use SVC as the gateway to program OS functions.

1.2

Starter Files

In https://github.com/yqh/ECE254 GitHub repository, the lab1/starter/ directory
contains the following:
• Startup/: frequently used source code; and
• HelloWorld/: a µVision project that prints “Hello World!” to UART0.
7

1.3

Pre-lab Preparation

• Read Part III Chapters 1 and Section 2.5.

1.4

Warm-up Exercises

Change to the directory where you have the ece254 lab repository. Use ”git pull” command to fetch any newly updated files from https://github.com/yqh/ECE254.
Notes
• The Keil IDE does not tolerate space(s) in the path name on Nexus. For example,
a project in a directory which contains My Documents as part of the path name
sometimes will give you error saying certain files could not be created.

1.4.1

Build and Run the HelloWorld Application

This exercise is to familiarize you with the simulator target and in-memory execution target
executions. When you open the provided HellowWorld project, you will see the following
two targets as shown in Figure 1.1.

Figure 1.1: Keil IDE: List of Targets

• The HelloWorld SIM target is to use simulator to debug the target with the default
memory map.
• The HelloWorld RAM target is to use the hardware ULINK/ME Cortex debugger to
debug the target. The memory map is reconfigured (see 1.2.2) to relocate everything
to RAM and a debugger initialization file (see 1.1) is required.

8

Figure 1.2: Keil IDE: Target Option Button
Execution of the HelloWorld SIM Target
Open the HelloWorld application and follow the steps listed below.
1. Click the Target Option button (see figure 1.2) to verify the following:
• the default memory map is used (see figure1.3); and

Figure 1.3: Keil IDE: Target Default Memory Map Configuration
• the simulator is used (see figure 1.4).
2. Build the HelloWorld SIM Target by pressing the Build button or F7 (see Figure
1.5).
3. Click the Start/Stop Debug Session button or press Ctrl-F5 to enter the simulator
(see Figure 1.6).
9

Figure 1.4: Keil IDE: Simulator Debugger Configuration

Figure 1.5: Keil IDE: Build Button

Figure 1.6: Keil IDE: Start/Stop Debug Session Button

Figure 1.7: Keil IDE: Evaluation Mode Code Size Warning Dialog Box
4. Click OK when you are prompt with the code size limit of 32K in evaluation mode
warning dialogue box (see Figure 1.7).
5. Choose UART#1 from the Serial Windows button (see Figure 1.8). You will see the
UART#1 window shown up at the bottom right part of the screen (see Figure 1.9).
6. Click the Run button or press F5 key to execute the program under simulator (see
10

Figure 1.8: Keil IDE: Enable UART Window View

Figure 1.9: Keil IDE: UART Window View and Run Button
Figure 1.9). You will see ”Hello World!” appearing in the UART#1 window in the
simulator.
7. Click the Start/Stop Debug Session button or press Ctrl-F5 to exit from the simulator
(see Figure 1.6).

11

Execution of the HelloWorld RAM Target
Open the HelloWorld application and follow the steps listed below.
1. Click the Target Option button (see figure 1.2) to verify the following:
• the in-memory exectuion memory map is used (see figure1.10); and

Figure 1.10: Keil IDE: Target In-Memory Execution Memory Map Configuration
• the ULINK2/ME Cortex Debugger is selected with a valid initialization file (see
figure 1.11).

Figure 1.11: Keil IDE: ULINK2/ME Cortex Debugger Configuration
2. Build the HelloWorld RAM Target by pressing the Build button or F7 (see Figure
1.5).
3. Start a PuTTY terminal and make sure you have the correct configuration as shown
in Figures 1.12(a) and 1.12(b).
12

(a) PuTTY Session for Serial Port Communication

(b) PuTTY Serial Port Configuration

4. Click the Start/Stop Debug Session button or press Ctrl-F5 to enter a debug session
(see Figure 1.6). This target uses the hardware ULINK2/ME Cortex Debugger. Note
you should not click the Download button to load the code to the board. When you
start a debug session, the debugger initialization file (i.e. RAM.ini) will execute the
LOAD command to load the code to the board.
5. Click OK when you are prompt with the code size limit of 32K in evaluation mode
warning dialogue box (see Figure 1.7).
6. Click the Run button or press F5 key to execute the program on the board and you
should see “Hello World!” displayed on PuTTY. Note that nothing will appear in
the UART#1 window because we are not using the simulator to debug.
7. Click the Start/Stop Debug Session button or press Ctrl-F5 to exit from the debug
session (see Figure 1.6).

1.5

Real-time Executive Exercises

The Keil MDK-ARM contains the RealView Real-Time Library (RL-ARM), which has
a Real-time Executive (RTX) named RL-RTX. The RTX kernel is a real time operating
system (RTOS) that enables one to create applications that simultaneously perform multiple functions or tasks (statically created processes). Tasks can be assigned execution
priorities. The RTX kernel uses the execution priorities to select the next task to run

13

(preemptive scheduling). It provides additional functions for inter-task communication,
memory management and peripheral management.
RTX programs are written using standard C constructs and compiled with the RealView
Compiler. The RTL.h header file defines the RTX functions and macros that allow you to
easily declare tasks and access all RTOS features.

1.5.1

Manual of RL-RTX

The manual of the RL-RTX is accessible through the Keil MDK-ARM Help (see Figure
1.12). The highlighted sections are highly recommended to read through.

Figure 1.12: Keil IDE: RL-RTX Manual

1.5.2

Creating a Real-time Executive Application

Overview
Our goal is to create an application that simultaneously run two tasks which output to
UART. The first task displays the value of a loop variable i every one second. The second
task displays “Task2: Hellow World!” every three seconds. By using the task management
14

and time management functions provided by RL-RTX, we could create such an application
very easily.
Listing 1.1 shows the source code of the modified helloworld.c which now calls RTX
API functions. Two tasks are defined. A task is a function whose prototype starts with
the keyword __task. A task function normally never terminates. If a task function needs
to terminate, then os_tsk_delete_self() is required to be called. Otherwise undefined
behaviour will happen and will cost lots of your time to debug without any clue.
/**
* @file: helloworld.c
* @brief: Two simple tasks running pseduo-parallelly
*/
#include
#include
#include
#include




"uart_polling.h"

__task void task1()
{
unsigned int i = 0;
for(;; i++)
{
printf("Task1: %d\n", i);
os_dly_wait(100);
}
}
__task void task2()
{
while(1)
{
printf("Task2: HelloWorld!\n");
os_dly_wait(300);
}
}
__task void init(void)
{
15

os_tsk_create(task1, 1); // task1 at priority 1
os_tsk_create(task2, 1); // task2 at priority 1
os_tsk_delete_self(); // must delete itself before exiting
}
int main ()
{
SystemInit();
uart0_init();
os_sys_init(init);
}
Listing 1.1: RTX HelloWorld C Source Code

Detailed Steps
To create an RL-RTX application for MCB1700 boards, first you need to follow the same
steps as you create a regular µVision application. We already have a HelloWorld application from the warm-up exercises. To modify this already created application into
an RL-RTX application, replace the helloworld.c with the modified one as shown in
Listing1.1, which use some RL-RTX API functions.
Next you will need to do three extra steps to make the application an RTX application
and the steps are:
1. To include the RTX Library header file RTL.h in the source code.
All the RTX kernel APIs are listed in RTL.h file which is by default located at
ARM\RV31\INC\ under the Keil software installation directory. You will need to include this file in your C file before you can call any RL-RTX API functions. Adding
the following line in your source code helloworld.c after the #include 
line:
#include 

2. To tell the linker to link with the RTX library.
The RL-RTX kernel comes in the form of a pre-compiled library, which by default is
located at ARM\RV31\LIB\ under the Keil software installation directory. In order to
16

use the functions in this library, one needs to link the application with RTX kernel
library. This is achieved by specifying “RTX kernel” under Operating system in the
target option setting (see Figure 1.13).

Figure 1.13: Keil IDE: Using RTX Kernel
3. To configure the RTX kernel by modifying source code of RTX_Conf_CM.c.
Figure 1.14 is the HelloWorld RTX project setup. Comparing it with the non-RTX
µVision HelloWorld application used in the warm-up exercises, RTX_Conf_CM.c and
Retarget.c1 are two new files added to the project.

Figure 1.14: Keil IDE: RTX HelloWorld Project Files
The RTX_Conf_CM.c is for RTX kernel configuration such as how big the stack a task
needs, how long the time slice should be and what is the CPU frequency et. al..
The configuration is at source code level through the Configuration Wizard in the
µVision editing window (see Figure 1.15). Special attention should be paid to the
following setting to avoid hard to debug problems:
1

These two files are located in the lab1 starter/Startup directory on GitHub. You should copy these
two files to the HelloWorld/src directory where the source code files of the project are located and then
add the copied files to the project group. Directly adding these two files to the project group from the
starter/Startup folder is not a good programming practice.

17

• Do remember to configure the CPU speed to 100 MHZ through the RTX_Conf_CM.c
configuration wizard. Otherwise you will get wrong timing result.
• The default stack size is 200 bytes. Using printf() family functions such as
sprintf() can easily cause a stack overflow. Normally 512B would be sufficient.
If you are not tight on memory, 1 KB is safer.
• The number of concurrent running tasks is the maximum active tasks the application is allowed to have. If the code has more active tasks than this number,
your application will run into undefined hard to debug behaviour.

Figure 1.15: Configuring RTX Kernel
The Retarget.c is not RTX application specific. This file implements the low-level
I/O functions that higher level I/O functions in C library such as printf() needs.
With this file, your RTX task can call C library functions such as printf() and the
output will appear in UART0.
Build the project and execute it both inside the simulator and on the actual board.
Note that the simulator runs the application a lot slower than the actual board. One wall
clock second is about one minute inside the simulator.

1.5.3

Building an RL-RTX Library for Cortex-M3

The ARM RL-RTX kernel comes with source code. You can modify the source code and
build your own modified ARM RL-RTX Library. The simplest way to do this is to make a
18

copy of the existing RTX library project for Cortex-M processors and then start to make
modifications. Here are detailed steps:
1. Create a folder to hold your RTX Cortex-M3 library project. Let’s name the folder
RTX_CM3.
2. Go to ARM\RL\RTX under the Keil software installation directory and copy the following items to your RTX Cortex-M3 library project folder of RTX_CM3. Note you
need to preserve the original directory structure when you make the copy of all the
files listed below. Note that the CM directory needs to be put under SRC directory.
• RTX_Lib_CM.uvopt
• RTX_Lib_CM.uvproj
• SRC\CM

Figure 1.16: RTX Library Source Files for Cortex-M3
3. Start working on your own copy of the RTX library from now on. Remove HAL_CM1.c
and HAL_CM4.c files that are under RTX_CM3\SRC\CM directory (see figure 1.16). These
two files are for Cortex-M1 and Cortex-M4 processors which is not the hardware in
the lab.
19

Figure 1.17: RTX Library Project Components for Cortex-M3
4. Open RTX_Lib_CM.uvproj under your RTX_CM3 directory that you newly created.
Right click the CM3_LE target under the project window to bring up the menu and
click “Manage Components”. You will only need the CM3_LE target, which supports
NXP1768 on MCB1700 boards. Remove all other targets in the Project Targets
window. You also need to remove the HAL_CM1.c and HAL_CM4.c files in the Files
window (see Figure 1.17).
5. Click the Target Option button and activate the C/C++ tab. Set the optimization to
Level 0 (-O0) in the Language / Code Generation section (see Figure 1.18). Turning
off the compiler optimization will allow the debugger to provide useful debugging
information during the development.
6. Click the Build button to build the library. You will notice a .lib file is created under
CM3_LE folder and this is the RTX library for Cortex-M3 processor you just built.
The RTX library cannot be executed. You will need to create an RTX Application
which calls some of the functions inside the RTX library file in order to see the effect
of the library. We will further discuss how to do this in Section 1.5.5.

20

Figure 1.18: RTX Library Project Optimization Level Setting

1.5.4

Creating a Multi-project Workspace

We now have two RTX related projects. One is the RTX HelloWorld application that
uses the RL-RTX built by ARM. The second is the self built RTX library for Cortex-M3
processors. We would like to create a workspace so that we can work on these two projects
in the same IDE window. The steps are as follows.
1. Put the HelloWorld RTX application and the self-built RTX library for Cortex-M3
project in the same folder.
2. Click Project → New Multi-Project Workspace (see Figure 1.19).

Figure 1.19: Keil IDE: Create New Multi-Project Worksapce
A new window appears to ask you to give a name for the multi-project workspace and
let’s call it helloworld_rtxlib.uvmpw (see Figure 1.20) and press the Save button.
21

Figure 1.20: Keil IDE: Naming a New Multi-Project Worksapce
3. A new dialogue box will pop up to ask you add µVision projects into the workspace.
Click the New button to start adding a project and click the Browse button to select
a project file (see Figure 1.21).

Figure 1.21: Keil IDE: Adding a µVision Project into Worksapce
Add the RTX_CM_Lib.uvproj to the workspace first(see Figure 1.22). Similarly, add
the RTX_HelloWorld.uvproj to the workspace. Click the OK button to finish adding
projects into the workspace.
4. Two projects appear under the Project Window (see Figure 1.23. Click the Batch
Build button (see Figure 1.23 to bring up the Batch Build window.
Select all the targets you want to build in a batch (see Figure 1.24). By setting up
batch build, multiple targets can be built by a single click of the Build button inside
the batch build dialogue box2 .
2
Whenever you make a change in the RTX Library project, you need to rebuild the library and the
application that uses the library. So batch build will make multiple builds easy to carry out.

22

Figure 1.22: Keil IDE: Adding RTX CM Lib.uvproj into Worksapce

Figure 1.23: Keil IDE: Workspace with Two Projects

1.5.5

Making an RTX Application with a Self-built RTX Library

Having finished the workspace setup, we now start to modify the RTX HelloWorld application so that it links with the self-built RTX Library instead of the pre-built RTX library
provided by ARM. The steps are as follows.
1. Activate the RTX_HelloWorld project by highlighting the project name and right
click. Then click the Set as Active Project (see Figure 1.25).
2. Remove the pre-built RTX library by ARM from the target option setting (see Figure
1.26).
23

Figure 1.24: Keil IDE: Batch Build

Figure 1.25: Keil IDE: Set an Active Project
3. Add the RTX library we built (i.e. RTX_CM3.lib) to the project (see Figure 1.27).
Build and download the updated HelloWorld RTX application. Verify it works both under
simulator and on the board.

24

Figure 1.26: Keil IDE: Removing Linkage with Stocked RTX Library

Figure 1.27: Keil IDE: Adding Your Own RTX Library

1.6
1.6.1

Assignment
Questions

The RTX_lib.c is located at ARM\RV31\INC under the default Keil installation directory3 .
The variable os_active_TCB is defined in this file and mainly used in rt_Task.c file.
The rt_TypeDef.h has the kernel data structure definitions. Read rt_TypeDef.h and
rt_Task.c files and answer the following questions.
1. Which global variable has the os_active_TCB array length information?
2. What does os_active_TCB array in RTX_lib.c contain?
3. Which global variable has the os idle task TCB information?
4. Is the TCB of the os idle task an element in the os_active_TCB?
3

The default installation directory is C:\Software\Keil on ECE Nexus PCs.

25

1.6.2

Programming Project Description

The requirements of the programming project is described in this section. We will provide
more instructions on how to set up a template of the programming project in Section 1.6.3
1. Add a function to RL-RTX API to return the number of active tasks in the system.
int os_tsk_count_get (void);

A task is considered active when its state is not set to INACTIVE in the TCB. Note
that the os idle task is a valid task that you should check the state as well. You need
to use the SVC as the gateway to access the kernel data structure.
2. Write five simple tasks. One of the tasks calls the os_tsk_count_get function to
test the number of active tasks in the system returned by the function.

1.6.3

Adding a New Function to the RTX Library

The self-built RTX library for Cortex-M3 for now provides the same functionality as the
pre-built RTX Library provided by ARM for Cortex-M3 processors. In this lab programming project, you are asked to add one new function to the RTX Library. This means you
need to modify the source code of the RTX library project.
The default RTL.h file is the RTX user interface. When a new RTX API function is
added to the RTX, the corresponding user level interface of the function needs to be added
to this file. However to make the newly added function easy to view, we instead will create
a new RTL_ext.h file which contains the user level interface of the newly added functions.
A good place to put this new header file will be in the directory where the RTX_CM3 library
project is resided. Let’s create a directory and name it INC. Inside this directory, create a
file named RTL_ext.h. Listing 1.2 gives the user interface of the required new function.
Then we need to create the corresponding kernel functions which does the actual work
of calculating how many tasks are not in the INACTIVE state. Since this function is
related to task management, it is natural to add the kernel function rt_tsk_count_get()
implementation in rt_Task.h and rt_Task.c files. However, for the same reason for easy
to view the changes we will make to the kernel, we will create two new kernel source code
files in the RTX_CM3\SRC\CM directory and name them as rt_Task_ext.h (see Listing 1.3
) and rt_Task_ext.c (see Listing 1.4). The rt_Task_ext.c should be added to the RTX
library project under the kernel group.

26

/**
* @file: RTL_ext.h
*/
#ifndef __RTL_EXT_H__
#define __RTL_EXT_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned int U32;
#if !(__TARGET_ARCH_6S_M || __TARGET_ARCH_7_M || __TARGET_ARCH_7E_M)
/*--------------------------------------------------------------------*
Functions ARM
*-------------------------------------------------------------------*/
#else
/*--------------------------------------------------------------------*
Functions Cortex-M
*-------------------------------------------------------------------*/
#define __SVC_0
__svc_indirect(0)
/* ECE254 Comment: added for lab2 */
extern int rt_tsk_count_get(void);
#define os_tsk_count_get() _os_tsk_get((U32)rt_tsk_count_get)
extern int _os_tsk_get (U32 p) __SVC_0;
#ifdef __cplusplus
}
#endif
#endif
#endif
/* end of file */
Listing 1.2: The RTL ext.h C Source Code
/**
* @file: rt_Task_ext.h
*/
extern int rt_tsk_count_get (void);
/* end of file */
Listing 1.3: The rt Task ext.h C Source Code

27

/**
* @file: rt_Task_ext.c
*/
#include "rt_TypeDef.h"
#include "RTX_Config.h"
#include "rt_System.h"
#include "rt_Task.h"
#include "rt_List.h"
#include "rt_MemBox.h"
#include "rt_Robin.h"
#include "rt_HAL_CM.h"
#include "rt_Task_ext.h"
int rt_tsk_count_get (void) {
/* add your own code here */
/* change the following line to return the number of active tasks */
return 0;
}
/* end of file */
Listing 1.4: The rt Task ext.c Template C Source Code

1.6.4

Using the Newly Created RTX Function

To use the newly created function, you will need to include the newly created RTL_ext.h
header file in the C source code. Then a task can call this function to obtain the number
of tasks that are not in INACTIVE state. The following is an example code excerpt.
#include "../../RTX_CM3/INC/RTL_ext.h"
//...
__task void task1()
{
int num;
while (1) {
// ...
num = os_tsk_count_get();
printf("number of tasks: %d.\n", num);
// ...
}
}

28

Points
3
2

3
2

Description
Answers to lab assignment questions
Building a multi-project workspace that contains
a self-built RTX and an application that links with
this RTX
Implementation of os_tsk_count_get
Implementation of testing tasks to test
os_tsk_count_get()
Table 1.1: Lab1 Marking Rubric

1.7
1.7.1

Deliverables
Pre-lab Deliverables

There is no pre-lab deliverable.

1.7.2

Post-lab Deliverables

Submit a compressed archive file that contains the following:
1. Answers to questions in Section 1.6.1.
2. The entire folder that contains the multi-project workspace where the modified RTX
library project and the RTX application project source code to solve the programming
project (see Section 1.6.2) are located. Include a README (any useful instructions
for evaluation TA) in the folder.
Name the file lab1_Gid.zip, where id is your two digit Group ID, and submit it to
Lab1 Dropbox in Learn.

1.8

Marking Rubric

Table 1.1 shows the rubric for marking the lab.

29

Lab 2
Task Management in ARM RL-RTX
2.1

Objective

This lab is to learn about, and gain practical experience in ARM RL-RTX kernel programming. In particular, you will add three functions to ARM RL-RTX library. After this lab,
students will have a good understanding of:
• how to program an RTX function to read kernel task control block related data
structure; and
• how to block and unblock a task by using context switching related kernel functions.

2.2

Starter files

The lab2/starter directory in the ECE254 GitHub repository contains a template lab2
project, which is a multi-project workspace that contains one RTX library project and
one RTX application that uses the RTX library built by the first RTX library project. In
the RTX Library project there two files modified from the original ARM RL-RTX source
code so that the ret_val in TCB is changed to U32 and the corresponding assembly code
affected due to this changed is modified accordingly. It is very important that you use
these modified files rather than the original ones from ARM. These two files are
• the modified HAL_CM3.c; and
• the modified rt_TypeDef.h.
In the RTX application project, you are provided with the following code :
30

• main_task_exp.c
which has the subroutine to map a function pointer address to the function name.

2.3

Pre-lab Preparation

None

2.4

Assignment

This lab requires students to read the existing ARM RL-RTX source code and then use the
relevant kernel routines to accomplish a programming project (see Section 2.4.1). Reading source code and answer the questions in Section 2.4.1 will help you to complete the
programming project.

2.4.1

Questions

Questions 1-4 are to help you finish Part A. Questions 5-10 are to help you finish Part B.
Please solve questions before you proceed with coding.
1. Read rt_get_TID() code in rt_Task.c file. Assume that a non-idle task has a task
ID of n, what is the index of this task’s TCB in the os_active_TCB array?
2. Read the rt_TypeDef.h file and answer the following questions.
• What is the purpose of the p_lnk variable in the struct OS_TCB?
• What is the purpose of tsk_stack and stack variables in struct OS_TCB?
• If you have a variable with type of struct OS_XCB *, will casting this variable
to struct OS_TCB * keep the p_lnk field?
3. Read the rt_Task.c and RTX_lib.c files and answer the following question.
• What is the purpose of variables mp_tcb and mp_stk?
You may want to search the entire source code to further explore how these variables
are set and used (see Figure 2.1).
4. Read Section C.2.3 and study rt_init_stack() and rt_get_PSP() functions source
code in HAL_CM3.c file and answer the following questions.
31

Figure 2.1: Keil IDE: Search
• Which registers are saved on the task stack?
• Which bits of which global variable contain the default task stack size in bytes?
• How to determine the memory address of the first item that is pushed onto a
task stack?
• For a task that is not in RUNNING state, how to determine the memory address
of the last item that is pushed onto its task stack (i.e. the top of the stack)?
• For a task that is in RUNNING state, how to determine the memory address of
the last item that is pushed onto its task stack (i.e. the top of the stack)?
5. Read the RTX Library Reference in the Keil IDE Help (see 1.12) and answer the
following questions:
• In an RTX application, how to declare a memory pool named "mympool" that
has 20 blocks of memory with block size of 12 bytes?
• Write one line of code to initialize the "mympool".
• What is the corresponding kernel function of _alloc_box()?
• What is the corresponding kernel function of _free_box()?
6. Read the rt_TypeDef.h file. What is the purpose of ret_val in the struct OS_TCB?
32

7. Read the rt_List.c file and answer the following questions:
• What does the rt_put_prio() function do?
• What does the rt_get_first() function do?
You may want to further explore other functions in the rt_List.c file.
8. Read the rt_Task.c file and answer the following questions:
• What does the rt_block() function do?
• What does the rt_dispatch() function do?
9. How to set the return value of a function becomes a bit tricky when context switching
is involved. One such example is os_mbx_wait() function. This function will return
OS_R_MBX if the task has waited until a message was put in the mailbox (i.e. the task
was blocked to wait for a message to arrive and then unblocked when the message
arrives). Read the rt_Mailbox.c file and find the lines where the return value
of OS_R_MBX is set. Why the corresponding kernel function rt_mbx_wait() does
not have a line to set the return value to OS_R_MBX? You may skip the code in
functions isr_mbx_receive() and rt_mbx_psh() for the purpose of completing this
assignment.
10. To block a task, you will need to create a queue that the blocked tasks can stay.
There are two global queues for now in the kernel and they are os_rdy and os_dly.
What data structure do these two queues use?

2.4.2

Programming Project

Part A
To get familiar with kernel source code, a good start is to write a function to retrieve
a kernel data structure. In this assignment, you are to implement a primitive to obtain
the task status information from the RTX at runtime given the task id. The function
description is as follows.
• OS_RESULT os_tsk_get (OS_TID task_id, RL_TASK_INFO *buffer)

The primitive returns information about a task. The system call returns a rl_task_info
structure , which contains the following fields:

33

typedef struct rl_task_info {
U8 state;
/* Task state
*/
U8 prio;
/* Execution priority
*/
U8 task_id;
/* Task ID value for optimized TCB access */
U8 stack_usage; /* Stack usage percent value. eg.=58 if 58% */
void (*ptask)(); /* Task entry address
*/
} RL_TASK_INFO;

The state field describes the state of this task and is one of:
INACTIVE
Tasks which have not been started or tasks which have been deleted are in
INACTIVE state.
READY
Tasks which are ready to run are in the READY state.
RUNNING
The task that is currently running is in the RUNNING state. Only one task at a
time can be in this state.
WAIT_DLY
Tasks which are waiting for a delay to expire are in the WAIT_DLY state. The
task is switched to the READY state once the delay has expired.
WAIT_SEM
Tasks which are waiting for a semaphore are in the WAIT_SEM state. When the
token is obtained from the semaphore, the task is switched to the READY state.
WAIT_MUT
Tasks which are waiting for a free mutex are in the WAIT_MUT state. When a
mutex is released, the task acquires the mutex and switches to the READY state.
WAIT_MBX

34

Tasks which are waiting for a mailbox message are in the WAIT_MBX state. Once
the message has arrived, the task is switched to the READY state. Tasks waiting
to send a message when the mailbox is full are also put into the WAIT_MBX state.
When the message is read out from the mailbox, the task is switched to the
READY state.
WAIT_MEM
Tasks which are waiting for memory are in the WAIT_MEM state. Once the memory is available, the task is switched to READY state. The os_mem_alloc()
function is used to place a task in WAIT_MEM state.
These states are described in details in the RL-ARM Real-Time Library User’s Guide
→ Theory of Operation→ Task Management section except for WAIT_MEM state. Read
Part B regarding how tasks are blocked upon calling os_mem_alloc() function.
The prio field describes the priority of this task.
The task_id field describes the id of task assigned by the OS.
The stack_usage describes how much stack space is used by this task. The value is
the percent value. For example, if 58% of this task stack is used, stack_usage is set
to 58.
The ptask field describes the entry address of this task function.
The function returns OS_R_OK on success and OS_R_NOK otherwise.
Part B
You are to write two memory management RTX functions to manage a memory pool
for tasks. You may assume there is only one user-defined memory pool in the system.
The memory pool is declared by _declare_box() and further initialized by _init_box(),
which are existing RTX functions.
The function you are going to add are described as follows.
• void *os_mem_alloc (void *box_mem)

The primitive allocates a fixed-size of memory to the calling task from the memory
pool pointed by box_mem and returns a pointer to the allocated memory. When there
is not enough memory available, the calling task is blocked until enough memory
35

becomes available. If several tasks are waiting for memory and memory becomes
available, the highest priority waiting task will get the memory. Within the same
priority waiting tasks, the one that waits longest will get the memory.
• OS_RESULT os_mem_free (void *box_mem, void *box)

The primitive returns a memory block whose address is box to memory pool with
starting address of box_mem. Note the box is allocated by os_mem_alloc from the
meory pool of box_mem. It returns OS_R_OK on success and OS_R_NOK otherwise.
If several tasks are waiting for memory and memory becomes available, the highest
priority waiting task will get the memory and be unblocked. Preemption may happen
if the unblocked task has higher priority than the task that calls this function to free
up the memory.
Create a set of testing tasks to demonstrate that you have successfully implemented
the required functions in Parts A and B. Your test tasks should do the following tests.
• A task periodically prints task status of each task in the system.
• A task can allocate a fixed size of memory.
• A task will get blocked if there is no memory available when os_mem_alloc() is
called.
• A blocked on memory task will be resumed once enough memory is available in the
system.
• Create a testing scenario that multiple tasks with different priorities are all blocked
waiting for memory. When memory becomes available, test whether it is the highest
priority task that waits the longest that gets the memory first.

2.4.3

Source Code File Organization Convention

To make the changes you have made to the RTX library obvious to the grading teaching
assistant. We would like to impose the following source code file organization convention.
• The newly added RTX function user interface is in RTL_ext.h file.
• The corresponding kernel function of an os_xxx() function should be named as
rt_xxx() in the kernel space.
36

• All extended task management related kernel functions are in rt_Task_ext.[ch]
files.
• All extended memory management related kernel functions are in rt_MemBox_ext.[ch]
files.
• When you want to add extra code to an existing rt_xxx.[ch] file, create new
rt_xxx_ext.[ch] files instead and put the code in these new files.
• For any miscellaneous source code that you cannot figure out where to put, create
rt_misc_ext.[ch] files and put the code in these files.
• You should try to avoid changing existing RTX library source code. If you are not
able to do so, then write detailed comments in the existing source code where you
make the changes and what the changes are. The comments should look like the
following:
/*
/*
//
/*

2.4.4

Start: ECE254 Lab2 Changes by GroupID */
Detailed comments about the changes ...*/
Changed source code appear here
End: ECE254 Lab2 Changes by GroupID */

Third-party Testing

We will provide a third-party testing RTX application to test the implementation of the
newly added os_* functions in the RTX library. The third-party testing application make
application level function calls to the RTX library you will build. Your own testing application will not be tested by the third-party testing.

2.5
2.5.1

Deliverables
Pre-Lab Deliverables

None

37

2.5.2

Post-Lab Deliverables

Submit a compressed archive file that contains the following:
1. A lab2_QA.txt file which contains answer to questions in Section 2.4.1.
2. Your entire multi-project workspace to solve the programming project. Do not hard
code the absolute path of file names in your project files including but not limited to
source code, the RAM.ini file or the .uvproj files. Always use relative paths so that
the grading TA will be able to compile your project.
3. A test description file to describe what each testing task does. Name the file Test_spec.txt.
This file is added to the RTX application project as shown in Figure 2.2.

Figure 2.2: Keil IDE: Location of Test Specification File.
4. A README file to describe what you have submitted and how to build and run your
project(s).
Name the file lab2_Gid.zip, where id is your two digit Group ID, and submit it to Lab3
Dropbox in Learn.

2.6

Marking Rubric

Table 2.1 shows the marking rubric for this lab.

38

Points
20
30

Sub-total

15
10
5
50
20
20
10

Description
Answers to questions
Part A Implementation
os_tsk_get function implementation
self-implemented testing cases to test Part A API
function
Third-party testing case result
Part B Implementation
os_mem_alloc and os_mem_free implementation
self-implemented testing cases to test the correctness of Part B API functions
Third-party testing case result

Table 2.1: Lab2 Marking Rubric

39

Lab 3
Inter-task Communication and
Concurrency
3.1

Objective

This lab is to learn about, and gain practical experience in inter-task communication and
concurrency control in a general Linux environment. A task can be implemented as a
process or a thread. Tasks communicate by message passing or shared memory in this
lab project. For message passing, tasks do not share any memory. The operating system
message passing facility takes care of shared memory access within its kernel space. For
shared memory, tasks need to take care of the shared memory conflicting operations. The
operating system provides concurrency control facility such as semaphore and mutex library
calls.
The choice of implementing a task as a process or a thread and the inter-task communication by message passing or shared memory results in different performance. This
lab compares the performance of inter-process communication by message passing with
inter-thread communication by shared memory with concurrency control 1 by solving a
producer consumer problem.
After this lab, students will have a good understanding of, and ability to program with
• the fork() and exec() system calls, and their use for creating a new child process
on the Linux platform;
1

Sharing data among threads in a general Linux environment is straight forward because threads share
the same memory [6]. The challenging part of sharing memory is the concurrency control to avoid race
conditions.

40

• the wait() family system calls, and their use to obtain the status-change information
of a child process; and
• the POSIX message queue facility () on the Linux platform for interprocess communication.
• the pthread pthread_create() and pthread_join() to create and join threads; and
• the pthread sem_init(), sem_post() and sem_wait() library calls for inter-thread
communication concurrency control in a general Linux environment.

3.2

Starter Files

The starter file is on GitHub at http://github.com/yqh/ECE254/ under directory lab3/starter.
It contains one sub-directory:
• ALP/: sample code of fork() from [6];
• cmd_arg: sample code of capturing command line input arguments;
• gettimeofday/: sample code of using gettimeofday();
• mqueue/: sample code of using POSIX queue functions; and
• tools/: a shell script to compute statistics of timing data.

3.3

Pre-lab Preparation

1. Read Chapter 3.
2. Read Section 3.2.2 about fork() and exec() and Sections 3.4.1 and 3.4.2 about
wait() in [6].
3. Complete the example code of wait() on page 57 in [6] by including necessary header
files and compile it. Execute it to examine the difference of the output from the
output of the code in Listing 3.4.
4. Read the man page (section 7) of mq_overview
(http://linux.die.net/man/7/mq_overview).
5. Build and execute the sample code in the mqueue sub-directory from the lab3 starter
files.
41

6. Read Linux man page of semaphore overview.
man 7 sem_overview
7. Read Sections 4.1, 4.4 and 4.5 about thread programming in [6].
8. Read supplementary materials regarding POSIX pthread in [5]
(http://www.cs.cf.ac.uk/Dave/C/node29.html#SECTION002940000000000000000).

3.4

Assignment

Solve the producer-consumer problem with a bounded buffer in a general Linux environment.
This is a classic multi-tasking problem in which there are one or more tasks that create
data (these tasks are referred to as “producers”) and one or more tasks that use the data
(these tasks are referred to as “consumers”) 2 . We will have a system of P producers and
C consumers . Three experimental cases then exist:
• single producer/single consumer;
• single producer/multi-consumer; and
• multi-producer/single consumer.
The producer tasks will together generate a fixed number, N, integers in total. Each
producer will generate a set of integers, one at a time. Since there is more than one
producer, and we do not want the producers to have to coordinate their actions since that
would require additional inter-task communication, we will adopt the following approach:
each producer has an identity number, id, from 0 to P-1. The producer with identity
number id will produce the integers i such that i%P == id. For example, if there are
7 producers, producer number 3 will produce the integers 3, 10, 17, . . . . Each time a
new number is created, it is placed into a fixed-size buffer, size B integers, shared with the
consumer tasks. When there are B integers in the buffer, producers stop producing.
Each consumer is likewise given an integer identity, cid, from 0 to C-1. Each consumer
task reads the integer out of the buffer, one at a time, and calculates the square root of the
2

If it helps, you can think of the producer as a keyboard device driver and the consumer as the
application wishing to read keystrokes from the keyboard; in such a scenario the person typing at the
keyboard may enter more data than the consuming program wants, or conversely, the consuming program
may have to wait for the person to type in characters. This is, however, only one of many cases where
producer/consumer scenarios occur, so do not get too tied to this particular usage scenario.

42

integer. When the square root of the integer is itself an integer, the consumer prints out
its identity, the original integer taken from the buffer, and the value of the square root on
the terminal screen (in Linux). For example, if there are 6 consumers and consumer with
cid = 3 read the value 16 from the buffer, it will display 3 16 4.
Given that the buffer has a fixed size, B, and assuming that N > B, it is possible for the
producers to have produced enough integers that the buffer is filled before any consumer
has read any data. If this happens, the producer is blocked, and must wait till there is at
least one free spot in the buffer.
Similarly, it is possible for the consumers to read all of the data from the buffer, and
yet more data is expected from the producers. In such a case, the consumer is blocked,
and must wait for the producers to deposit one or more additional integers into the buffer.
Further, if any given producer or consumer is using the buffer, all other consumers and
producers must wait, pending that usage being finished. That is, all access to the buffer
represents a critical section, and must be protected as such.
The program terminates when the consumers have read all N characters from the producers and finish displaying all square roots that are integers. Note that there is a subtle
but complex issue to solve: there are multiple consumers that are reading from the buffer,
and thus a mechanism needs to be established to determine whether or not some consumer
has read the last integer.
Question: Is there a similar problem for the producers to deal with, or is their situation
simpler?

Requirements
Let N be the number of integers the producers should produce in total, B be the buffer size,
P be the number of producers and C be the number of consumers. The producer consumer
system is called with the execution command:
./produce   

The command will execute per the above description and will then print out the time it took to execute. You should measure the time before you create the first process/thread and the time after the last integer is consumed and consumers finish displaying all received integers with perfect square roots. On Linux, use gettimeofday() for time measurement and terminal screen for display. Thus your last line of output should be something like: System execution time: 43 For a set of given (N,B,P,C) tuple values, run your application and measure the time it takes. Note for a give value of (N,B,P,C), you need to run multiple times to compute the average execution time in a general Linux environment. Compare the performance of multi-process communication by message queue with multi-thread communication by shared memory. 1. Implement each producer/consumer task as a process. Use message queue as the bounded buffer for inter-task communications. You start your program with one process which then forks multiple producer processes and multiple consumer processes. Note that shared memory access is taken care of by the operating system message passing facility. However, kernel memory is finite, and thus there cannot be an unbounded number of messages outstanding; at some point the producer must stop generating messages and the consumer must consume them, otherwise the kernel’s memory will be completely consumed with messages, blocking the sender from further progress. What is needed, therefore, is to set up the correct queue size. When the queue is full, the producer is blocked by the system and cannot continue to send messages until a message is consumed. 2. Create a process with a fixed buffer size in which producers and consumers are threads within the process and the buffer is a shared global data structure such as a circular queue that all threads share access to. Note that shared memory access needs to be taken care of at the application level. The POSIX thread semaphore and mutex are to be used for concurrency control. A Sample Program Run [ecelinux1:]./produce 20 2 1 3 0 0 0 2 1 1 2 4 2 2 9 3 1 16 4 System execution time: 0.000642 seconds Note that due to concurrency, your output may not be exactly the same as the sample output above. Also depending on the implementation details and the platform where the program runs, the sample system execution time is only for illustration purpose. The exact system execution time value your program produces will be different than the one shown in the sample run. 44 3.5 3.5.1 Deliverables Pre-lab Deliverables There is no pre-lab deliverable for this lab. 3.5.2 Post-lab Deliverables Submit a compressed archive file that contains the following: 1. A directory named src, where it contains the source code and a README which gives build instructions. 2. A lab report named lab3_rpt.pdf which contains the following items. • Timing analysis for Linux environment – Two tables that show the average timing measurement data for the (N,B,P,C) values shown in Table 3.1 for a general Linux environment. One table is for the timing result by the approach of inter-process communication with message queue. Another table is for the timing result by the approach of inter-thread communication with shared memory. Note that for each row in the table, you need to run the program X (We recommend the value of X to be 500.) times and compute the average time. – Given (N,B,P,C) = (398, 8, 1, 3), run your program X times (i.e. X=500) on Linux platform. Present the average timing measurement data and its standard deviation. – Compare the timing results of multi-thread with shared memory and multiprocess with message queue. Discuss the advantages and disadvantages of these two approaches to solve the same problem. • Add an appendix in the report which contains your source code of the producer and consumer well as any other routine that create these processes/tasks. Note that you are required to comment the code appropriately so that any other programmer can follow your algorithms and logic. 3.6 Report Marking Rubric The Rubric for marking the submitted source code and report is listed in Table 3.2. 45 N 100 100 100 100 100 100 100 100 100 100 398 398 398 398 398 B 4 4 4 4 4 8 8 8 8 8 8 8 8 8 8 P 1 1 1 2 3 1 1 1 2 3 1 1 1 2 3 C 1 2 3 1 1 1 2 3 1 1 1 2 3 1 1 Time Table 3.1: Timing measurement data table for given (N, B, P, C) values. Points Sub-Points I 20 5 15 Sub-Points II Description Source Code Code compilation Correct implementation of the algorithm and execution gives the expected results Report Timing measurement results of ecelinux Average timing measurement results Average system execution time mean and standard deviation results given (N,B,P,C) =(398,8,1,3). Discussion of the advantages and disadvantages of POSIX queue among processes and shared memory among threads 30 30 12 6 12 Table 3.2: Lab3 Marking Rubric 46 Lab 4 Memory Management 4.1 Objective This lab is to learn about and gain practical experience in memory allocation strategy implementation and to do some evaluation on the merits of these algorithms. You will implement two memory allocation algorithms: best fit and worst fit, and perform analysis to determine which is better. This lab is to be completed on a Linux platform. After this lab, students will have a good understanding of the strength and weaknesses of dynamic memory allocation algorithms and the ability to manage a limited resource (memory) in an efficient way. 4.2 Starter Files The starter file is on GitHub at http://github.com/yqh/ECE254/ under lab4/starter. It contains the following files: • main_test.c: a code template file for writing testing cases; • Makefile: a sample makefile; • mem.h: the memory management routine interface file; and • mem.c: the source code template file to be completed by students. 4.3 Pre-lab Preparation • Review the algorithms of best fit and worst fit in lecture notes. 47 4.4 Assignment There is a programming project and a report which documents the data structure and algorithms of the programming project and contains analysis of the outcome of the two allocation algorithms with experimental data. 4.4.1 Programming Project You are to implement two memory allocation algorithms: best fit and worst fit. For each allocation algorithm, you will first implement a memory initialization function, which requests a chunk of free memory dynamically from the system to be managed by different allocation algorithms. Then you will implement an allocation function and a de-allocation function. One utility function will also be implemented to help analyze the efficiency of allocation algorithm and its implementation. Then, you will write a number of tests to test the correctness of your implementations. Finally, you will write an analysis on the strengths and weaknesses of the two different allocation algorithms. Description of Functions The specification of each function to be implemented are described below: • Memory initialization functions: NAME best_fit_memory_init, worst_fit_memory_init - Initialize dynamic memory to be managed SYNOPSIS #include "mem.h" int best_fit_memory_init(size_t size); int worst_fit_memory_init(size_t size); DESCRIPTION These functions initialize the system with a single block of memory. The name of the function indicates the allocation algorithm to be used. They will always initialize the system as if no memory has been allocated (i.e., all memory is free). The input parameter size is the memory size in bytes. 48 You are to use the malloc() function inside the initialization function to acquire a chunk of memory of size bytes and then initialize your own memory allocator data structure based on the memory allocation algorithm. Note that malloc() can only be used in the initialization function implementation once to request the memory to be managed by your allocator and de-allocator from the system. It should not be used in other places in the code. Instead, you need to implement your own version of allocator without the need of calling malloc() again. You are responsible for designing and implementing the structures used to track what memory is currently allocated and what is free. Any memory you use for this will be inside the block of memory that the allocation algorithms are responsible for managing. The space your structures are needed to maintain un-allocated (free) memory is defined as free space. There is a trade off to be made between space and speed. The more space your structures require, the less space is available for allocator to return to users. On the other hand, larger data structures may result in faster operations, if the data structures are efficient. Having different memory initialization functions for different memory allocation algorithm gives you the possibility of using different data structures for different memory allocating algorithms. This does not, however, imply you must use different data structures for different algorithms. It is possible your algorithms may use a common data structure. It is your decision. RETURN VALUE These functions return 0 on success and -1 on failure. An example cause of failure is the size value is too small. • Allocation functions: NAME best_fit_alloc, worst_fit_alloc - Allocate dynamic memory SYNOPSIS #include "mem.h" void *best_fit_alloc(size_t size); void *worst_fit_alloc(size_t size); DESCRIPTION These are two allocation functions. The name of the function indicates the 49 allocation algorithm to be used. 1 The input parameter size in each allocator is the number of bytes requested from the allocator. Each allocator then returns the starting address of a block of memory of the appropriate size. The memory address should be four bytes aligned. Memory requests may be of any size from one byte all the way up to the full size of memory given in the initialization function. For a request for N bytes, return a memory block of (N + δ) bytes, where δ = 0, 1, 2, 3, and (N +δ) is a multiple of four. Should δ > 0, the additional space is just internal fragmentation which may be ignored (and the user will not be told). If for some reason the memory request cannot be satisfied, such as there being insufficient available memory, a pointer to 0 or NULL should be returned. For example, a request of zero byte will return NULL. Recall that the space your structures are needed to maintain un-allocated (free) memory is defined as free space. One subtle point is that because the data structures for managing the memory is counted into the total size of memory initialized, the actual maximum size that is available to allocate to user is the size of memory initialized minus the space consumed by the memory management data structures. Assuming the system is initialized with N bytes, an allocation request of N bytes will return NULL, for the system requires some space, say X (X > 0) byte(s) to manage the memory. Similarly, assuming the biggest free memory block has a size of M bytes, then a request of M bytes may return NULL, since your data structure choice may require new space of X (X > 0) byte(s) to manage these M bytes. RETURN VALUE These functions return a pointer to the allocated memory or NULL if the request fails. • Deallocation functions: NAME best_fit_dealloc, worst_fit_dealloc - Free dynamic memory SYNOPSIS #include "mem.h" void best_fit_dealloc(void *ptr); void worst_fit_dealloc(void *ptr); 1 The best fit memory init() or worst fit memory init() function needs to be invoked before calling one of these two functions. 50 DESCRIPTION These are the de-allocation functions. The name of the function indicates the allocation algorithm used. The best_fit_dealloc() function takes the starting address of a block of memory returned by best_fit_alloc() and frees the block of memory. Similarly, the worst_fit_dealloc() function takes the starting address of a block of memory returned by worst_fit_alloc() and frees the block of memory. If the freed memory block is adjacent to other free memory blocks, it is merged with them immediately (i.e. immediate coalescence) and the combined block is then re-integrated into the memory under management. You are not required to clear the block (that is, fill the memory with zeros). If the input parameter of best_fit_dealloc() and worst_fit_dealloc() is not an address returned by best_fit_alloc() and worst_fit_alloc() respectively, the behaviour of the function is undefined and is left as a free engineering choice. Similarly, if a block is deallocated more than once, without having been allocated in the meantime, the behaviour of the function is also undefined. RETURN VALUE These functions return no value. • Utility functions: NAME best_fit_count_extfrag, worst_fit_count_extfrag - Count external fragmented memory blocks SYNOPSIS #include "mem.h" int best_fit_count_extfrag(size_t size); int worst_fit_count_extfrag(size_t size); DESCRIPTION These are utility functions that counts the number of free (i.e. unallocated) memory blocks in memory that are of a size less than the input value of size. The input value size is in bytes. The name of the function indicates the allocation algorithm used. Testing The lab does not require the memory allocator/de-allocator to be re-entrant. In order to test your implementation of the required functions, create a single task and let it allo51 cate and de-allocate memory in different ways and possibly multiple times. The provided main_test.c file is for writing testing code. Create a set of testing scenarios to test functions implemented and document the testing specification in the report (See 4.4.2). Implement the tests in main_test.c file. There is no hard requirement on what tests to be implemented. The rule of thumb is that the tests will convince yourself that your implementation is correct. However to provide more leads to testing ideas, you may want to consider repeatedly requesting and then releasing and make sure no extra memory appears or no memory gets lost. The sum of free memory and allocated memory should always be constant. Another test you want to include is about the external fragmentation. Allocating and de-allocating memory with different sizes and see how external fragmentation is affected. The utility functions best_fit_count_exfrag() and worst_fit_count_exfrag() are useful tools. 4.4.2 Report Create your own experiment to compare the two allocation algorithms from the external fragmentation point of view. Use your own experimental data to conduct an analysis and draw a conclusion. Write the following items in a report and name it report.pdf. • Statement of the problem to be discussed in the report; • Descriptions of the data structure and algorithms to implement the allocation algorithms; • Testing scenario description; • Experimental data to support comparison results; and • The comparison conclusion. To illustrate key algorithms, pseudocode is a good way to describe the algorithm from a high-level point of view. 4.4.3 Third-party Testing and Source Code File Organization We will write a third-party testing program to verify the correctness of your implementation of the functions. We will test best fit and worst fit separately. In order to do so, we will need to enforce certain source code file organization standard. 52 Download all the starter files and put them under a directory and name the directory as src. Do not change the file names. Do not make any changes of the contents of mem.h file. Do not change the existing function prototype in the given mem.c. You are allowed to add self-defined new functions to mem.c. You are also allowed to create new .h and .c files. The newly created .h file is allowed to be included in the mem.c file. However in order for us to test your implementation, we require you not to include any newly created header files in the main_test.c file. To include more default system defined header files (i.e. include files with angle brackets) is allowed. You should have one Makefile to build a final executable with name main_test.out. We expect that typing command make will generate the main_test.out. Note that the main_test.c calls the memory management functions you will implement. During testing, the main_test.c file will be replaced by a third-party testing code with the same file name. Using the provided template main_test.c under the starter folder to test your Makefile before submission. 4.5 4.5.1 Deliverable Pre-Lab Deliverables None. 4.5.2 Post-Lab Deliverables Create a compressed archive file that contains everything under the src directory and report.pdf. Name the compressed file lab4_Gid.zip, where id is your two digit Group ID, and submit to Learn Lab4 Dropbox. 4.6 Marking Rubric The Rubric for marking the submitted source code and report is listed in Table 4.1. The correctness of your implementation will be tested by a third-party testing program and a minimum 10 points will be deducted if we find memory is lost or extra memory appears after repeating allocation and de-allocation function calls. Your grade will be relative to the amount of error the third-party testing program has identified. 53 Points 35 Sub-Points 5 25 5 15 1 5 4 3 2 Description Source Code Code compilation Correct implementation of the algorithm and execution gives the expected results Student provided testing implementation Report Statement of the problem to be discussed in the report Description of data structures and algorithms Testing scenario description Experimental data for comparison results Conclusion Table 4.1: Lab4 Marking Rubric 54 Part III Development Environment Quick Reference Guide 55 Chapter 1 Keil Software Development Tools The Keil MDK-ARM development tools are used for MCB1700 boards in our lab. The tools include • µVision4 IDE which combines the project manager, source code editor and program debugger into one environment; • ARM compiler, assembler, linker and utilities; • Real-Time Library which includes an RTX Real-Time Kernel with source code; • ULINK USB-JTAG Adapter which allows you to debug the embedded programs running on the board. The MDK-Lite is the evaluation version and does not require a license. However it has a code size limit of 32KB, which is adequate for your course projects. The MDK-Lite version 4.60.0.0 1 is installed on all lab computers. If you want to install the software on your own computer, Appendix B gives detailed instruction. 1.1 Creating an Application in µVision4 IDE To get started with the Keil IDE, the MDK-ARM Primer http : //www.keil.com/support/man/docs/gsac/ 1 The latest version is 5.1.0.0. We have not fully tested the supplied sample code with this version. This manual is based on version 4.60.0.0. 56 is a good place to start. We will walk you through the IDE by developing a simple HelloWorld application which displays Hello World through the UART0 that is connected to the lab PC. Note the HelloWorld example uses polling rather than interrupt. 1.1.1 Create a New Project 1. Create a folder named HelloWorld on your computer 2 . Then create HelloWorld\src sub-folder. 2. Copy the following files to HelloWorld\src folder from lab1\starter\Startup folder: • startup_LPC17xx.s • system_LPC17xx.c • uart_polling.c • uart_polling.h 3. Create a new µVision project by click • Project → New µVision Project (See Figure 1.1) Figure 1.1: Keil IDE: Create a New Project • Choose NXP(Founded by Philips) → LPC1768 (See Figure 1.2(a) and Figure 1.2(b)) • Answer “No” when you are asked to copy the startup code (See Figure 1.3). 1.1.2 Managing Project Components You just finished creating a new project. One the left side of the IDE is the Project window and expand all objects, you will see the default project setup as shown in Figure 1.4. 2 The folder path name should not contain spaces on Nexus computers. 57 (a) Choose NXP (b) Choose LPC1768 Figure 1.2: Keil IDE: Choose MCU Figure 1.3: Keil IDE: Copy Startup Code Figure 1.4: Keil IDE: A default new project 1. Rename the Target The “Target 1” is the default name of the project build target and you can rename 58 it by clicking the target name to highlight it and then click the highlighted name to input a new target name, say “HelloWorld SIM” 2. Rename the Source Group The IDE allows you to group source files to different groups to better manage the source code. By default “Source Group 1” is created. You can rename the source group by clicking the source group name to highlight it and then click again to input a new name, say “Startup Code”. 3. Add a New Source Group You can add new source groups to your project. Click Project → Manage → Components, Environment, Books... (See Figure 1.5). You can now add new source groups Figure 1.5: Keil IDE: Manage Project Components to the project. Let’s add “System Code” and “Source Code” source groups to the project (See Figure 1.6. Your project will now look like Figure 1.7 Figure 1.6: Keil IDE: Manage Components Window 4. Add Source Code to a Source Group Now add system_LPC17xx.c to System Code group by double clicking the source group name and choose the file from the file window. Double clicking the file name will add the file to the source group. Or you can select the file and click the “Add” button at the lower right corner of the window (See Figure 1.8). Similarly, add uart_polling.c to System Code group and startup_LPC17xx.s to Startup Code group. Your project will now look like Figure 1.9. 59 Figure 1.7: Keil IDE: Updated Project Profile Figure 1.8: Keil IDE: Add Source File to Source Group Figure 1.9: Keil IDE: Updated Project Profile 5. Create a new source file The project does not have a main function yet. We now create a new file by clicking 60 the “New” button (See Figure 1.10). Before typing anything to the file, save the file Figure 1.10: Keil IDE: Create New File and name it helloworld.c. Write the following code to the helloworld.c file: #include #include "uart_polling.h" int main() { SystemInit(); uart0_init(); uart0_put_string("Hello World!\n\r"); return 0; } Then add helloworld.c to the Source Code group. Your final project would look like the screen shot in Figure 1.11. Figure 1.11: Keil IDE: Final Project Setting 1.1.3 Build and Download Follow the following steps to configure your project for build: 61 • Create the following folders to hold different build-related files for the two targets. – HelloWorld\build\FLASH – HelloWorld\build\RAM • Click the Target Option button and select the Output tab. Click the “Select the Folders for Objects” button to bring up the Browse for Folder dialogue box (see Figure 1.12). You will chose the FLASH folder if you want to build the HelloWorld SIM target and RAM folder if you want to build the HelloWorld RAM target. Similarly, you could select the Listing tab to specify the location of the listing files. Figure 1.12: Keil IDE: Selecting Output Folder To build the target, click the “Build” button (see Figure 1.13). If nothing is wrong, the Figure 1.13: Keil IDE: Build Target build output window at the bottom of the IDE will show a log similar like the one shown in Figure 1.14 62 Figure 1.14: Keil IDE: Build Target Figure 1.15: Keil IDE: Download Target to Flash To download the code to the board, click the “Load” button (see Figure 1.15). The download is through the ULINK-ME. You will need a terminal emulator such as PuTTY that talks directly to COM ports in order to see output of the serial port. Open up the PuTTY on your PC and choose COM1. An example PuTTY Serial configuration is shown in Figures 1.16(a) and 1.16(b). Press the Reset button on the board and you should see “Hello World!” displayed on PuTTY. 1.2 Debugging You can use either the simulator within the IDE or the ULINK Cortex Debugger to debug your program. To start a debug session, click Debug→Start/Stop Debug Session from the IDE menu bar or press Ctrl+F5. Figure 1.16 shows the a typical debug session interface. As any other GUI debugger, the IDE allows you to set up break points and step through your source code. It also shows the registers, which is very helpful for debugging low level code. Click View, Debug and Peripherals from the IDE menu bar and explore the functionality of the debugger. 63 (a) PuTTY Session for Serial Port Communication 1.2.1 (b) PuTTY Serial Port Configuration Simulation Most of the development normally is done under the simulation mode. The default setting of the project uses the simulator to debug as shown in the target option (see Figure 1.17 Instead of load the program to the board for execution, you can run the code using the debugger under simulation mode.Note that the simulator runs the application a lot slower than the actual board. One wall clock second is about one minute inside the simulator. 1.2.2 Configure In-Memory Execution Using ULINK Cortex Debugger When you debug hardware related problems, you most likely will find the ULINK Cortex Debugger is helpful. You need to configure the debugger as shown in Figure 1.18. The default image memory map setting is that the code is executed from the ROM (see Figure1.19(a). Since the ROM portion of the code needs to be flashed in order to be executed on the board, this incurs wear-and-tear on the on-chip flash of the LPC1768. Since most attempts to write a functioning RTX will eventually require some more or less elaborate debugging, the flash memory might wear out quickly. Unlike the flash memory stick file systems where the wear is aimed to be uniformly distributed across the memory portion, this flash memory will get used over and over again in the same portion. The ARM compiler can be configured to have a different starting address. We can create a RAM target where the code starting address is in RAM (see Figure 1.19(b). An 64 Figure 1.16: Keil IDE: Debugging initialization file RAM.ini (see Listing 1.1)is needed to do the proper setting of SP, PC and vector table offset register. FUNC void Setup (void) { SP = _RDWORD(0x10000000); // Setup Stack Pointer PC = _RDWORD(0x10000004); // Setup Program Counter _WDWORD(0xE000ED08, 0x10000000); // Setup Vector Table Offset Register } // You need to provide the path of the .axf file here LOAD build\RAM\HelloWorld.axf INCREMENTAL // Download Setup(); // Setup for Running g, main Listing 1.1: The RAM.ini file To download the code to the board, one should not use the download button. Instead, the debug button is used to initiate a debug session and the command 65 Figure 1.17: Keil IDE: Using Simulator for Debugging Figure 1.18: Keil IDE: Using ULINK Cortex Debugger (a) Default Memory Setting (b) In-Memory Execution Setting Figure 1.19: Keil IDE: Configure for In-Memory Execution LOAD build\RAM\HelloWorld.axf INCREMENTAL in the RAM.ini file will load the code to the board. You will need to edit this command so that the path of the .axf reflects the actual path of your .axf location. 66 Chapter 2 Programming MCB1700 2.1 The Thumb-2 Instruction Set Architecture The Cortex-M3 supports only the Thumb-2 (and traditional Thumb) instruction set. With support for both 16-bit and 32-bit instructions in the Thumb-2 instruction set, there is no need to switch the processor between Thumb state (16-bit instructions) and ARM state (32-bit instructions). In the RTOS lab, you will need to program a little bit in the assembler language. We introduce a few assembly instructions that you most likely need to use in your project in this section. The general formatting of the assembler code is as follows: label opcode operand1, operand2, . . . ; Comments The label is optional. Normally the first operand is the destination of the operation (note STR is one exception). Table 2.1 lists some assembly instructions that the RTX project may use. For complete instruction set reference, we refer the reader to Section 34.2 (ARM Cortex-M3 User Guide: Instruction Set) in [4]. 2.2 ARM Architecture Procedure Call Standard (AAPCS) The AAPCS (ARM Architecture Procedure Call Standard) defines how subroutines can be separately written, separately compiled, and separately assembled to work together. The 67 Mnemonic LDR LDM STR MRS MSR PUSH POP BL BLX BX Operands/Examples Rt, [Rn, #offset] LDR R1, [R0, #24] Rn{!}, reglist LDM R4, {R0 − R1} Rt, [Rn, #offset] STR R3, [R2, R6] STR R1, [SP, #20] Rd , spec reg MRS R0, MSP MRS R0, PSP spec reg, Rm MSR MSP, R0 MSR PSP, R0 reglist PUSH {R4 − R11, LR} reglist POP {R4 − R11, PC} label BL funC Rm BLX R12 Rm BX LR Description Load Register with word Load word value from an memory address R0+24 into R1 Load Multiple registers Load word value from memory address R4 to R0, increment the address, load the value from the updated address to R1. Store Register word Store word in R3 to memory address R2+R6 Store word in R1 to memory address SP+20 Move from special register to general register Read MSP into R0 Read PSP into R0 Move from general register to special register Write R0 to MSP Write R0 to PSP Push registers onto stack push in order of decreasing the register numbers Pop registers from stack pop in order of increasing the register numbers Branch with Link Branch to address labeled by funC, return address stored in LR Branch indirect with link Branch with link and exchange (Call) to an address stored in R12 Branch indirect Branch to address in LR, normally for function call return Table 2.1: Assembler instruction examples 68 C compiler follows the AAPCS to generate the assembly code. Table 2.2 lists registers used by the AAPCS. Register r15 r14 r13 r12 r11 r10 r9 r8 r7 r6 r5 r4 r3 r2 r1 r0 Synonym Special PC LR SP IP v8 v7 v6 SB TR v5 v4 v3 v2 v1 a4 a3 a2 a1 Role in the procedure call standard The Program Counter. The Link Register. The Stack Pointer (full descending stack). The Intra-Procedure-call scratch register. Variable-register 8. Variable-register 7. Platform register. The meaning of this register is defined by platform standard. Variable-register 5. Variable-register 4. Variable-register 3. Variable-register 2. Variable-register 1. argument / scratch register 4 argument / scratch register 3 argument / result / scratch register 2 argument / result / scratch register 1 Table 2.2: Core Registers and AAPCS Usage Registers R0-R3 are used to pass parameters to a function and they are not preserved. The compiler does not generate assembler code to preserve the values of these registers. R0 is also used for return value of a function. Registers R4-R11 are preserved by the called function. If the compiler generated assembler code uses registers in R4-R11, then the compiler generate assembler code to automatically push/pop the used registers in R4-R11 upon entering and exiting the function. R12-R15 are special purpose registers. A function that has the svc indirect keyword makes the compiler put the first parameter in the function to R12 followed by an SVC instruction. R13 is the stack pointer (SP). R14 is the link register (LR), which normally is used to save the return address of a function. R15 is the program counter (PC). Note that the exception stack frame automatically backs up R0-R3, R12, LR and PC together with the xPSR. This allows the possibility of writing the exception handler in purely C language without the need of having a small piece of assembly code to save/restore R0-R3, LR and PC upon entering/exiting an exception handler routine. 69 2.3 Cortex Microcontroller Software Interface Standard (CMSIS) The Cortex Microcontroller Software Interface Standard (CMSIS) was developed by ARM. It provides a standardized access interface for embedded software products (see Figure 2.1). This improves software portability and re-usability. It enables software solution suppliers to develop products that can work seamlessly with device libraries from various silicon vendors [2]. Figure 2.1: Role of CMSIS[8] The CMSIS uses standardized methods to organize header files that makes it easy to learn new Cortex-M microcontroller products and improve software portability. With the .h (e.g. LPC17xx.h) and system startup code files (e.g., startup_LPC17xx.s), your program has a common way to access • Cortex-M processor core registers with standardized definitions for NVIC, SysTick, MPU registers, System Control Block registers , and their core access functions (see core cm ∗ .[ch] files). • system exceptions with standardized exception number and handler names to allow RTOS and middleware components to utilize system exceptions without having compatibility issues. • intrinsic functions with standardized name to produce instructions that cannot be generated by IEC/ISO C. • system initialization by common methods for each MCU. Fore example, the standardized SystemInit() function to configure clock. • system clock frequency with standardized variable named as SystemFrequency defined in the device driver. 70 • vendor peripherals with standardized C structure. Figure 2.2: CMSIS Organization[2] 2.3.1 CMSIS files The CMSIS is divided into multiple layers (See Figure 2.2). For each device, the MCU vendor provides a device header file .h (e.g., LPC17xx.h) which pulls in additional header files required by the device driver library and the Core Peripheral Access Layer (see Figure 2.3). By including the .h (e.g., LPC17xx.h) file into your code file. The first step to initialize the system can be done by calling the CMSIS function as shown in Listing 2.1. SystemInit(); // Initialize the MCU clock Listing 2.1: CMSIS SystemInit() The CMSIS compliant device drivers also contain a startup code (e.g., startup_LPC17xx.s), which include the vector table with standardized exception handler names (See Section 2.3.3. 2.3.2 Cortex-M Core Peripherals We only introduce the NVIC programming in this section. The Nested Vectored Interrupt Controller (NVIC) can be accessed by using CMSIS functions (see Figure 2.4). As an 71 Figure 2.3: CMSIS Organization[2] Figure 2.4: CMSIS NVIC Functions[2] example, the following code enables the UART0 and TIMER0 interrupt NVIC_EnableIRQ(UART0_IRQn); // UART0_IRQn is defined in LPC17xx.h NVIC_EnableIRQ(TIMER0_IRQn); // TIMER0_IRQn is defined in LPC17xx.h 72 2.3.3 System Exceptions Writing an exception handler becomes very easy. One just defines a function that takes no input parameter and returns void. The function takes the name of the standardized exception handler name as defined in the startup code (e.g., startup_LPC17xx.s). The following listing shows an example to write the UART0 interrupt handler entirely in C. void UART0_Handler (void) { // write your IRQ here } Another way is to use the embedded assembly code: __asm void UART0_Handler(void) { ; do some asm instructions here BL __cpp(a_c_function) ; a_c_function is a regular C function ; do some asm instructions here, } 2.3.4 Intrinsic Functions ANSI cannot directly access some Cortex-M3 instructions. The CMSIS provides intrinsic functions that can generate these instructions. The CMSIS also provides a number of functions for accessing the special registers using MRS and MSR instructions. The intrinsic functions are provided by the RealView Compiler. Table 2.3 lists some intrinsic functions that your RTOS project most likely will need to use. We refer the reader to Tables 613 and 614 one page 650 in Section 34.2.2 of [4] for the complete list of intrinsic functions. 2.3.5 Vendor Peripherals All vendor peripherals are organized as C structure in the .h file (e.g., LPC17xx.h). For example, to read a character received in the RBR of UART0, we can use the following code. unsigned char ch; ch = LPC_UART0->RBR; // read UART0 RBR and save it in ch 73 Instruction CPSIE I CPSID I Special Register CONTROL MSP PSP Access Read Write Read Write Read Write CMSIS Intrinsic Function void enable irq(void) void disable irq(void) CMSIS Function uinit32 t get CONTROL(void) void set CONTROL(uint32 t value) uinit32 t get MSP(void) void set MSP(uint32 t value) uinit32 t get PSP(void) void set PSP(uint32 t value) Table 2.3: CMSIS intrinsic functions defined in core cmFunc.h 2.4 Accessing C Symbols from Assembly Only embedded assembly is support in Cortex-M3 (i.e. inline assembly is not supported). To write an embedded assembly function, you need to use the asm keyword. For example the the function “embedded asm function” in Listing 2.2 is an embedded assembly function. You can only put assembly instructions inside this function. Note that inline assembly is not supported in Cortex-M3. The cpp keyword allows one to access C compile-time constant expressions, including the addresses of data or functions with external linkage, from the assembly code. The expression inside the cpp can be one of the followings: • A global variable defined in C typedef struct pcb { struct * mp_next; uint32_t m_sp; //..... } pcb_t; pcb_t g_pcb; uint32_t g_var; __asm embedded_asm_function(void) { LDR R3, =__cpp(&g_pcb) ; load R3 with the address of g_pcb LDM R3, {R1, R2} ; load R1 with g_pcb.mp_next ; load R2 with g_pcb.m_sp LDR R4, =__cpp(g_var) ; load R4 with the value of g_var 74 } Listing 2.2: Example of accessing global variable from assembly • A C function extern void a_c_function(void); ... __asm embedded_asm_function(void) { ;...... BL __cpp(a_c_function) ; a_c_function is regular C function ;...... } • A constant expression in the range of 0 − 255 defined in C. uint8_t const g_flag; __asm embedded_asm_function(void) { ;...... MOV R4, #_cpp(g_flag) ; load g_flag value to R4 ;...... } Note the MOV instruction only applies to immediate constant value in the range of 0 − 255. You can also use the IMPORT directive to import a C symbol in the embedded assembly function and then start to use the imported symbol just as a regular assembly symbol. For example void a_c_function (void) { // do something } __asm embedded_asm_add(void) { IMPORT a_c_function ; a_c_function is a regular C function BL a_c_function ; branch with link to a_c_function } 75 Names in the __cpp expression are looked up in the C context of the __asm function. Any names in the result of the __cpp expression are mangled as required and automatically have IMPORT statements generated from them. 2.5 SVC Programming: Writing an RTX API Function Figure 2.5: SVC as a Gateway for OS Functions [8] A function in RTX API requires a service from the operating system. It needs to be implemented through the proper gateway by trapping from the user level into the kernel level. On Cortex-M3, the SVC instruction is used to achieve this purpose. The basic idea is that when a function in RTX API is called from the user level, this function will trigger an SVC instruction. The SVC_Handler, which is the CMSIS standardized exception handler for SVC exception will then invoke the a kernel function that provides the actual service (see Figure 2.5). Effectively, the RTX API function is a wrapper that invokes SVC exception handler and passes corresponding kernel service operation information to the SVC handler. To generate an SVC instruction, there are two methods. One is a direct method and the other one is an indirect method. The direct method is to program at assembly instruction level. We can use the embedded assembly mechanism and write SVC assembly instruction inside the embedded assembly function. The ARM RL-RTX API functions that are not started with the os_ prefix are implemented in this way. Examples are _alloc_box and _free_box defined in HAL_CM3.c. Listing 2.3 shows the code snippet of the _alloc_box. 76 __asm void *_alloc_box(void *box_mem) { LDR R12,=__cpp(rt_alloc_box) ; privileged mode code handling omitted for brevity reason SVC 0 BX LR ALIGN } Listing 2.3: Code Snippet of alloc box The corresponding kernel function is the C function rt_alloc_box defined in rt_MemBox.c. This function entry point is loaded to r12. Then SVC 0 causes an SVC exception with immediate number 0. In the SVC exception handler, we can then branch with link and exchange to the address stored in r12. Listing 2.4 is an excerpt of the SVC_handler in HAL_CM3.c from the ARM RL-RTX source code. __asm void SVC_Handler(void) { MRS R0, PSP ;Extract SVC number, if SVC 0, then do the following LDM R0, {R0-R3, R12}; Read R0-R3, R12 from stack BLX R12 ; R12 contains the kernel function entry point ;Code to handle context switching is omitted MVN LR, #:NOT:0xFFFFFFFD; set EXC_RETURN, thread mode, PSP BX LR ; ;User SVC code is omitted } Listing 2.4: Code Snippet of SVC Handler The indirect method is to ask the compiler to generate the SVC instruction from C code. The ARM compiler provides an intrinsic keyword named __svc_indirect which passes an operation code to the SVC handler in r12[3]. This keyword is a function qualifier. The two input we need to provide to the compiler is • svc_num, the immediate value used in the SVC instruction and • op_num, the value passed in r12 to the handler to determine the function to perform. The following is the syntax of an indirect SVC. 77 __svc_indirect(int svc_num) return_type function_name(int op_num[, argument-list]); The system handler must make use of the r12 value to select the required operation. For example The RL-RTX provide the following user level function to terminate a task: #include OS_RESULT os_tsk_delete (OS_TID taskid) In RTL.h, the following code is revelent to the implementation of the function. #define __SVC_0 __svc_indirect(0) extern OS_RESULT rt_tsk_delete(OS_TID task_id); #define os_tsk_delete(task_id) _os_task_delete ((U32) rt_tsk_delete, task_id) extern OS_RESULT _os_tsk_delete (U32 p, OS_TID task_id) __SVC_0; The compiler generates two assembly instructions LDR.W r12, [pc, #offset]; Load rt_tsk_delete in r12 SVC 0x00 The SVC_handler in Listing 2.4 then can be used to handle the SVC 0 exception. 78 Chapter 3 Introduction to ECE Linux Programming Environment 3.1 Linux Hardware Environment There are ten Linux servers that are open to ECE undergraduate students. They are ecelinux1.uwaterloo.ca - ecelinux10.uwaterloo.ca. To access these machines from off campus. You will need to use the campus VPN. You could also first ssh into ecelinux4 and then ssh into other Linux servers from there. Note that the ecelinux4 should not be used for computing jobs, it is for accessing other Linux servers on campus. Linux CentOS is installed on all these machines. 3.2 How to Connect to Linux Servers The servers are accessed by remote login. You will need to have a terminal client that supports secure shell (ssh) installed before you can log onto one of the ecelinux servers. Two popular terminal clients are: • Windows secure shell client and • PuTTY . Both terminal clients are installed on ECE Nexus machines. Use your WatIAM credential to login onto these machines. To use the SSH Secure shell windows client, click Start → All Programs → Internet Tools → Secure shell client. Figure 3.1(a) is a screen shot taken on a Nexus computer. 79 To use the PuTTY, click Start → All Programs → Portable PuTTY → PuTTY. Figure 3.1(b) is a screen shot taken on a Nexus computer. (a) SSH Secure Shell Client (b) PuTTY Figure 3.1: Invoking Terminal Clients on an ECE Nexus PC 3.3 3.3.1 Work Environment Setup Setting up Remote Linux Graphic Support After you login, you will notice that you are in a command line shell environment, one where GUI applications cannot be run. For example the ddd debugger is an important GUI application you will most likely want to use. You will need to configure your environment to support GUI application to be able to display graphics on your terminal. First start the X server on your local machine. Xming is a popular and free X server which is installed on ECE Nexus computers. Start Xming by clicking All Programs → Xming (see Figure 3.2). The second step is to configure the terminal client so that X11 forwarding is enabled. 80 Figure 3.2: Invoking Xming on an ECE Nexus Computer (a) Setting (b) X11 Tunneling Setting Figure 3.3: SSH Secure Shell Client X11 Setting For SSH secure shell client, select Edit → Settings to bring up the setting dialog window (see Figure 3.3(a)). Go to Profile Settings → Tunneling and put a check mark beside the Tunnel X11 connection item (see Figure 3.3(b)). For PuTTY, go to Connection → SSH → X11 and put a check mark beside the Enable X11 forwarding item (see Figure 3.4). 3.3.2 Mapping Linux Account on Nexus Your ECE Linux files can be access through network drive mapping on Nexus machines. Open My Computer → Tools → Map Network Drive (see Figure 3.5(a)). Under Driver, 81 Figure 3.4: PuTTY X11 Forwarding Setting pick a drive letter, say P. Under Folder, type \\eceserv\homes. Put a check mark beside Reconnect at logon item and click Finish (see Figure 3.5(b)). You will use your WatIAM credential to authenticate yourself. (a) Prepare to Map a Network Drive (b) Configuring Network Drive Mapping Figure 3.5: SSH Secure Shell Client X11 Setting 82 3.4 Basic Software Development Tools To develop a program, there are three important steps. First, a program is started from source code written by programmers. Second, the source code is then compiled into object code, which is a binary. Non-trivial project normally contains more than one source file. Each source file is compiled into one object code and the linker would finally link all the object code to generate the final target, which is the executable that runs. People refer to compiling and linking as building a target. It is very rare that the target will run perfectly the first time it is built. Most of time you need to fix defects and bugs in your code and the this is the third step. The debugger is a tool to help you identify the bug and fix it. Table 3.1 shows the key steps in programming work flow, the corresponding tools are needed and some example tools provided by a general purpose Linux operating system. Task Tool Examples Editing the source code Editor vi, emacs Compiling the source code Compiler gcc Debugging the program Debugger gdb, ddd Table 3.1: Programming Steps and Tools At each development step, you will have a choice of tools to get the work done. Most of you probably are more familiar with a certain Integrated Development Environment (IDE) which integrates all these tools into a single environment. For example Eclipse and Visual Studio. However another choice is that you pick your favorite tool in each programming step and build your own tool chain. Many seasoned Linux programmers build their own tool chains. A few popular tools are introduced in the following subsections. 3.4.1 Editor Some editors are designed to better suit programmers’ needs than others. The vi (vim and gvim belong to the vi family) and emacs (xemacs belongs to emacs family) are the two most popular editors for programming purposes. Two simple notepad editors pico and nano are also available for a simple editing job. These editors are not designed for serious programming activity. To use one of them to write your first Hello World program is fine though. After you finish editing the C source code, give the file name an extension of .c. Listing 3.1 is the source code of printing ”Hello World!” to the screen. 83 #include #include int main() { printf("Hello World!\n"); exit(0); } Listing 3.1: HelloWorld C source Code Next we will compile and execute the program. 3.4.2 C Compiler The executablegcc is the GNU project C and C++ compiler. To compile the HelloWorld source code in Listing 3.1, type the following command at the prompt: gcc helloworld.c You will notice that a new file named a.out is generated. This is the executable generated from the source code. To run it, type the following command at the prompt and hit Enter. ./a.out The result is“Hello World!” appearing on the screen. You can also instruct the compiler to name the executable another name instead of the default a.out. The -o option in gcc allows one to name the executable a name. For example, the following command will generate an executable named “helloworld.out”. gcc helloworld.c -o helloworld.out although there is no requirement that the name ends in .out. 3.4.3 Debugger The GNU debugger gdb is a command line debugger. Many GUI debugger uses gdb as the back-end engine. One GNU GUI debugger is ddd. It has a powerful data display functionality. 84 GDB needs to read debugging information from the binary in order to be able to help one to debug the code. The -g option in gcc tells the compiler to produce such debugging information in the generated executable. In order to use gdb to debug our simple HelloWorld program, we need to compile it with the following command: gcc -g helloworld.c -o helloworld.out The following command calls gdb to debug the helloworld.out gdb helloworld.out This starts a gdb session. At the (gdb) prompt, you can issue gdb command such as b main to set up a break point at the entry point of main function. The l lists source code. The n steps to the next statement in the same function. The s steps into a function. The p prints a variable value provided you supply the name of the variable. Type h to see more gdb commands. Compared to gdb command line interface, the ddd GUI interface is more user friendly and easy to use. To start a ddd session, type the command ddd and click File → Open Program to open an executable such as helloworld.out. You will then see gdb console in the bottom window with the source window on top of the gdb console window. You could see the value of variables of the program through the data window, which is on top of the source code window. Select View → to toggle all these three windows. 3.5 More on Development Tools For any non-trivial software project, it normally contains multiple source code files. Developers need tools to manage the project build process. Also project normally are done by several developers. A version control tool is also needed. 3.5.1 How to Automate Build Make is an utility to automate the build process. Compilation is a cpu-intensive job and one only wants to re-compile the file that has been changed when you build a target instead of re-compile all source file regardless. The make utility uses a Makefile to specify 85 the dependency of object files and automatically recompile files that has been modified after the last target is built. In a Makefile, one specifies the targets to be built, what prerequisites the target depends on and what commands are used to build the target given these prerequisites. These are the rules contained in Makefile. The Makefile has its own syntax and is a good reference on Make. The general form of a Makefile rule is: target ...: prerequisites ... recipe ... ... One important note is that each recipe line starts with a TAB key rather than white spaces. Listing 3.2 is our first attempt to write a very simple Makefile. helloworld .out: helloworld .c gcc −o helloworld.out helloworld .c Listing 3.2: Hello World Makefile: First Attempt Our second attempt is to break the single line gcc command into two steps. First is to compile the source code into object code .o file. Second is to link the object code to one final executable binary. Listing 3.3 is our second attempted version of Makefile. helloworld .out: helloworld .o gcc −o helloworld.out helloworld .o helloworld .o: helloworld .c gcc −c helloworld.c Listing 3.3: Hello World Makefile: Second Attempt When a project contains multiple files, separating object code compilation and linking stages would give a clear dependency relationship among code. Assume that we now need to build a project that contains two source files src1.c and src2.c and we want the final executable to be named as app.out. Listing 3.4 is a typical example Makefile that is closer to what you will see in the real world. all : app.out app.out: src1 .o src2 .o gcc −o app.out src1.o src2 .o src1 .o: src1 .c gcc −c src1.c 86 src2 .o: src2 .c gcc −c src2.c clean : rm ∗.o app.out Listing 3.4: A More Real Makefile: First Attempt We also have added a target named clean so that make clean will clean the build. So far we have seen the Makefile contains explicit rules. Makefile can also contain implicit rules, variable definitions, directives and comments. Listing 3.5 is a Makefile that is used in the real world. 5 # Makefile to build app.out CC=gcc CFLAGS=−Wall −g LD=gcc LDFLAGS=−g 7 OBJS=src1.o src2.o 9 all : app.out app.out: $(OBJS) $(LD) $(CFLAGS) $(LDFLAGS) −o $@ $(OBJS) .c.o: $(CC) $(CFLAGS) −c $< .PHONY: clean clean : rm −f ∗.o ∗.out 1 2 3 4 10 11 12 13 14 15 16 Listing 3.5: A Real World Makefile Line 1 is a comment. Lines 2 − 7 are variable definitions. Line 12 is an implicit rule to generate .o file for each .c file. See http://www.gnu.org/software/make/manual/make.html if you want to explore more of makefile. 3.5.2 Version Control Software ECE Linux has the following version control software installed. • CVS • SVN • Git 87 Choose your favourite one. Any one of them will be able to help you manage ECE254 code repository. Git is getting more and more popularity these days. If you decide to use GitHub to host your repository, please make sure it is a private one. Go to http://github.com/edu to see how to obtain five private repositories for two years on GitHub for free. 3.5.3 Integrated Development Environment Eclipse with C/C++ Plug-in has been installed on all ECE Linux servers. You will need to first set up the X Window support properly (see section 3.3.1, then type the following command to bring up the eclipse frontend. /opt/eclipse64/eclipse This eclipse is not the same as the default eclipse under /usr/bin directory. You may find running eclipse over network performs poorly at home though. It depends on how fast your network speed is. If you have Linux operating system installed on your own personal computer, then you can download the eclipse with C/C++ plugin from the eclipse web site and then run it from your own local computer. However you should always make sure the program will also work on ecelinux machines, which is the environment TAs would be using to test your code. 3.6 Man Page Linux provides manual pages. You can use the command man followed by the specific command or function you are interested in to obtain detailed information. Mange pages are grouped into sections. We list frequently used sections here: • Section 1 contains user commands. • Section 2 contains system calls • Section 3 contains library functions • Section 7 covers conventions and miscellany. To specify which section you want to see, provide the section number after the man command. For example, 88 man 2 stat shows the system call stat man page. If you omit the 2 in the command, then it will return the command stat man page. You can also use man -k or apropos followed by a string to obtain a list of man pages that contain the string. The Whatis database is searched and now run man whatis to see more details of Whatis. 89 Appendix A Forms Lab administration related forms are given in this appendix. 90 ECE254 Request to Leave a Project Group Form Name: Quest ID: Student ID: Lab Assignment ID Group ID: Name of Other Group Members: Provide the reason for leaving the project group here: Signature Date 91 Appendix B MDK-ARM Installation There is only a windows port for the Keil MDK-ARM for now. The MDK-ARM V4.60.0.0 direct download link is inside the Learn (http://learn.uwaterloo.ca) 1 . During the process of the installation of the MDK-ARM, you will be asked to add example code. Choose Keil(NXP) MCB1xxx Boards example projects (see Figure B.1). Figure B.1: MDK-ARM Installation Steps: Choose Example Projects 1 The latest version of MDK-ARM is at Keil website http://www.keil.com/download/product/. However the lab manual is written for V4.60.0.0. We haven’t tested the latest version. 92 Figure B.2: MDK-ARM Installation Steps: Finish At the last step of MDK-ARM installation, be sure that the launch the “ULINK Pro Driver Figure B.3: MDK-ARM Installation Steps: ULINK Pro Driver V1.0” driver installation check box is checked (see Figure B.2. Once you click “Finish” button, the ULINK Pro Driver installation starts. Click “Install” button to install the driver (see Figure B.3). 93 Appendix C Keil MCB1700 Hardware Environment C.1 MCB1700 Board Overview The Keil MCB1700 board is populated with NXP LPC1768 Microcontroller. Figure C.1 shows the important interface and hardware components of the MCB1700 board. Figure C.2 is the hardware block diagram that helps you to understand the MCB1700 board components. Note that our lab will only use a small subset of the components which include the LPC1768 CPU, COM and Dual RS232. The LPC1768 is a 32-bit ARM Cortex-M3 microcontroller for embedded applications requiring a high level of integration and low power dissipation. The LPC1768 operates at up to an 100 MHz CPU frequency. The peripheral complement of LPC1768 includes 512KB of on-chip flash memory, 64KB of on-chip SRAM and a variety of other on-chip peripherals. Among the on-chip peripherals, there are system control block, pin connect block, 4 UARTs and 4 general purpose timers, some of which will be used in your RTX course project. Figure C.3 is the simplified LPC1768 block diagram [4], where the components to be used in your RTX project are circled with red. Note that this manual will only discuss the components that are relevant to the RTX course project. The LPC17xx User Manual is the complete reference for LPC1768 MCU. C.2 Cortex-M3 Processor The Cortex-M3 processor is the central processing unit (CPU) of the LPC1768 chip. The processor is a 32-bit microprocessor with a 32-bit data path, a 32-bit register bank, and 94 Figure C.1: MCB1700 Board Components [1] Figure C.2: MCB1700 Board Block Diagram [1] 95 Figure C.3: LPC1768 Block Diagram 96 Figure C.4: Simplified Cortex-M3 Block Diagram[8] 32-bit memory interfaces. Figure C.4 is the simplified block diagram of the Cortex-M3 processor [8]. The processor has private peripherals which are system control block, system timer, NVIC (Nested Vectored Interrupt Controller) and MPU (Memory Protection Unit). The MPU programming is not required in the course project. The processor includes a number of internal debugging components which provides debugging features such as breakpoints and watchpoints. C.2.1 Registers The processor core registers are shown in Figure C.5. For detailed description of each register, Chapter 34 in [4] is the complete reference. • R0-R12 are 32-bit general purpose registers for data operations. Some 16-bit Thumb instructions can only access the low registers (R0-R7). • R13(SP) is the stack pointer alias for two banked registers shown as follows: – Main Stack Pointer (MSP): This is the default stack pointer and also reset value. It is used by the OS kernel and exception handlers. – Process Stack Pointer (PSP): This is used by user application code. 97 Figure C.5: Cortex-M3 Registers[4] On reset, the processor loads the MSP with the value from address 0x00000000. The lowest 2 bits of the stack pointers are always 0, which means they are always word aligned. In Thread mode, when bit[1] of the CONTROL register is 0, MSP is used. When bit[1] of the CONTROL register is 1, PSP is used. • R14(LR) is the link register. The return address of a subroutine is stored in the link register when the subroutine is called. • R15(PC) is the program counter. It can be written to control the program flow. • Special Registers are as follows: – Program Status registers (PSRs) – Interrupt Mask registers (PRIMASK, FAULTMASK, and BASEPRI) – Control register (CONTROL) When at privilege level, all the registers are accessible. When at unprivileged (user) level, access to these registers are limited. 98 C.2.2 Processor mode and privilege levels The Cortex-M3 processor supports two modes of operation, Thread mode and Handler mode. • Thread mode is entered upon Reset and is used to execute application software. • Handler mode is used to handle exceptions. The processor returns to Thread mode when it has finished exception handling. Software execution has two access levels, Privileged level and Unprivileged (User) level. • Privileged The software can use all instructions and has access to all resources. Your RTOS kernel functions are running in this mode. • Unprivileged (User) The software has limited access to MSR and MRS instructions and cannot use the CPS instruction. There is no access to the system timer, NVIC , or system control block. The software might also have restricted access to memory or peripherals. User processes such as the wall clock process should run at this level. When the processor is in Handler mode, it is at the privileged level. When the processor is in Thread mode, it can run at privileged or unprivileged (user) level. The bit[0] in CONTROL register determines the execution privilege level. Figure C.6 illustrate the mode and privilege level of the processor. Figure C.6: Cortex-M3 Operating Mode and Privilege Level[8] Note that only privileged software can write to the CONTROL register to change the privilege level for software execution in Thread mode. Unprivileged software can use the 99 SVC instruction to make a supervisor call to transfer control to privileged software. Another way to change between Privileged Thread mode and Unprivileged thread mode is to modify the EXC RETURN value in the LR (R14) when returning from an exception. You probably want to use this mechanism for context switching. C.2.3 Stacks The processor uses a full descending stack. This means the stack pointer indicates the last stacked item on the stack memory. When the processor pushes a new item onto the stack, it decrements the stack pointer and then writes the item to the new memory location. The processor implements two stacks, the main stack and the process stack. One of these two stacks is banked out depending on the stack in use. This means only one stack is visible at a time as R13. In Handler mode, the main stack is always used. The bit[1] in CONTROL register reads as zero and ignores writes in Handler mode. In Thread mode, the bit[1] setting in CONTROL register determines whether the main stack or the process stack is currently used. Table C.1 summarizes the processor mode, execution privilege level, and stack use options. Processor mode Thread Used to execute Applications Handler Exception handlers Privilege level for software execution Privileged Unprivileged Privileged CONTROL Bit[0] Bit[1] 0 0 1 1 0 Stack used Main Stack Process Stack Main Stack Table C.1: Summary of processor mode, execution privilege level, and stack use options C.3 Memory Map The Cortex-M3 processor has a single fixed 4GB address space. Table C.2 shows how this space is used on the LPC1768. Note that the memory map is not continuous. For memory regions not shown in the table, they are reserved. When accessing reserved memory region, the processor’s behavior is not defined. All the peripherals are memory-mapped and the LPC17xx.h file defines the data structure to access the memory-mapped peripherals in C. 100 Address Range 0x0000 0000 to 0x1FFF FFFF 0x2000 0000 to 0x3FFF FFFF 0x4000 0000 to 0x5FFF FFFF General Use On-chip non-volatile memory On-chip SRAM Boot ROM On-chip SRAM (typically used for peripheral data) GPIO APB Peripherals AHB peripherals 0xE000 0000 to 0xE00F FFFF Cortex-M3 Private Peripheral Bus (PPB) Address range details 0x0000 0000 − 0x0007 FFFF Description 512 KB flash memory 0x1000 0x1FFF 0x2007 0x2008 0000 − 0x1000 0000 − 0x1FFF C000 − 0x2007 0000 − 0x2008 7FFF 1FFF FFFF 3FFF 32 KB local SRAM 8 KB Boot ROM AHB SRAM - bank0 (16 KB) AHB SRAM - bank1 (16 KB) 0x2009 0x4000 0x4008 0x5000 C000 − 0x2009 0000 − 0x4007 0000 − 0x400F 0000 − 0x501F FFFF FFFF FFFF FFFF GPIO APB0 Peripherals APB1 Peripherals DMA Controller, Ethernet interface, and USB interface Cortex-M3 private registers(NVIC, MPU and SysTick Timer et. al.) 0xE000 0000 − 0xE00F FFFF Table C.2: LPC1768 Memory Map C.4 Exceptions and Interrupts The Cortex-M3 processor supports system exceptions and interrupts. The processor and the Nested Vectored Interrupt Controller (NVIC) prioritize and handle all exceptions. The processor uses Handler mode to handle all exceptions except for reset. C.4.1 Vector Table Exceptions are numbered 1-15 for system exceptions and 16 and above for external interrupt inputs. LPC1768 NVIC supports 35 vectored interrupts. Table C.3 shows system exceptions and some frequently used interrupt sources. See Table 50 and Table 639 in [4] for the complete exceptions and interrupts sources. On system reset, the vector table is fixed at address 0x00000000. Privileged software can write to the VTOR (within the System Control Block) to relocate the vector table start address to a different memory location, in the range 0x00000080 to 0x3FFFFF80. C.4.2 Exception Entry Exception entry occurs when there is a pending exception with sufficient priority and either • the processor is in Thread mode 101 Exception number 1 2 3 4 IRQ number -14 -13 -12 Vector address or offset 0x00000004 0x00000008 0x0000000C 0x00000010 Exception type Reset NMI Hard fault Memory management fault Priority C PreFix -3, the highest -2, -1 Configurable NMI HardFault MemManage .. . 11 .. . -5 0x0000002C SVCall Configurable SVC -2 -1 0 1 2 3 4 5 6 7 8 0x00000038 0x0000003C 0x00000040 0x00000044 0x00000048 0x0000004C 0x00000050 0x00000054 0x00000058 0x0000005C 0x00000060 PendSV SysTick WDT Timer0 Timer1 Timer2 Timer3 UART0 UART1 UART2 UART3 Configurable Configurable Configurable Configurable Configurable Configurable Configurable Configurable Configurable Configurable Configurable PendSVC SysTick WDT IRQ TIMER0 IRQ TIMER1 IRQ TIMER2 IRQ TIMER3 IRQ UART0 IRQ UART1 IRQ UART2 IRQ UART3 IRQ 14 15 16 17 18 19 20 21 22 23 24 .. . Table C.3: LPC1768 Exception and Interrupt Table • the processor is in Handler mode and the new exception is of higher priority than the exception being handled, in which case the new exception preempts the original exception (This is the nested exception case which is not required in our RTOS lab). When an exception takes place, the following happens • Stacking When the processor invokes an exception (except for tail-chained or a late-arriving exception, which are not required in the RTOS lab), it automatically stores the following eight registers to the SP: – R0-R3, R12 – PC (Program Counter) – PSR (Processor Status Register) – LR (Link Register, R14) 102 Figure C.7 shows the exception stack frame. Note that by default the stack frame is aligned to double word address starting from Cortex-M3 revision 2. The alignment feature can be turned off by programming the STKALIGN bit in the System Control Block (SCB) Configuration Control Register (CCR) to 0. On exception entry, the processor uses bit[9] of the stacked PSR to indicate the stack alignment. On return from the exception, it uses this stacked bit to restore the correct stack alignment. Figure C.7: Cortex-M3 Exception Stack Frame [8] • Vector Fetching While the data bus is busy stacking the registers, the instruction bus fetches the exception vector (the starting address of the exception handler) from the vector table. The stacking and vector fetch are performed on separate bus interfaces, hence they can be carried out at the same time. • Register Updates After the stacking and vector fetch are completed, the exception vector will start to execute. On entry of the exception handler, the following registers will be updated as follows: – SP: The SP (MSP or PSP) will be updated to the new location during stacking. Stacking from the privileged/unprivileged thread to the first level of the exception handler uses the MSP/PSP. During the execution of exception handler routine, the MSP will be used when stack is accessed. – PSR: The IPSR will be updated to the new exception number 103 – PC: The PC will change to the vector handler when the vector fetch completes and starts fetching instructions from the exception vector. – LR: The LR will be updated to a special value called EXC RETURN. This indicates which stack pointer corresponds to the stack frame and what operation mode the processor was in before the exception entry occurred. – Other NVIC registers: a number of other NVIC registers will be updated .For example the pending status of exception will be cleared and the active bit of the exception will be set. C.4.3 EXC RETURN Value EXC RETURN is the value loaded into the LR on exception entry. The exception mechanism relies on this value to detect when the processor has completed an exception handler. The EXC RETURN bits [31 : 4] is always set to 0xFFFFFFF by the processor. When this value is loaded into the PC, it indicates to the processor that the exception is complete and the processor initiates the exception return sequence. Table C.4 describes the EXC RETURN bit fields. Table C.5 lists Cortex-M3 allowed EXC RETURN values. Bits Description 31:4 0xFFFFFFF 3 Return mode (Thread/Handler) 2 Return stack 1 Reserved; must be 0 0 Process state (Thumb/ARM) Table C.4: EXC RETURN bit fields [8] Value 0xFFFFFFF1 0xFFFFFFF9 0xFFFFFFFD Return Mode Handler Thread Thread Description Exception return gets state from MSP MSP PSP SP after return MSP MSP PSP Table C.5: EXC RETURN Values on Cortex-M3 C.4.4 Exception Return Exception return occurs when the processor is in Handler mode and executes one of the following instructions to load the EXC RETURN value into the PC: 104 • a POP instruction that includes the PC. This is normally used when the EXC RETURN in LR upon entering the exception is pushed onto the stack. • a BX instruction with any register. This is normally used when LR contains the proper EXC RETURN value before the exception return, then BX LR instruction will cause an exception return. • a LDR or LDM instruction with the PC as the destination. This is another way to load PC with the EXC RETURN value. Note unlike the ColdFire processor which has the RTE as the special instruction for exception return, in Cortex-M3, a normal return instruction is used so that the whole interrupt handler can be implemented as a C subroutine. When the exception return instruction is executed, the following exception return sequences happen: • Unstacking: The registers (i.e. exception stack frame) pushed to the stack will be restored. The order of the POP will be the same as in stacking. The SP will also be changed back. • NVIC register update: The active bit of the exception will be cleared. The pending bit will be set again if the external interrupt is still asserted, causing the processor to reenter the interrupt handler. C.5 Data Types The processor supports 32-bit words, 16-bit halfwords and 8-bit bytes. It supports 64-bit data transfer instructions. All data memory accesses are managed as little-endian. 105 Bibliography [1] MCB1700 User’s Guide. http://www.keil.com/support/man/docs/mcb1700. 95 [2] MDK Primer. http://www.keil.com/support/man/docs/gsac. 70, 71, 72 [3] Realview compilation tools version 4.0: Compiler reference guide, 2007-2010. 77 [4] LPC17xx User Manual, Rev2.0, 2010. 67, 73, 94, 97, 98, 101 [5] AD Marshall. Programming in c unix system calls and subroutines using c. Available on-line at http://www.cs.cf.ac.uk/Dave/C/CE.html, 1999. 42 [6] M. Mitchell, J. Oldham, and A. Samuel. Advanced linux programming. Available on-line at http://advancedlinuxprogramming.com, 2001. 40, 41, 42 [7] W. Stallings. Operating systems: internals and design principles. Prentice Hall, seventh edition, 2011. [8] J. Yiu. The Definitive Guide to the ARM Cortex-M3. Newnes, 2009. x, 70, 76, 97, 99, 103, 104 106


Source Exif Data:
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.5
Linearized                      : No
Page Count                      : 119
Page Mode                       : UseOutlines
Author                          : Yiqing Huang
Title                           : UW LaTeX Thesis Template
Subject                         : 
Creator                         : LaTeX with hyperref package
Producer                        : pdfTeX-1.40.12
Create Date                     : 2017:05:15 14:57:40-04:00
Modify Date                     : 2017:05:15 14:57:40-04:00
Trapped                         : False
PTEX Fullbanner                 : This is MiKTeX-pdfTeX 2.9.4487 (1.40.12)
EXIF Metadata provided by EXIF.tools

Navigation menu