at91sam7s quick start guide for...

83
AT91SAM7S Quick Start Guide for Beginners 6 March 2013 Version 2.2 06/03/2013 Version 2 16/01/2013 Version 1 03/01/2013 Adam Goodwin

Upload: others

Post on 19-Apr-2020

8 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

AT91SAM7S Quick Start Guide for Beginners 6 March 2013

Version 2.2 – 06/03/2013

Version 2 – 16/01/2013

Version 1 – 03/01/2013

Adam Goodwin

Page 2: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Table of Contents

Introduction ............................................................................................................................................ 4

Overview ................................................................................................................................................. 5

A Brief Introduction to the SAM7S MCUs ............................................................................................... 6

The SAM7S Variants ............................................................................................................................ 6

Memory Remapping and Exception Vectors ...................................................................................... 6

Reset Configuration ............................................................................................................................ 7

SAM7S Processor ................................................................................................................................ 7

Installing the Required Software and Hardware .................................................................................... 8

What You’ll Need ................................................................................................................................ 8

1. The Eclipse IDE (Integrated Development Environment) ....................................................... 8

2. YAGARTO (Yet Another GNU ARM Toolchain) ........................................................................ 8

3. OpenOCD (Open On-Chip Debugger) ...................................................................................... 8

4. An ECE USB-JTAG adapter ....................................................................................................... 9

5. A board with a SAM7S chip, wired to allow a JTAG connection ............................................. 9

The Overall Setup ................................................................................................................................ 9

Installing the Components ................................................................................................................ 12

Eclipse ........................................................................................................................................... 12

YAGARTO ....................................................................................................................................... 12

OpenOCD ...................................................................................................................................... 12

Hardware ...................................................................................................................................... 12

Creating the LED-Flashing Test Project ................................................................................................. 15

Starting a New Eclipse Project .......................................................................................................... 17

Copying Existing Files to the Project ................................................................................................. 19

Writing the Source Files .................................................................................................................... 20

Creating the “sam7s_startup.s” Assembly Source File ................................................................. 20

Creating the “board.h” Header File .............................................................................................. 26

Creating the “sam7s256_init.c” C Source File............................................................................... 27

Creating the “main.c” C Source File .............................................................................................. 37

Creating the Delay Module ........................................................................................................... 39

Scripts and Configuration Files ......................................................................................................... 42

Creating the “led_test_sam7s256.ld” Linker Script ...................................................................... 42

Page 3: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Creating the “run_openocd.bat” Batch File .................................................................................. 50

Creating the “uc-sam7-usbjtag.cfg” OpenOCD Configuration File ............................................... 51

Creating the Makefile ................................................................................................................... 55

Testing and Debugging .......................................................................................................................... 67

Checking the Hardware and OpenOCD ............................................................................................. 68

Programming the Board .................................................................................................................... 70

Debugging the Program .................................................................................................................... 72

Debugging the Startup Code ............................................................................................................. 73

Debugging Using OpenOCD .............................................................................................................. 75

Conclusion and Contact Information .................................................................................................... 79

Web Links .............................................................................................................................................. 80

Version 2 Changes ................................................................................................................................. 82

References ............................................................................................................................................ 83

Page 4: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Introduction

The intention of this guide is to allow a quick and easy setup of everything you need to begin

programming one of Atmel’s AT91SAM7S microcontrollers.

The idea is that by simply following the guide you will be ready to write software for the

microcontroller, and won’t have to spend time stumbling around trying to get things working on

your own. Starting from scratch on your own would be particularly difficult if you are unfamiliar with

the open source software required.

This guide is aimed at beginners who have very little embedded systems experience outside of an

introductory university course, but who are to work with a SAM7S microcontroller and need a crash

course. The guide assumes you are working under Windows, with the guide itself being put together

and tested under Windows 7.

I have made this guide after getting a SAM7S up and running for the first time, so I am by no means

an expert. I made extensive use of two tutorials to get to this stage: Using Open Source Tools for

AT91SAM7S Cross Development (Lynch, 2007) and Building Bare-Metal ARM Systems with GNU

(Samek, 2007).

These two tutorials were extremely helpful, and I recommend reading them if you have time. If you

try to follow the two tutorials exactly you may have some trouble with the tutorials being out of

date, due to significant changes having been made to the open-source tools since the time of the

tutorials’ writing. However, by reading them you will learn a lot.

This guide is based heavily on the two tutorials (I cannot emphasise enough how helpful these

tutorials were) but has been brought up to date to 2013. This guide also contains instructions

specific to the University of Canterbury, due to the use of the Electrical and Computer Engineering

(ECE) Department’s USB-JTAG adapter, but it shouldn’t be too difficult to use another interface in its

place if needed (the guide will try to help you with this once you reach the relevant section).

Upon completing this guide you should have an LED-flashing application which can be successfully

loaded to the SAM7S chip and debugged from your PC. Above all else, what you should gain from

this guide is an elementary understanding of all aspects of programming a SAM7S, and a saving of

many hours you might have otherwise spent learning how to get up and running on your own. The

guide is all-inclusive, so hopefully you will not be left confused about any aspect of the process.

Note: As stated previously, this is a guide for beginners. Those with more experience will probably

manage on their own, or will only need to make use of the more advanced tutorials from James

Lynch and Miro Samek. Still, this brief guide may be worth reading as a primer, and any suggestions

or corrections are welcome.

You can contact me via email at [email protected].

Page 5: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Overview

This guide takes you step-by-step through installing the required software and hardware, then

moves onto an example project which simply flashes any LEDs connected to the microcontroller

(MCU). When you test the program after reaching the end of the guide a very brief introduction to

debugging (using the command line) is also included.

When walking you through the required software and hardware the guide explains briefly what each

component is, where to get it, and how everything fits together.

The workings of the example project are explained as each of the source files is introduced. The

guide is based around a SAM7S256, but explains with examples how to adapt the project to different

models of SAM7S MCU where appropriate. The project can be put together and loaded onto your

chip as a starting point, and from there you can then extend the example to suit the needs of your

own project.

The example project contains low level code, and some script, which is sourced from Miro Samek’s

tutorial (Samek, 2007). However, there are some minor modifications. Note also that there are no

advanced features, such as interrupts, utilised in this example project. This is because the example

project is intended to be a very simple test to make sure that everything has been set up correctly

for development, and that everything is working as expected. If you get this simple project working,

you can then go on to adjust it to include all of the more complicated features of Miro Samek’s

tutorial – such as nested interrupts or C++ code. James Lynch’s tutorial also covers advanced

features such as interrupts.

The idea with using such a simple example project is that if you reach the end of the guide, but don’t

have the expected result, you’ll have fewer places to look for the cause of your problems. If you’re

anything like me you’ll also prefer to rely on as little of somebody else’s code as possible when

learning to use a new MCU, with the code that you do reuse being simple enough to understand.

The main point is, if something isn’t working, it’s extremely difficult to work out the source of the

problem if your project is trying to use all sorts of advanced features – at least that’s what I find.

To summarise what the guide contains before you dive in, here are the main segments that will be

covered:

- Introduction to the microcontroller

- Installation of the software and hardware

- Creating the example project

- Testing that everything has worked, and debugging

Page 6: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

A Brief Introduction to the SAM7S MCUs

As you already know, this guide is specifically for Atmel’s range of SAM7S MCUs. Depending on how

much you know, there are a few things discussed here which might help you to become more

familiar with the SAM7S family of MCUs.

The SAM7S Variants

First of all, there are eight devices in the family. They are the (AT91SAM7S) 16, 161, 32, 321, 64, 128,

256 and 512. Aside from the 16 and 32 which have 48 pins, they are all 64 pin MCUs with very

similar peripherals. The main difference between the devices is the amount of flash memory and

SRAM that they have.

The best place to go for information on the MCUs is the SAM7S datasheet, available from Atmel here

(it is named SAM7S Series Complete).

Memory Remapping and Exception Vectors

One important thing to know about the SAM7S MCUs is the memory remapping feature. Although

this is explained in the datasheet it is worth mentioning explicitly here. A SAM7S, being a 32-bit

MCU, can address up to 232 bytes or 4GB of memory. To us, the most relevant of these addresses are

those referring to the first 3MB of memory:

The 1MB address space spanning from the address 0x0010 0000 through to 0x001F FFFF (inclusive)

is mapped to the internal flash memory of the MCU. The actual size of the flash memory varies from

16KB to 512KB depending on the particular SAM7S variant being used, but it always starts at 0x0010

0000.

The 1MB of addresses spanning from 0x0020 0000 through to 0x002F FFFF (inclusive) refers to the

internal SRAM of the MCU (again with the actual memory size varying from 4KB to 64KB depending

on the MCU).

Finally, the 1MB region from 0x0000 0000 to 0x000F FFFF (inclusive) is known as the remap region.

Ordinarily this points to the 1MB flash memory area, but this can be toggled via a register in order to

point the remap region to the 1MB area for SRAM.

This is important, because the design of the processor means that the bytes of memory from 0x0000

0000 up to (but not including) 0x0000 0020 are exception vectors. These “exception vectors” are 32-

bit addresses which the processor should execute instructions from, should an exception occur.

To explain further, consider the reset exception vector at address 0x0000 0000. When the MCU is

powered on or reset, the first instruction to be executed is fetched from the reset exception vector.

So the instruction at address 0x0000 0000 is the first instruction of your program.

Page 7: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

The consequence of the exception vectors and the remapping feature is that while your program is

running, it can change where the exception vectors are located (in flash or in RAM) by using the

remap command. In turn, the consequence of this is that once the remap region is mapped to RAM,

you can modify the exception vectors by writing into the first 32 bytes of address space (which can’t

be done when this area of memory points to flash – as flash is for all intents and purposes read-only

memory).

Reset Configuration

To reset a SAM7S MCU, the reset pin needs to be pulled to ground. However, a trick that might catch

you out is that the reset pin needs to first be enabled in software – and it is disabled by default. How

to enable it is explained later in the guide, with the rest of the initialisation code, but it is again a

notable feature of the MCUs which should be brought to a beginner’s attention.

SAM7S Processor

The SAM7S family of MCUs uses the ARM7TDMI CPU, a 32-bit RISC-based CPU, the architecture of

which is the ARMv4T architecture. For more information on the architecture, refer to the ARM

Architecture Reference Manual available from ARM’s documentation site (the ARMv5 Reference

Manual covers the ARMv4T architecture). Note that you will need to register a free ARM account to

view the manual. The manual will be of particular use if you plan on looking at this guide’s assembly

code in any amount of detail.

An interesting feature of the architecture is that the CPU can execute both 32-bit ARM instructions

and 16-bit Thumb instructions. It’s up to you which source files you want to compile to ARM code

and which files you want to compile to Thumb code.

Page 8: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Installing the Required Software and Hardware

In this section the software and hardware needed for completing this quick start guide are

described. Everything you need, where to get it, and how to set it up, is explained here. We will start

by looking at each of the tools you need followed by an explanation of what they do and how they

interact.

What You’ll Need

What follows is a list of the software and hardware needed for this guide. All of the software is free,

and the list includes links to where you can obtain it. Don’t worry about installing everything in the

list now; the installation is covered later in the guide. At this stage you can just download everything

to a temporary folder and make sure you have the hardware on hand.

We will start at the higher level software needed to program the chip and then work down towards

the MCU. Here is what you’ll need:

1. The Eclipse IDE (Integrated Development Environment)

First of all you will need somewhere to write your code. You can use anything you want for this

really, even just a text editor if you don’t want to download and install a full IDE. Eclipse has a lot

of useful features though, and YAGARTO (the next tool you’ll need) is designed to be compatible

with Eclipse, so it makes sense to use it for this guide.

(Note: I haven’t actually explained integrating YAGARTO and Eclipse in this guide, and only use

the command line for building and debugging the example project, but once again you can get

information about connecting YAGARTO and Eclipse from James Lynch’s tutorial – if you’d prefer

to debug from within the IDE.)

You can get Eclipse from the Downloads page of the Eclipse website. Just make sure you choose

the Eclipse IDE for C/C++ Developers – the download should be a zipped folder. At the time of

writing the current release is Eclipse Juno 4.2.

2. YAGARTO (Yet Another GNU ARM Toolchain)

YAGARTO is a GNU ARM toolchain from Michael Fischer. It consists of two packages, which can

be found on the YAGARTO website. These are the “YAGARTO Tools” and the “YAGARTO GNU

ARM Toolchain” packages. YAGARTO contains everything you need to build ARM compatible

programs from your C code (the SAM7S MCUs have an ARM processor). Download these two

executables now. At the time of writing the most recent tools package is dated 18/10/2012 and

the most recent toolchain package is dated 22/12/2012.

3. OpenOCD (Open On-Chip Debugger)

OpenOCD is software created by Dominic Rath which allows you to program and debug the MCU

from your PC. OpenOCD’s SourceForge page can be found here. Under the “Getting OpenOCD”

Page 9: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

page you will find that there’s a link to Freddie Chopin’s website, where you can download a

copy of OpenOCD already compiled for Windows. Download the latest version now. At the time

of writing the current version is OpenOCD 0.6.1.

4. An ECE USB-JTAG adapter

You’ll need this to connect your SAM7S MCU to your PC. Although it is assumed you’ll be using

one of the ECE Department’s USB-JTAG adapters, it shouldn’t be too difficult to use another

adapter if you have one. Still, you can borrow an ECE adapter at no cost, so I recommend using

one at least until you finish this guide and have gotten everything to work. At that point you can

try a different adapter if you like, and you’ll have narrowed the problem down if things stop

working.

5. A board with a SAM7S chip, wired to allow a JTAG connection

This guide was written while using a development board from Olimex (the SAM7-H256 board)

with a SAM7S256 MCU. The chances are that if you’re at the University of Canterbury, this is

what you are using as well. However, as long as you have an AT91SAM7S MCU that can accept a

JTAG connection, this guide will apply.

The Overall Setup

The below image shows the overall configuration of the five items from the previous section. What

follows on the next page is a description of the setup, so that you can understand what each

component is for and how they all interact.

1

2

3

4

5

Source code

Executable code

USB connection

JTAG connection

The overall setup with the Eclipse IDE (1), YAGARTO (2), OpenOCD (3), the USB to JTAG

interface (4), and the target board (5). The links show the relationship between

components.

Page 10: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Again I’ll start with the high level software and work down towards the lower levels. If you want

more information about any stage, James Lynch’s tutorial covers the same sort of material but is

more in-depth (Lynch, 2007).

For the purposes of this guide, as I have mentioned above, the Eclipse IDE is really only used as a text

editor. Nonetheless, to complete the LED-flashing project, you’ll need to write the source code

somewhere.

Once you have your source code, this is when the YAGARTO toolchain takes over (not the YAGARTO

Tools package, that comes in later). YAGARTO’s GNU compiler is used to compile your C code to

object code for the ARM7TDMI processor of a SAM7S chip. Similarly, YAGARTO’s GNU assembler is

used to assemble your assembly code into object code.

At this stage you’ll have several object files corresponding to each of the original source files.

YAGARTO’s GNU linker is then invoked, via the GNU compiler, to combine all of these together

(along with any standard libraries used) into a single ELF executable. For the linker to create this

executable program, we have to provide it with a linker script. The linker script tells the linker how

to lay out the program in memory, so that it can be run correctly from the flash memory and RAM of

the SAM7S MCU.

All of this YAGARTO use can be done from the command line. That is, you can go from the source

code to the ELF executable by entering (a lot of) commands into the Windows Command Prompt.

However, as you probably know, a makefile simplifies this greatly – you can just type “make” into

Command Prompt and everything is done automatically. This is where YAGARTO Tools comes in. The

YAGARTO Tools package contains Make for Windows as well as some other helpful tools.

Anyway, once you have an ELF executable you still need to get the program off of your computer

and into the MCU’s flash memory. OpenOCD runs as a server on your PC and handles

communication with the MCU, including the programming. OpenOCD connects to the ECE USB-JTAG

adapter via USB, which in turn connects to the MCU via JTAG. When starting OpenOCD we give it a

configuration file to tell it about the JTAG adapter and MCU we’re using.

What is meant by “OpenOCD runs as a server” is that OpenOCD runs in the background on your PC,

waiting for requests (from clients) for it to communicate with the connected hardware. You can

connect to the OpenOCD server over TCP using Telnet and give it commands manually (for example

you could send it commands to write your program to the MCU’s flash memory). Alternatively, you

can run GDB (the GNU debugger, included with YAGARTO) and connect GDB to the OpenOCD server.

From there, you can let GDB debug the program running on the MCU just as it would with any other

program (provided you have told GDB what the ELF executable in use on the MCU is). Not only can

GDB debug through OpenOCD, but you can also manually send commands to the OpenOCD server

from within GDB.

Page 11: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

So by using GDB you have full control. You can run GDB and debug your program, you can load an

ELF executable to the MCU, and you can also make use of OpenOCD commands from within GDB.

Hopefully you are now starting to understand the relationship shown in the image on the previous

page, and how you can go from writing source code to running your program on the MCU using the

tools in this guide.

Page 12: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Installing the Components

Eclipse

First install the Eclipse IDE. All that you should need to do is unzip the zipped folder you downloaded

from the Eclipse website. The only thing is that you might want to put it in an appropriate place. I

unzipped the compressed folder to “C:\Program Files\Eclipse” and put a shortcut to eclipse.exe on

the desktop.

If you try to run Eclipse, and get an error about the Java Runtime Environment (JRE), you probably

don’t have it installed. You can install it from the Java website, at the time of writing the most recent

version is Version 7 Update 10.

YAGARTO

Now install the YAGARTO Toolchain by double clicking the executable you downloaded from the

YAGARTO website. It should be the executable with a long name, containing the version numbers of

the main components of the toolchain. There isn’t much you need to do, just make sure all of the

installation components are selected and ensure you install to a directory with no spaces – such as

“C:\yagarto-20121222”. (The YAGARTO website explains that the path without spaces is required for

both of the packages to work.)

Now install the YAGARTO Tools package. Double click this installer and do the same as with the

toolchain; make sure all of the components are selected and that you choose a path with no spaces.

OpenOCD

Finally install OpenOCD. Again, this is just a matter of extracting the compressed folder to an

appropriate directory – I chose “C:\Program Files\openocd-0.6.1”.

Hardware

If you are not using the ECE USB-JTAG adapter you should follow the installation instructions

included with your adapter. The rest of this section can be skipped.

Otherwise, if you are using the ECE USB-JTAG adapter, you should connect the adapter to a USB port

on your PC for the first time. The adapter uses an FT2232D chip, and the appropriate FTDI drivers

should be installed automatically by Windows. If Windows gives a message that the drivers were not

successfully installed, you might have to click on this notification and specify that Windows should

search the internet for the appropriate drivers.

To verify that the drivers have installed, open Control Panel. On Windows 7, if you are viewing by

category, open Hardware and Sound then open Device Manager. Otherwise just open Device

Page 13: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Manager directly. Once Device Manager has opened, expand the “Universal Serial Bus controllers”

section. Here you should see the “USB Serial Converter A” and “USB Serial Converter B” devices

associated with the USB-JTAG adapter.

Right-click on “USB Serial Converter A”. From here you can choose “Update Driver Software...” if you

haven’t already had the successful install. You can also right-click and choose properties to verify the

installation. In the Driver tab of the properties window you should see that the FTDI drivers are

installed; it should look something like the following:

There is still another step to completing the installation of the adapter; the libusb-win32 filter driver

needs to be installed. This can be downloaded from the SourceForge page here. Scroll down and

follow the link to the download site, then browse the libusb-win32-releases folder. Then browse to

the folder of the most recent release – at the time of writing the most recent release is 1.2.6.0.

The file you are looking for will be named “libusb-win32-devel-filter-1.2.6.0.exe”, except with the

appropriate version number that you selected. Download and run this installer. It shouldn’t matter

where you install to – I used the default setting of “C:\Program Files\LibUSB-Win32”.

Page 14: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Once the installation has finished, navigate to the install directory and find in the “bin” folder the

“install-filter-win.exe” program. Run this program, and when it opens choose to “Install a device

filter”.

In the next set of options, you need to select the “USB Serial Converter A” device as the device to be

filtered.

Click “Install”, and you should get a notification that the filter was successfully installed. You can now

cancel out of the installer. If you want to check everything is as it should be, go back to Device

Manager. If the “USB Serial Converter A” device is still listed, and doesn’t have a little yellow error

icon over it, then everything should be fine.

Now you have everything you need to start working on a SAM7S project. Creating the LED-flashing

project is next up, starting on the following page.

Page 15: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Creating the LED-Flashing Test Project

This section will walk you through using Eclipse to write all of the source files, scripts, and other files

needed to complete the LED-flashing example project. The idea is that you’ll rewrite each of the files

into your Eclipse workspace – making sure you have an understanding of what’s happening in each

one.

If you’re in a real hurry you can just copy and paste each of the files’ contents from this guide, but I

recommend you take the time to try and get as full an understanding as possible.

The reason this is a “quick start” guide is not because it’s short, but because a person unfamiliar with

many of these kinds of files and scripts should be able to get a good idea of how they all work

relatively quickly by reading it (much more quickly than if you tried to learn by reading the

documentation only). At least, that is the intention.

The files you will need to create for this project are:

- sam7s_startup.s*

- board.h

- sam7s256_init.c*

- main.c

- delay.h

- delay.c

- led_test_sam7s256.ld*

- run_openocd.bat

- uc-sam7-usbjtag.cfg

- makefile*

To be clear, you really do need to have an understanding of these sorts of files to program a SAM7S

MCU with any confidence. Particularly, you should make an effort to understand the assembly file,

initialisation C file, linker script file, OpenOCD configuration file, and makefile.

The files are explained in more detail as they are introduced to the project throughout the guide.

This includes the changes you’ll need to make if you’re not using a SAM7S256 MCU or an ECE USB-

JTAG adapter.

There is also one additional file you will need, which I haven’t included in this guide:

- AT91SAM7S256.h

This is a header file from Atmel’s official libraries. You can get this file from the AT91 library available

from this download page on Atmel’s website. At the time of writing, the latest version of the library

is AT91LIB version 1.9. You can download the zipped folder and find within it just the header file

needed for your MCU, but you might as well hold onto the entire library as it contains some other

header and source files you might find useful in the future.

*These files are based heavily on files from Miro Samek’s tutorial (Samek, 2007). Although my versions

differ, particularly with respect to the makefile, the majo rity of the code is his and credit (and my thanks)

goes to him for it. The link to the Building Bare-Metal ARM Systems with GNU article is in the references

section of this guide.

Page 16: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Note: The AT91LIB library appears to have AT91SAM7S header files for every SAM7S chip, except for

the SAM7S16. The header file for the SAM7S16 is there, but has been incorrectly named as

AT91SAM7S161.h (open the file and look at the comments and definitions, and you’ll soon see that

this is the case). In actual fact it is the header file for the SAM7S161 that is missing.

If you happen to be using a SAM7S161, what you should be able to do is copy a header file from one

of the other 64-pin MCUs (e.g. AT91SAM7S256.h) and replace its memory mapping definitions (at

the bottom of the file) with those from the AT91SAM7S161.h file (which is supposed to be named

AT91SAM7S16.h). I don’t guarantee this to be completely correct though.

Page 17: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Starting a New Eclipse Project

When you start Eclipse it will ask you to choose a workspace folder. Each Eclipse project you make is

contained in its own folder, and all of your project folders will be stored in your workspace folder.

Put your workspace folder wherever is most convenient.

After Eclipse has loaded and you close the welcome page, it should look something like this:

Because this guide won’t go into using Eclipse for building and debugging, it’s enough to know that

the Project Explorer (the left pane) will show the directory layout of your project, and that the blank

pane in the middle of the screen will contain the text editor for writing the source files.

In the File menu select “New”, then “C Project”.

In the window that pops up, for the project name

choose something like “led_test_sam7s256” or

“led_test_sam7s16” depending on your MCU. For the

project type, expand the “Makefile project” folder and

select “Empty Project”.

Leave the toolchain as “Other” then click finish to

create the project.

At this stage we could start adding new source files to

the project, but there are a few other things to change

in the project settings in order to make better use of

the Eclipse IDE.

Page 18: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

From the Project menu choose “Properties”. In the new window, expand “C/C++ General” and select

“Paths and Symbols” in the left pane.

Now you should see something similar to that shown below:

In the “Includes” tab, with “GNU C” selected as the language, you need to add the include directory

“C:\yagarto-20121222\lib\gcc\arm-none-eabi\4.7.2\include”. Change this appropriately if you have

a different YAGARTO version or if you installed YAGARTO to a different location.

This is required, because when you include a header file in your code such as “stdint.h”, Eclipse will

try to find it so that it can parse your code correctly and warn you of any errors. For example, if you

don’t tell Eclipse where “stdint.h” is, imagine you try to declare a variable of type “uint32_t” after

including “stdint.h” in a source file.

Eclipse will tell you that the use of uint32_t is an error in your code, which it isn’t, because if you

compiled your code using the YAGARTO toolchain, YAGARTO’s GCC would know where to look for

YAGARTO’s stdint.h file and there would be no problem.

Page 19: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

In the same properties window, now switch to the “Symbols” tab. For similar reasons to before, add

the symbols shown in the following image:

Make sure the symbols have leading and trailing double-underscores, with single-underscores

between words. The purpose of defining these symbols for Eclipse is that the stdint.h header file,

used in this example project, contains “#if”, “#ifdef” and “#ifndef” C preprocessor directives reliant

on these symbols. Without defining them within Eclipse, Eclipse would parse the header files

incorrectly and wrongly notify you of errors in your code (as explained on the previous page).

Click “OK” to apply the settings and return to the main Eclipse window.

Copying Existing Files to the Project

There is one source file which has already been created, which is the AT91SAM7S256.h header file.

Minimize Eclipse, and simply copy the file from the Atmel library you downloaded and paste it into

the example project’s folder in Windows Explorer.

Maximize Eclipse again and expand the project in the Project Explorer. You might need to right-click

on the project and click “Refresh”, but what you should see is that Eclipse has now acknowledged

the header file as being part of the project. Note also that there is now an “Includes” item in the

Project Explorer, which is the result of the previous step.

Page 20: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Writing the Source Files

Creating the “sam7s_startup.s” Assembly Source File

From the File menu select “New”, then “Source File”. Type in “sam7s_startup.s” as the source file’s

name then click finish. In the editor that has opened, you will notice that the source file already

contains a comment based on a template. You can now fill in the rest of the file with the actual

assembly code (for this guide, put all of the files you create into the example project’s folder).

What follows is the completed source file*. I have broken it apart in order to discuss particular

sections, but the entire file is still included here (this format will be used for the rest of the files in

this guide as well). The comments should hopefully make clear most of the functionality.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

/* File: sam7s_startup.s * Created on: 7/12/2012 * Author: Adam Goodwin * * Description: This is startup code which can be used for any of the SAM7S * chips. The startup code sets up the initial exception vectors, calls the * initialisation code specific to the SAM7S model being used, initialises RAM * by copying from ROM or setting to zero, and sets up the stack pointers for * each of the seven processor modes of the ARM7TDMI CPU. The code also embeds * a string to be stored near the beginning of ROM, indicating the software * version. * * To reiterate: this code is suitable for all SAM7S model MCUs, however the * initialisation code called from this file will need to be changed depending * on the SAM7 model in use (e.g. SAM7S16, SAM7S256 etc). The initialisation * code called in this particular file is found in sam7s256_init.c. * * Note: This code is based on the startup code used in the series of articles * "Building Bare-Metal ARM Systems with GNU" by Miro Samek. You can find the * articles at http://www.state-machine.com/resources/articles.php#ARM */ /* Program Status Register bit definitions: * When I_BIT is set, IRQ interrupts are disabled. When F_BIT is set, FIQ * interrupts are disabled. */ .equ I_BIT, 0x80 .equ F_BIT, 0x40 .equ USR_MODE, 0x10 .equ FIQ_MODE, 0x11 .equ IRQ_MODE, 0x12 .equ SVC_MODE, 0x13 .equ ABT_MODE, 0x17 .equ UND_MODE, 0x1B .equ SYS_MODE, 0x1F /* Constant for pre-filling the stack. When debugging, if none of this constant * is visible, it probably means a stack overflow has occurred. */ .equ STACK_FILL, 0xAAAAAAAA

* The files are included verbatim, so there are a lot of comments. Hopefully this will give a better

understanding of what each file is doing.

Page 21: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

As you can see from the comments, this startup file can be used as-is for any of the SAM7S MCUs.

The file contains the first code that will be run every time the MCU is reset; it sets up the CPU and

RAM as required for using the C language, and calls MCU initialisation code located in another file.

***

43 44 45 46 47 48 49

/* Append to the .text section, and specify that 32-bit ARM instructions are * being used. */ .text .arm

The specification of the text section is to do with the linker. This will be explained further, when the

linker script file is discussed, but for now it is enough to say that line 46 is telling the assembler to,

during assembly, append the rest of the file to the .text “section”. The linker will take input sections,

such as .text, from the compiled or assembled object files, and then put them into the output

sections specified by the linker script. These output sections will then be loaded into specific

memory locations.

If you aren’t used to the syntax of assembly code, and want to look up specific aspects of it, the GNU

Assembler documentation is the place to go. The index page is especially useful if you want to look

up a particular item, like the text directive mentioned just previously.

***

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80

/* The following code, being the exception vectors, needs to be linked at the * start of ROM. This isn't necessarily address zero, due to the remapping * operation possible with the SAM7S chips. */ .global _vectors .func _vectors _vectors: /* Vector table * This is used only until RAM is remapped to address zero instead of flash. */ LDR pc, =_reset /* Reset (Branches absolutely to _reset in flash) */ B . /* Undefined Instruction */ B . /* Software Interrupt */ B . /* Prefetch Abort */ B . /* Data Abort */ B . /* Reserved */ B . /* IRQ */ B . /* FIQ */ .size _vectors, . - _vectors .endfunc /* Embed in ROM a short string identifying the program. */ .string "Test LED Flashing Program" /* After including a string, the following ensures the location counter has * its two least significant bits equal to zero. In other words, the * assembly that follows will begin on a 4-byte boundary of ROM * corresponding to the 32-bit word length of the ARM CPU.

Page 22: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

81 82 83 84

*/ .align 2

Lines 62 through to 69 are the first actual pieces of assembly code in the file; the body of the

_vectors function. You’ll notice that these are the ARM exception vectors. Provided the linker script

specifies that the code in this startup file is the first piece of code to put in flash memory, this would

put the exception vectors at address 0x0010 0000 (the beginning of flash). This is exactly what we

want, because when the MCU is powered on or reset, the flash memory is mapped to address

0x0000 0000 as explained in the Memory Remapping and Exception Vectors section of this guide.

Thus, the exception vectors are where the CPU assumes them to be (starting at address 0x0000

0000), and the correct result will occur in the event of an exception.

Note also that the reset exception vector is the only one which branches somewhere useful. The

other exceptions will enter infinite loops if they occur, as their vectors simply contain branches to

themselves. The reset exception vector’s instruction results in loading the absolute address (in flash)

of the _reset function to the program counter. From this a branch to the _reset function’s flash

address (something like 0x0010 003C) will occur. The idea of this is to stop running code from the

remap region as soon as possible, and instead start running code from the actual flash addresses.

This reduces the risk of strange bugs occurring due to address mappings moving about.

One final note in this section of code is about the .align directive. Generally, the align directive aligns

the output code to the specified byte boundary. For example, “.align 4” would align the code to the

next nearest 4-byte boundary.

The necessity of this is due to the string embedded in the code just prior to the use of the .align

directive. Being a 32-bit machine, each instruction for the SAM7S takes up 4 bytes (2 bytes in Thumb

mode). Therefore, for instructions to be fetched correctly, they must start on an address that is a

multiple of 4 (or 2 in Thumb mode). Otherwise, the CPU would fetch the end of one instruction and

the start of the next.

If the string doesn’t take up just the right amount of bytes, then the next instruction would be out of

the required alignment. This is why .align is used.

It is important to note however, that for ARM targets the .align directive does not work in the way

described in the “.align 4” example above. For ARM, “.align n” is equivalent to “.align 2n”. You can

think of the value n as being the number of least-significant-bits in an instruction address that need

to be zero (in order for an address to be pointing at the beginning of an instruction, and not part way

through one).

So where “.align 2” is used in the code above, 22 evaluates to 4, meaning that the code is aligned to a

4-byte boundary – which is what you would want for the 4-byte ARM instructions being used.

***

85 86 87

/* The reset function calls the appropriate initialisation code for the SAM7S * model being used. In this case, the initialisation function sam7s256_init is * called from the file sam7s256_init.c. When the MCU resets, the instruction

Page 23: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

* at address 0x0 performs a branch to this location allowing the reset function * to run before anything else (see the vector table above). */ .func _reset _reset: /* NOTE: This startup file is suitable for any SAM7S chip, as long as the * following branch instruction calls an initialisation function appropriate * for the chip being used, in this case the SAM7S256. */ LDR r0, =_reset /* Pass the reset address as the 1st arg */ LDR r1, =_cstartup /* Pass the return address as the 2nd arg */ MOV lr, r1 /* Store return address in the link register */ LDR sp, =__stack_end__ /* Set the temporary stack pointer */ B sam7s256_init /* Branch to the initialisation function */

This is the beginning of the _reset function. In this first part of the function, the initialisation code

from a C file (discussed soon) is called before returning to the part of the _reset function labelled

“_cstartup”. Because C code is being called, the stack pointer needs to be set so that registers can be

pushed onto the stack. Here the stack pointer is set temporarily to the address “__stack_end__”

which is a symbol defined in the linker script.

Just make sure that the function being called, in this case “sam7s256_init”, is named appropriately

for the chip you’re using. This is the only adjustment you need to make to this assembly file.

***

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

_cstartup: /* NOTE: The following executes upon return from the chip-specific * initialisation code. At this time, the memory remap will have occurred, * and so address 0x00000000 will point to RAM. */ /* Copy the .fastcode section from ROM to RAM. */ LDR r0, =__fastcode_load LDR r1, =__fastcode_start LDR r2, =__fastcode_end 1: CMP r1, r2 LDMLTIA r0!, {r3} STMLTIA r1!, {r3} BLT 1b /* Copy the .data section from ROM to RAM (initialised variables). */ LDR r0, =__data_load LDR r1, =__data_start LDR r2, =_edata 1: CMP r1, r2 LDMLTIA r0!, {r3} STMLTIA r1!, {r3} BLT 1b /* Clear the .bss section to zero (clearing uninitialised variables). */ LDR r1, =__bss_start__ LDR r2, =__bss_end__ MOV r3, #0 1: CMP r1, r2

Page 24: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

137 138 139 140 141 142 143 144 145 146 147 148

STMLTIA r1!, {r3} BLT 1b /* Fill in the .stack section. */ LDR r1, =__stack_start__ LDR r2, =__stack_end__ LDR r3, =STACK_FILL 1: CMP r1, r2 STMLTIA r1!, {r3} BLT 1b

In the above code, sections of flash are copied into RAM. The fastcode section which is copied from

ROM to RAM is simply user-specified pieces of code which are to be run from RAM in order to gain a

performance boost (as loading data from RAM during execution does not require wait states – which

are explained later).

The .data and .bss sections being copied to RAM are to do with variables. Obviously the point of

variables is that they can be written to, as well as read, so they therefore need to be located in RAM.

Initialised variables need to have their initial values copied to the correct location in RAM, and

uninitialized variables need to have their values located in RAM set to zero.

The stack section being filled in RAM is simply for debugging purposes. It doesn’t matter what the

values in the stack are initially, because they will only be read after they have been previously

written. However, if the stack is filled with the same value, then you can quickly tell when a stack

overflow has occurred during debugging. If none of the stack fill value is visible in RAM, it means that

the entire stack has been completely used, and so it’s quite probable that a stack overflow has

occurred.

All of the above symbols with leading and trailing double-underscores are addresses defined in the

linker script. So don’t worry about them for now.

***

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169

/* Initialise the stack pointers for each of the exception modes. The * ARM7TDMI cpu has seven processor modes, each having their own individual * stack pointer. The only exception is that the system and user modes share * a stack pointer, so overall there are six stack pointers to initialise. * Each stack pointer is initialised by switching to a mode then setting * the stack pointer. * * For more information, see the ARM Architecture Reference Manual. */ MSR CPSR_c, #(IRQ_MODE | I_BIT | F_BIT) /* Switch to IRQ mode */ LDR sp, =__irq_stack_top__ /* Set the IRQ stack pointer */ MSR CPSR_c, #(FIQ_MODE | I_BIT | F_BIT) /* Switch to FIQ mode */ LDR sp, =__fiq_stack_top__ /* Set the FIQ stack pointer */ MSR CPSR_c, #(SVC_MODE | I_BIT | F_BIT) /* Switch to SVC mode */ LDR sp, =__svc_stack_top__ /* Set the SVC stack pointer */ MSR CPSR_c, #(ABT_MODE | I_BIT | F_BIT) /* Switch to ABT mode */ LDR sp, =__abt_stack_top__ /* Set the ABT stack pointer */

Page 25: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

170 171 172 173 174 175 176 177 178 179

MSR CPSR_c, #(UND_MODE | I_BIT | F_BIT) /* Switch to UND mode */ LDR sp, =__und_stack_top__ /* Set the UND stack pointer */ /* This is for setting the system and user mode stack pointer. This is the * normal mode of operation. */ MSR CPSR_c, #(SYS_MODE | I_BIT | F_BIT) /* Switch to SYS mode */ LDR sp, =__c_stack_top__ /* Set the C stack pointer */

The ARM7TDMI CPU has seven processor modes, usually switched between when an exception

occurs. Two modes share a stack pointer, whereas for the other modes the stack pointer is

preserved separately for each one. Therefore to initially set up the stack pointers, each mode must

be forcibly switched to in order to assign the appropriate stack pointer address for that mode. The

last mode switched to is the system mode, as it is the normal mode of operation, so it will be

remained in for the rest of the code.

***

180 181 182 183 184 185 186 187 188 189 190 191 192

/* Enter the C code. */ LDR r12, =main MOV lr, pc /* Set the return address */ BX r12 /* The target code can be ARM or THUMB */ /* Cause an exception if main() ever returns. */ SWI 0xFFFFFF .size _reset, . - _reset .endfunc .end

Finally the _reset function ends, with a branch to the location of the main function. After this an

exception is caused, because if the program executes the SWI instruction it means that it must have

returned from the main function – which is not supposed to happen.

Page 26: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Creating the “board.h” Header File

After that last file, this one is nice and simple. The board.h header file contains only a couple of

definitions. These provide the rest of the code with the important details about the board you are

using. You would add to this file any additional information needed for your specific setup.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

// File: dev_board.h // Created on: 10/12/2012 // Author: Adam Goodwin // // Description: This header file contains definitions specific to the board in // use. In this case, the board is the SAM7-H256 from Olimex. #ifndef DEV_BOARD_H #define DEV_BOARD_H // External oscillator frequency which provides the Main Clock, MAINCK, in Hz. #define MAINCK 18432000U // Master Clock frequency (PLL Clock divided by 2) in Hz. #define MCK 47923200U #endif // DEV_BOARD_H

First, notice that in my case I named the filename “dev_board.h” instead of just “board.h”. This is

because I was using a development board from Olimex. If you’re going to be using more than one

board then I suggest you also have multiple board.h files, each with an appropriate name. Create

them in the same way you created the startup assembly file.

Only two constants are defined in board.h. The first, MAINCK, is set to the frequency (in hertz) of the

external crystal connected to the MCU. So for example, with the SAM7-H256 development board I

used, an 18.432MHz crystal oscillator is connected to the MCU to provide the Main Clock signal. (See

the SAM7S datasheet for details on the clocks – I have included a brief rundown in the next section

though.)

The second constant defined in the file is MCK, which is the Master Clock frequency in hertz (the

Master Clock is the CPU clock rate, and also provides the clock signal to peripherals). Unlike the Main

Clock, the Master Clock (to an extent) isn’t defined by external components; it is instead configured

in software. If you intend to choose a different MCK frequency here, you will have to put an

approximate value for now and remember to come back later to change it to the exact value.

This is because you can’t select any frequency you want; you can only select frequencies which are

possible to reach using the available configuration options. The next section explains this further.

Here the nominal Master Clock frequency is about 48MHz; it is up to the code in the next section to

ensure this frequency is achieved. (The external crystal of 18.432MHz with a Master Clock of around

48MHz is a common configuration for SAM7S chips; the 48MHz is a requirement for the use of USB).

So for this example project these two definitions are the only ones required. Make sure you have set

them correctly for the board you’re using.

Page 27: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Creating the “sam7s256_init.c” C Source F i le

This file ties in with the last two in order to finish the configuration stage. Recall from the startup

assembly code that a chip-specific initialisation function was called. This file contains that

initialisation function, which configures things like the CPU clock, and sets up the exception vectors

in RAM. Make sure you name the function as you did in the assembly code, and also name the file

itself correctly for the chip you’re using.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

// File: sam7s256_init.c // Created on: 10/12/2012 // Author: Adam Goodwin // // Description: This initialisation code is specifically for the SAM7S256 model // of MCU. There is one initialisation function which should be called during // the more general (valid for all SAM7S chips) startup assembly code. // // Some aspects of this initialisation code are dependent on the board which the // SAM7S256 chip is mounted on. These dependencies are handled by the board // header file included in this code. In this case, dev_board.h. // // The main purpose of this initialisation code is to set up the SAM7S256 // hardware, such as the processor clocks, as well as to remap the RAM to be // visible at address 0x0 (instead of ROM). // // The startup assembly code which utilises this file is found in // sam7s_startup.s. // // Note: This code is based on the initialisation code used in the series of // articles "Building Bare-Metal ARM Systems with GNU" by Miro Samek. You can // find the articles at http://www.state-machine.com/resources/articles.php#ARM #include <stdint.h> #include "AT91SAM7S256.h" #include "dev_board.h"

Make sure you include the appropriate Atmel header file, and use the correct name for the board

header file.

***

28 29 30 31 32 33 34 35 36 37 38 39 40

// This is the address where RAM starts, as defined by the linker script, i.e. // 0x00200000. extern uint32_t __ram_start; // The first of the following values is an LDR instruction, with the address // offset left blank. The bitwise or of this value with a 12-bit value results // in an instruction that branches to an address relative to the program // counter. The relative location, or offset, from the program counter is given // by the 12-bit value. Note that the value of the program counter, as seen by // an instruction, is the address two instructions ahead of that instruction. static uint32_t const LDR_PC_PC_0x18 = 0xE59FF000U | 0x18; static uint32_t const MAGIC = 0xDEADBEEFU;

Page 28: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

At the end of this file, the SAM7S memory remapping feature will be used. This is in order to have

the exception vector addresses point to RAM instead of flash. Once exceptions start occurring after

the remap has been completed, the vectors in RAM will need to contain executable instructions.

Otherwise, when an exception occurs, the CPU will try to execute whatever random value happens

to be stored in RAM at the exception vector location. For this reason, a constant representing an

executable instruction is created:

The constant LDR_PC_PC_0x18 contains a “load register” ARM instruction, where a data word is

loaded from memory into a register. The destination register in this case is the program counter (PC)

– which therefore causes the program to branch. The CPU will treat the value loaded into the

program counter as an address, and so will branch to the address represented by that value.

The program counter is also used as the base register in this use of the instruction. The base

register’s value, plus some offset, is used as the address to load the data word from.

If you look at the Instruction Set Encoding section of the ARM Architecture Reference Manual, you’ll

see that 0xE59F F000 corresponds to the “load register” instruction described above, with the

program counter serving as both the destination and base registers.

Notice that the lower twelve bits (3 hexadecimal digits) of 0xE59F F000 are zero. This means that the

offset from the base register, which is specified in these lower bits, is also zero. This is why there is a

bitwise-or of 0xE59F F000 and 0x18 when defining LDR_PC_PC_0x18. This makes the offset 0x18,

completing the instruction.

So the instruction represented by LDR_PC_PC_0x18 causes a branch to an absolute address. The

absolute address is stored in memory, at a location relative to the current value of the program

counter. The address to branch to is stored in memory at the current value of the program counter

plus 0x18. The reason for this will become clear towards the end of the file.

***

41 42 43 44 45 46 47 48 49 50 51 52 53 54

// Function: sam7s256_init // // Description: This function is called by the startup assembly code after // initialising the C (user and system mode) stack, but before initialising the // segments in RAM. The function cannot rely on initialisation of any static // variables, because these have not yet been initialised in RAM. // // Parameters: // reset_addr: The address of the _reset function from the startup assembly // code. // return_addr: The address within the _reset function that this function // returns to. void sam7s256_init(void (*reset_addr)(), void (*return_addr)()) {

Here the initialisation function actually begins. Recall that in the assembly code the address of the

reset function and the return address within the reset function were passed as arguments. The

initialisation function configures high-importance peripherals of the MCU.

***

Page 29: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

55 56 57 58 59 60 61 62 63

// Enable the User Reset. // // By default, a low level on the NRST pin will not result in the MCU being // reset. The reset pin needs to be enabled for any attached reset button // to work, as well as to allow resets via JTAG during programming and // debugging. The password of 0xA5 is required to confirm the setting is // intentional. AT91C_BASE_RSTC->RSTC_RMR |= AT91C_RSTC_URSTEN | (0xA5 << 24);

Here the reset pin is enabled. Without this, grounding the NRST pin would not reset the

microcontroller – i.e. reset buttons etc would not work. The register definitions and constants used

here, and throughout the rest of this file, come from the Atmel library header file. For information

about each peripheral and their associated registers consult the SAM7S datasheet.

***

64 65 66 67 68 69 70 71 72 73

// Set up the Embedded Flash Controller. // // Set the Flash Microsecond Cycle Number (FMCN) and the Flash Wait State // (FWS) fields of the Flash Mode Register (FMR). The FMCN bits of the FMR // are set to the number of Master Clock cycles that occur in one // microsecond (500000 is added to ensure rounding up). FWS is set to one, // as required for operation over 30MHz. AT91C_BASE_MC->MC_FMR = ((AT91C_MC_FMCN) & ((MCK + 500000)/1000000 << 16)) | AT91C_MC_FWS_1FWS;

The flash memory in the MCU can only be read from so quickly. Here the flash controller is

configured to have wait states, to stop attempts at accessing the flash from happening faster than

the flash can handle. You’ll only ever need one wait state at most, and don’t need any if you are

going to operate the MCU at below 30MHz.

Here you also need to specify the number of Master Clock cycles that occur in one microsecond, so

that the flash will operate properly.

***

74 75 76 77 78 79 80 81 82

// Disable the Watchdog Timer. // // The Watchdog Timer is enabled by default, with a default period of 16 // seconds. Note that the Watchdog mode register can only be written once // per processor reset. Therefore, if you want to set up the Watchdog Timer, // you must do this here (or elsewhere only if this write to the register is // removed). AT91C_BASE_WDTC->WDTC_WDMR = AT91C_WDTC_WDDIS;

The Watchdog Timer allows the microcontroller to automatically reset via software, if the timer ever

reaches zero. This means that if the program gets stuck in a while loop, for example, after a set

amount of time in the loop the MCU will reset (not if you coded the loop to restart the Watchdog

Timer though). Here we disable the Watchdog Timer.

Page 30: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Now is a good time to summarise the clocks on the SAM7S MCUs:

The MCU’s Clock Generator can output three different clock signals for use by the rest of the MCU.

The first is the Slow Clock (SLCK), which is built-in and runs at 32.768kHz. The second is the Main

Clock (MAINCK), which is determined by your choice of external crystal (here it is 18.432MHz). The

third clock signal is the PLL Clock (PLLCK), which can be configured to a frequency equal to MAINCK

divided and multiplied by some user-defined values.

One of these three sources is chosen to be prescaled, and this prescaled clock signal then provides

the Master Clock (MCK). From here the Master Clock directly provides the Processor Clock (PCK) and

the clock signal for the peripherals. Upon reset, the Slow Clock is the default signal used.

***

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

// Enable the Main Oscillator. // // For an external crystal of 18.432MHz, the datasheet specifies (in the // table of Main Oscillator Characteristics) that the startup time for the // the Main Oscillator will be somewhere between 1ms and 1.4ms. The OSCOUNT // field of the MOR register therefore needs to be set appropriately for // this startup time. OSCOUNT contains the number of times that the Slow // Clock (32768Hz) should go through 8 cycles in order to allow the startup // time to have elapsed. // // For the startup time of 1.4ms, this equates to 45.8752 cycles of the Slow // Clock (0.0014s * 32768Hz). The Main Oscillator will be stable after this // many Slow Clock cycles. Therefore OSCOUNT is set to 6 (45.8752 / 8 // rounded up). The enable bit is also set at this time. AT91C_BASE_PMC->PMC_MOR = ((6 << 8) & AT91C_CKGR_OSCOUNT) | AT91C_CKGR_MOSCEN; // Wait for the startup time to elapse and the Main Oscillator to stabilise. while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS));

To configure the Main Oscillator, the startup time needs to be defined in terms of the Slow Clock,

and the oscillator needs to be enabled. After this the code then busy-waits until the startup time has

elapsed and the oscillator has stabilised. You will need to ensure that the startup time is correct if

you have used a different external crystal.

***

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

// Configure the PLL. // // Set the divider field to 5 to get 3.6864MHz (18432000Hz / 5). Set the // multiplier field to 25 to get 95.8464MHz (3.6864MHz * (25 + 1)). // Similarly to OSCOUNT above, PLLCOUNT must also be calculated. This // depends on the low-pass filter connected to the PLLRC pin. Atmel has a // tool available for calculating startup times, it is available on their // website at http://www.atmel.com/tools/ATMELPLLLFTFILTERCALCULATOR.aspx // With the values R = 1K, C1 = 10nF and C2 = 1nF, Atmel's tool gives a // worst-case startup time of 0.865ms. Therefore, PLLCOUNT should be set to // 29, as 29 Slow Clock ticks will take just over this amount of time // (0.000865s / (1 / 32768Hz)). AT91C_BASE_PMC->PMC_PLLR = (AT91C_CKGR_DIV & 0x05) | (AT91C_CKGR_PLLCOUNT & (29 << 8)) | (AT91C_CKGR_MUL & (25 << 16)); // Wait for the startup time to elapse. while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK));

Page 31: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

119 120 121

// Wait for the Master Clock to become ready. while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));

In the above code the PLL is configured. This consists of setting the divider, and multiplier, to get the

desired output PLL frequency. Note that there is a trick; the multiplier value stored in the register is

actually increased by one before being used to multiply the input signal frequency.

As with the Main Oscillator, a startup time in terms of Slow Clock cycles needs to be defined. To

calculate the startup time, you can use the Excel-based tool provided by Atmel found here. You need

to know the Low-Pass Filter configuration connected to the PLLRC pin in order to use this tool (as

well as the clock signal frequencies you desire).

If you have an 18.432MHz crystal, or if you are even using the same board as the one used in this

guide, you don’t need to worry about changing these settings (provided you are happy with the CPU

running at 48MHz).

***

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

// Configure the Master Clock and CPU clock. // // The CPU clock is simply the Master Clock, with the addition of being able // to be disabled (set to idle mode). The maximum clock speed is 55MHz, so // the Master Clock shall be set to the PLL Clock with a prescaler setting // of 2 to give 47.9232MHz (95.8464MHz / 2). AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; // Wait for the Master Clock to become ready. while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY)); // Set the Master Clock to the PLL Clock now that the prescaler has been set // to 2, meaning that the clock speed won't go over the 55MHz limit. AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK; // Wait for the Master Clock to become ready. while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));

As the final clock settings, the Master Clock (and thus the CPU clock) is configured. You’ll recall from

the dev_board.h header file that the nominal MCK frequency was around 48MHz, but was actually

specified as 47.9232MHz exactly. If you choose to set a different MCK frequency, you’ll have to

figure out what the closest you can get to it is through the PLL configuration and MCK configuration

settings (and by using the Atmel tool linked above). Remember to go back and change the board

header file once you have worked out the exact MCK frequency.

So here, when the Atmel tool is used and its restrictions on PLL multiplying/dividing values are

obeyed, 47.9232MHz is an attainable frequency close enough to 48MHz. It is achieved by PLLCK

being 95.8464MHz, as per above, then setting the prescaler to two for the MCK.

Note that the prescaler has to be set to two before switching the Master Clock source over to the

PLL Clock. Otherwise you’d be operating the CPU at 96MHz for a short amount of time, and the

maximum allowable clock speed specified in the datasheet is 55MHz.

***

Page 32: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179

// Setup the exception vectors in RAM. // // This is done before the memory remap to ensure that there are valid // exception vectors at address 0x0 as soon as the remap is completed. // // First, below, is the primary vector table, located at the very beginning // of RAM. Stored in these vectors are ARM instructions which branch to an // address found 32 bytes forward relative to each instruction. In other // words, the vectors in the primary vector table branch to the address // specified in the corresponding entry of the secondary vector table. Note // that 0x18 is used to specify the relative location of the entries in the // secondary table, not 0x20, as the program counter of the ARM7TDMI is 8 // bytes ahead of the executing instruction when in ARM mode // (0x18 + 0x08 = 0x20 = 32). See the ARM Architecture Reference Manual's // Registers section for more detail. // // You might find it strange that the address of __ram_start is used, and // not its value. This is because of how the linker, ld, creates symbols. // When the linker script defines __ram_start as the starting address of // RAM, it does not actually set the value of __ram_start to the address. // Instead, the value of __ram_start is undefined, and __ram_start's address // is set to the desired value. Therefore, to obtain the starting address of // RAM in C code, you access the __ram_start variable's address, not its // value. (Using the & operator). *(uint32_t volatile *)(&__ram_start + 0x00) = LDR_PC_PC_0x18; *(uint32_t volatile *)(&__ram_start + 0x01) = LDR_PC_PC_0x18; *(uint32_t volatile *)(&__ram_start + 0x02) = LDR_PC_PC_0x18; *(uint32_t volatile *)(&__ram_start + 0x03) = LDR_PC_PC_0x18; *(uint32_t volatile *)(&__ram_start + 0x04) = LDR_PC_PC_0x18; *(uint32_t volatile *)(&__ram_start + 0x05) = MAGIC; *(uint32_t volatile *)(&__ram_start + 0x06) = LDR_PC_PC_0x18; *(uint32_t volatile *)(&__ram_start + 0x07) = LDR_PC_PC_0x18; // Secondary vector table. *(uint32_t volatile *)(&__ram_start + 0x08) = (uint32_t)reset_addr; *(uint32_t volatile *)(&__ram_start + 0x09) = 0x04U; *(uint32_t volatile *)(&__ram_start + 0x0A) = 0x08U; *(uint32_t volatile *)(&__ram_start + 0x0B) = 0x0CU; *(uint32_t volatile *)(&__ram_start + 0x0C) = 0x10U; *(uint32_t volatile *)(&__ram_start + 0x0D) = 0x14U; *(uint32_t volatile *)(&__ram_start + 0x0E) = 0x18U; *(uint32_t volatile *)(&__ram_start + 0x0F) = 0x1CU;

This is near the end of the initialisation code. Line 161 through to line 176 is the setup of the

exception vectors in RAM.

To start with, look at the primary vector table. Here the ARM instruction discussed earlier, defined as

LDR_PC_PC_0x18, is being assigned to the first RAM addresses (with the exception of 0x14, but we’ll

get to that later). So we are filling in the exception vectors in RAM with legitimate ARM instructions,

as originally intended.

Now it’s time to understand why the instruction is the one that it is. Recall that LDR_PC_PC_0x18

causes a branch to an absolute address. The absolute address to branch to is stored in memory, at a

location relative to the current value of the program counter. The relative location is the program

counter plus 0x18.

First of all, why is the address stored at the program counter plus 0x18? Well, first you need to

understand something about the program counter and the ARM7TDMI pipeline:

Page 33: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Each instruction actually completes in three stages. First the instruction is fetched, then decoded,

then executed. When one instruction is being executed, the next is being decoded, and the next

again is being fetched. So by the time an instruction is being executed, the program counter is

actually pointing two instructions ahead – at the one being fetched. Each instruction (in ARM mode)

is 4 bytes long, and so at any given time the program counter is actually at the address of the current

instruction plus 8.

With this in mind, recall once again that the LDR_PC_PC_0x18 instruction requests a data word from

memory – at the address of PC + 0x18, As we’ve just learned, PC + 0x18 will equate to the address of

the current instruction plus 0x20 (because 0x18 + 0x08 = 0x20).

Now, how big is the vector table? The vector table contains eight 32-bit vectors/addresses. This

equates to 32 bytes which in hexadecimal is 0x20.

So picture the secondary vector table positioned in memory right after the first. To reach the first

entry in the secondary vector table, if you’re at the first entry in the primary table, you would take

the address of the current instruction and add the size of the vector table – i.e. 32 or 0x20.

What this all means is that the LDR_PC_PC_0x18 instruction, which is located in each of the RAM

exception vectors, branches to an address stored in the corresponding entry of the secondary vector

table.

In other words, through software you can specify an exception handler by storing its address in the

secondary vector table – located in RAM. Then, when the exception occurs and the CPU branches to

the exception vector, it will load this address from the secondary vector table into the program

counter. The CPU will then branch again to your exception handler.

If you look at the secondary vector table, you can see that all of the addresses are just the original

exception vectors – except for the reset exception. This is to cause infinite loops to result if any of

the unhandled exceptions occur.

The reset exception differs because you do not want a reset to result in an infinite loop. Instead the

CPU should branch to the address of the actual reset function in flash. The infinite loop wouldn’t

occur with a hardware reset via the NRST pin; however it is possible for the infinite loop to occur if

you implement it. If a software reset of the CPU is performed, and a software reset of the

peripherals isn’t performed, this would result in a reset which would use the RAM exception vector

with the infinite loop (a peripheral reset or loss of power is what undoes the remapping to RAM

operation, so not performing one is what allows the RAM reset exception vector to be used).

***

180 181 182 183 184 185 186 187 188 189

// Remap the memory. // // This is operated by a toggle only, so remapping needs to be performed // only if it has not been done already. You might not think this a problem, // as upon a reset the remap region at address 0x0 points to flash. However, // if a soft reset is performed, e.g. during debugging, there is no // guarantee that this assignment of flash to the remap region has occurred. // // To check the state of the remap region, we see if address 0x14 contains // MAGIC. If it does, then the remap region is already pointing to RAM. If

Page 34: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

190 191 192 193 194 195 196 197

// it doesn't, then the remap region is pointing to ROM, and a remap toggle // needs to occur. if (MAGIC != (*(uint32_t volatile *)0x14)) { AT91C_BASE_MC->MC_RCR = 1; } }

On the subject of remapping, the last action performed by the initialisation function is to remap the

memory, so that address 0x0000 0000 refers to RAM and not flash; thus enabling the newly

programmed exception vectors. The remap operation is a toggle only, so the code needs to

somehow check whether the remap region of memory is currently mapped to flash or RAM.

This is why the unused exception vector, 0x14, was set to the value MAGIC (which is equal to

0xDEADBEEF). If the remap region is set to RAM, writing 0x14 will be successful. If the remap region

is set to flash, the write to 0x14 will have no effect (flash is ROM). To decide whether or not to

perform a remap, address 0x14 is read. If its value is 0xDEADBEEF, then the remap doesn’t need to

occur as RAM is already mapped to address 0x0000 0000.

***

There is something that should be mentioned regarding this file. For a line such as the following:

162 *(uint32_t volatile *)(&__ram_start + 0x01) = LDR_PC_PC_0x18;

It might not be entirely clear, to those unfamiliar, how exactly the four-byte value of

LDR_PC_PC_0x18 is being written to address 0x0020 0004. This is what must be happening, as the

line is supposed to be writing the second exception vector in RAM (which would be the second four

bytes of RAM).

The right hand side of the assignment is straightforward, but what about the left? Well, the first

segment, (uint32_t volatile *), casts the expression (&__ram_start + 0x01) to a pointer to a volatile

uint32_t. The first asterisk on the line (on the far left) is then dereferencing this pointer, so that the

assignment is storing the value of LDR_PC_PC_0x18 to the address pointed to by the pointer

(uint32_t volatile *)(&__ram_start + 0x01).

This still leaves some confusion about how the (&__ram_start + 0x01) portion evaluates to 0x0020

0004 (this expression is what gets used as a pointer to the second exception vector in RAM, so it

must necessarily evaluate to 0x0020 0004).

Obviously &__ram_start must be 0x0020 0000 based on its name alone, but how is this so? On top

of that, &__ram_start being 0x0020 0000 would imply that the constant of “0x01” is somehow equal

to 0x04, in order to bring the result of the expression to 0x0020 0004. How can that be!?

To understand all of this we need to go back to line 30:

28 29 30

// This is the address where RAM starts, as defined by the linker script, i.e. // 0x00200000. extern uint32_t __ram_start;

Page 35: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

The variable __ram_start is declared as extern, meaning that the actual variable definition is

somewhere else. The “somewhere else” is in the linker script, but this is where things get a bit

strange.

When the linker script defines the symbol __ram_start as the address of the start of RAM, it doesn’t

make a variable whose value is 0x0020 0000. Instead, __ram_start is just a symbol at address 0x0020

0000. This is just how linker symbols work.

So in your C code, when you want to get the 0x0020 0000 value out of __ram_start, what you

actually need to do is get its address, not its value, hence the leading ampersand.

***

So that clears up half of our problem. We now know how &__ram_start equals 0x0020 0000.

But it is still extremely strange that, in the vector tables, each exception vector is accessed by adding

a value, from 0 to 15, to the base &__ram_start address. After all, each exception vector is four

bytes long, so shouldn’t each exception vector after the first be accessed by adding multiples of four

to the &__ram_start address?

Here’s a few of the lines again:

161 162 163

*(uint32_t volatile *)(&__ram_start + 0x00) = LDR_PC_PC_0x18; *(uint32_t volatile *)(&__ram_start + 0x01) = LDR_PC_PC_0x18; *(uint32_t volatile *)(&__ram_start + 0x02) = LDR_PC_PC_0x18;

For example, take a look at line 162. When 0x01 is added to &__ram_start, the idea is to get an

address four bytes further through RAM (to get to the next exception vector). But how does that

work when one is being added to &__ram_start, and not four?

The answer is that the addition of 0x01 to a pointer does not mean “add one to the address”. What

it actually means is “add one of the pointer’s value’s type’s size in bytes to the address”.

To be clearer: When you add to a pointer to change the address, it’s the type that the pointer points

to which determines how much the address changes by.

So when __ram_start is a uint32_t, adding 0x01 to &__ram_start increases the address by one times

the size (in bytes) of a uint32_t. In other words, because the size of a uint32_t is four bytes, the

result of &__ram_start + 0x01 is as intended – the address is that of &__ram_start increased by

four.

The reason why the operation works like this is in order to make addressing elements of an array

safer in C. If you have the base address of an array, and add an integer value to it, you move the

address forward by that many array elements. It stops you from accidentally moving part way

through an array element.

So you (hopefully) now have a full understanding of how the code for those vector tables actually

works.

Page 36: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

***

I expect these first code files may have left you confused, but it’s to be expected with an MCU so

much more complicated than you might be used to – especially as you can often get around dealing

with this low level configuration with simpler MCUs. With a SAM7S there’s not really any avoiding it.

The rest of the code files are much simpler though, and should be much more in line with what you

are used to.

Page 37: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Creating the “main.c” C Source Fi le

We are now at the stage of actually writing the LED-flashing program for the MCU. The program is

very simple, here it is:

1 2 3 4 5 6 7 8 9 10 11 12 13

// File: main.c // Created on: 7/12/2012 // Author: Adam Goodwin // // Description: This is a basic test program which is to flash LEDs connected // to a SAM7S256. The program simply drives the specified output pins // alternately high and low, making it easy to change the pins to suit your // board configuration. #include <stdint.h> #include "AT91SAM7S256.h" #include "delay.h"

The delay.h header file is coming up next; just know that it allows you to call a delay function for a

specified number of milliseconds. Remember to include the right Atmel header file.

***

14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

int main(void) { uint8_t i = 0; // The pins connected to the LEDs to be toggled. uint32_t pins = AT91C_PIO_PA5 | AT91C_PIO_PA6 | AT91C_PIO_PA7 | AT91C_PIO_PA8 | AT91C_PIO_PA9 | AT91C_PIO_PA10 | AT91C_PIO_PA11 | AT91C_PIO_PA12; // Enable the peripheral clock. AT91C_BASE_PMC->PMC_PCER |= 0x1 << AT91C_ID_PIOA; // Set the pins as GPIO and not peripherals. AT91C_BASE_PIOA->PIO_PER = pins; // Now set the pins as output. AT91C_BASE_PIOA->PIO_OER = pins; // Initialise the delay module. delay_init();

The configuration for the LEDs here should really be in another module, but for such a simple

program this is ok. The pins variable is used to define which input/output (IO) pins have LEDs

attached so change this to suit your board.

Enabling the IO is quite simple. First the peripheral clock is enabled for the Parallel IO controller.

Then the Parallel IO controller is configured to specify that the LED pins are to be user-controlled IO,

and not peripheral-controlled IO. Finally, the pins are set as outputs and not inputs.

You’ll also notice that the delay module is initialised here. The contents of this initialisation function

will be revealed in the next section.

Page 38: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

***

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

// Flash the LEDs quickly three times. for (i = 0; i < 3; i++) { // Turn the LEDs off. AT91C_BASE_PIOA->PIO_CODR = pins; // Wait 200ms. delay(200); // Turn the LEDs on. AT91C_BASE_PIOA->PIO_SODR = pins; // Wait 200ms. delay(200); } // Now flash the LEDs slowly indefinitely. while (1) { // Turn the LEDs off. AT91C_BASE_PIOA->PIO_CODR = pins; // Wait 1000ms. delay(1000); // Turn the LEDs on. AT91C_BASE_PIOA->PIO_SODR = pins; // Wait 1000ms. delay(1000); } return 0; }

Next the program does a quick three flashes of the LEDs using a for loop and the i variable declared

at the start of the main function.

Finally the program just loops indefinitely: Turning off the LEDs, waiting for one second, turning the

LEDs back on, and then waiting again before repeating.

To learn more about what the register settings used throughout this file are, look up the peripherals

in the SAM7S datasheet and search for the register field names (e.g. CODR or SODR). You can also

put the cursor on a constant in Eclipse’s text editor (for example PIO_CODR), then right click on it

and choose “Open Declaration” to have Eclipse go to the source of the constant or variable.

Page 39: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Creating the Delay Module

The delay module is what is used by main.c to control the timing of the LEDs’ flashing. The module

makes use of one of the SAM7S MCU’s Timer Counters and is comprised of a header file (delay.h)

and a C source file (delay.c). We will first look at the header file.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

// File: delay.h // Created on: 19/12/2012 // Author: Adam Goodwin // // Description: This delay module provides a means to perform basic busy-wait // loops using Timer Counter 0. #ifndef DELAY_H #define DELAY_H #include <stdint.h> #define MAX_DELAY_MS 1000U #define TC0_MCK_DIV 1024U // Function: delay_init // // Description: This function configures the timer counter to allow the timing // of delays to be accurately measured. void delay_init(void); // Function: delay // // Description: This function implements a delay by busy-waiting for the // specified number of milliseconds. The delay is capped to MAX_DELAY_MS. // // Parameters: // milliseconds: The duration of the delay in milliseconds. void delay(uint16_t milliseconds); #endif // DELAY_H

The header file defines two constants and declares two functions. The constants are the maximum

delay that will be allowed, and the division of MCK that will be used for timing.

The functions are for initialisation, and for causing a delay to occur.

Page 40: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Now we will look at the implementation of the module in delay.c:

1 2 3 4 5 6 7 8 9 10 11 12

// File: delay.c // Created on: 19/12/2012 // Author: Adam Goodwin // // Description: The implementation of the module declared in delay.h. #include <stdint.h> #include "delay.h" #include "AT91SAM7S256.h" #include "dev_board.h"

Here we have the usual includes. You know the drill – just make sure they’re named correctly for

your setup.

***

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

void delay_init(void) { // Enable the Timer Counter 0 peripheral clock. AT91C_BASE_PMC->PMC_PCER |= 0x1 << AT91C_ID_TC0; // Set the input clock source of the timer counter as the Master Clock // divided by 1024. Set the timer counter to stop and disable once the // compare value is reached. Select the waveform to count up without // resetting at the compare value. Also enable waveform mode. AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK | AT91C_TC_CPCSTOP | AT91C_TC_CPCDIS | AT91C_TC_WAVESEL_UP | AT91C_TC_WAVE; }

The delay_init function needs to be called before you try to use a delay. This initialises the Timer

Counter for use by the delay function.

First, as with the Parallel IO Controller in the main.c file, the Timer Counter peripheral clock is

enabled. Being a timer, Timer Counter Zero (TC0) needs access to the Master Clock signal if it is to

work.

Line 22 onwards is simply setting the operation of TC0 to a configuration appropriate for our

purposes. If you want to know more, use the SAM7S datasheet to look at the Timer Counter

documentation (in the Peripherals section). Just look at what each of the five Timer Counter Channel

Mode Register (TC_CMR) fields (set in the above code) do for the operation of the Timer Counter.

***

Page 41: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

void delay(uint16_t milliseconds) { // Cap the delay. if (milliseconds > MAX_DELAY_MS) { milliseconds = MAX_DELAY_MS; } // Set the Timer Counter 0 compare value. This depends on the Master Clock // rate, the divider setting from the delay initialisation function, and the // specified delay in milliseconds. AT91C_BASE_TC0->TC_RC = ((MCK / TC0_MCK_DIV) * milliseconds) / 1000U; // Enable the Timer Counter 0 clock, and trigger it (reset the counter and // start the clock). AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // Wait until an RC Compare has occurred. This means the timer has reached // its target and so the delay is finished. while (!(AT91C_BASE_TC0->TC_SR & AT91C_TC_CPCS)); }

The delay function itself begins by capping the user-specified delay to the maximum duration

defined in the header file.

Line 40 sets the TC0 Compare Value, which is the value that the counter will have to reach before

the delay is ended. The specified delay in milliseconds has to be converted into ticks of the timer, so

this therefore is dependent on the Master Clock speed and division used.

If you’ve used a different MCK speed than that assumed in this guide, you will have to rewrite line 40

to calculate the delay duration correctly. Note that if this means changing the division of the Master

Clock used, you will also have to change the constant defined in delay.h and the constant used in

delay_init (which is currently AT91C_TC_CLKS_TIMER_DIV5_CLOCK).

Another thing to keep in mind is how you use parentheses to ensure that the calculation never

results in an overflow, which may also require a different MAX_DELAY_MS value. With all of the

multiplying and dividing you can get some quite large numbers – or lose some accuracy during

divisions. Careful use of ‘U’ characters, appended to numerical constants, is also advised to ensure

that they are interpreted as unsigned values.

The remaining two lines of code (44 and 48) simply start the timer, then busy-wait until the compare

value is reached (as indicated by TC0’s Channel Status Register).

***

This concludes the discussion of the source files of the example project. What remains now is to

discuss the files needed for building the project and getting the code running on the MCU.

Page 42: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Scripts and Configuration Files

In this section the remainder of the project files are created. These files are what get the source

code running on the MCU. They are:

- led_test_sam7s256.ld*

- run_openocd.bat

- uc-sam7-usbjtag.cfg

- makefile*

Creating the “led_test_sam7s256.ld” Linker Script

When you create the new file in Eclipse, make sure you name the linker script appropriately for the

SAM7S chip you’re using.

The purpose of the linker script is to define how to create the LED-flashing program’s executable file.

When you compile your source code, you are left with object files – one for every source code file.

These need to be combined together, along with any required library object code, to give the final

executable as one individual file. This is what the GNU Linker does.

There is another important aspect to the linker as well. Object code files, due to being separate files

with no idea of how they are to be connected by the linker, need to leave some addresses

unspecified. The object code can’t know where all instructions are going to be located in memory; it

depends on where the linker puts each file in the final executable.

So it is the linker’s job to fill in these remaining addresses, and this linker script tells it how.

When looking through the linker script, to find out more about any particular aspect of the script,

you should go to the GNU Linker documentation.

*These files are based on files from Miro Samek’s tutorial (Samek, 2007).

Page 43: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

/* File: led_test_sam7s256.ld * Created on: 12/12/2012 * Author: Adam Goodwin * * Description: This linker script is specifically for the SAM7S256 MCU, where * the program to be loaded is the led_test_sam7s256 program from this project. * * The linker script takes input sections (such as .text, .rodata etc) from * compiled and assembled code, and organises them in output sections (such as * .reset, .ramvect etc). The organisation of the output sections, and the * resulting linker output file, determines the layout of the data in the MCU's * memories (RAM and ROM/flash). * * Note: This script is based on the linker script used in the series of * articles "Building Bare-Metal ARM Systems with GNU" by Miro Samek. You can * find the articles at http://www.state-machine.com/resources/articles.php#ARM */ OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_vectors)

These first few lines set some information for the linker: The endianness of the executable, and the

architecture of the CPU. The entry point of the program is also specified (it is the _vectors function

from the startup assembly code).

***

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

/* Memory map of the AT91SAM7S256. Simply change this to adapt the linker script * for loading the same program to a different SAM7S chip. */ MEMORY { ROM (rx) : ORIGIN = 0x00100000, LENGTH = 256k RAM (rwx) : ORIGIN = 0x00200000, LENGTH = 64k } /* The sizes of the stacks used by the application. */ C_STACK_SIZE = 512; IRQ_STACK_SIZE = 0; FIQ_STACK_SIZE = 0; SVC_STACK_SIZE = 0; ABT_STACK_SIZE = 0; UND_STACK_SIZE = 0;

Here the MEMORY command is telling the linker what areas of memory are available for use. It

specifies attributes (such as readable, writable etc) describing the memory regions, it specifies

where the memory regions are located, and it specifies what sizes the memory regions are.

ROM (which refers to the internal flash) and RAM (which refers to the internal SRAM) are the

memory regions that we are telling the linker about. You could also specify the remap region,

although it’s not needed for this guide.

Potentially, this is the only part of the linker script that you’ll need to change. If you’re not using a

SAM7S256 then you’ll need to specify the correct lengths of the ROM and RAM for your chip.

Page 44: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

This area of the script also contains the stack size definitions. The C stack size, (for the stack shared

between the user and system processor modes,) is arbitrarily set to 512 bytes. The remaining stack

lengths are set to zero, as the example code doesn’t do anything in these processor modes other

than enter infinite loops – so a stack isn’t needed.

***

40 41

SECTIONS {

Now to explain a little bit about sections: When source code is compiled or assembled to object

code, the object code is organised into sections. For example, executable code is placed in the .text

section, and initialised global variables are placed in the .data section.

The linker takes these input sections from object code and places them into output sections. The

output sections are then organised into memory. This all happens in the SECTIONS command’s block,

which is enclosed by curly braces.

***

42 43 44 45 46 47

.reset : { *startup.o (.text) /* Startup code from assembly startup file */ . = ALIGN(0x4); } >ROM

The first output section is the .reset section. Take a look at line 44: Here all .text input sections are

placed in the .reset output section, provided they come from an object file with “startup” at the end

of the filename. Hence the startup code is placed into memory before anything else – as long as the

startup file is named correctly. This is what we want because the startup code begins with the

exception vectors – so it needs to be the first thing in flash for the program to run. The use of

“>ROM” after the .reset section is what places it in the location of flash.

On line 45, the dot or “location counter” is aligned to a 4-byte word boundary. Recall from the

assembly code that a similar function was performed there. This is the same, except you might also

recall that the assembler required “.align 2” for ARM targets, which is not the case here.

The dot symbol in the linker script refers to the byte offset from the start of the current containing

object. So here the containing object is the .reset output section, which is in turn contained within

the SECTIONS command. Whenever you place some data in a containing object, the location counter

for that object is advanced by the number of bytes of data you placed.

So let’s say that the .text section of sam7s_startup.o is 101 bytes long. After placement in the .reset

output section, the .reset section’s location counter will be advanced by 101 (setting it to 101,

because it was at zero to begin with).

After the alignment in line 45, the location counter within the .reset output section will be set to

104. Then, because the .reset section has been placed in the SECTIONS command, the location

Page 45: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

counter for the SECTIONS command will also advance by 104 (0x68). However, because .reset was

placed in ROM (with starting address 0x0010 0000), the location counter for the SECTIONS command

would actually advance to 0x0010 0068.

You can also assign values to the location counter to set it directly, in order to skip forward or

backward a number of bytes (this is how the ALIGN is performed). You can’t skip the location

counter backwards within an output section though, or backwards outside of an output section

either – in this case only if it will cause an overlap of data (see the Location Counter page of the

linker documentation for more details).

This ability to skip the location counter backwards (when outside of an output section) is how you

are able to place something in RAM with “>RAM”, then still go back to place something in ROM with

“>ROM”.

***

48 49 50 51 52 53

.ramvect : { __ram_start = .; /* Used for vectors remapped to RAM */ . = 0x40; } >RAM

The next output section is .ramvect, which you’ll notice is placed in RAM (with “>RAM”). No data of

any use is included here. Instead, the location counter is simply skipped to 0x40 to artificially make

the .ramvect section 0x40 bytes long – allowing space for the exception vectors when they are

written to RAM in the initialisation C code.

You will notice that the __ram_start symbol, used in the initialisation code, is defined here on line

50. It is set to the location counter at the beginning of the .ramvect section, which would be 0x0 at

this time. This means that __ram_start refers to the address offset 0x0 bytes forward from the

address of the .ramvect section.

In other words, __ram_start is a symbol at the same address as the start of the .ramvect section.

Because .ramvect is the first section placed in RAM, its start address is the same as the start address

of RAM. Therefore, __ram_start is at the same address as the start of RAM, which is 0x0020 0000.

This is exactly in agreement with how __ram_start was used in the initialisation C code.

***

54 55 56 57 58 59 60 61 62 63 64 65 66

.fastcode : { __fastcode_load = LOADADDR(.fastcode); __fastcode_start = .; * (.glue_7) * (.glue_7t) * (.text.fastcode) /* Add other code modules to be executed from RAM here. There are two * options. Using the following example C function, the two methods are * below: *

Page 46: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

* int example(int a) * { * return a; * } * * Method 1. You can set the GCC compiler option -ffunction-sections, * which allows you to specify individual functions here in the linker * script. So to put the example function in RAM, you would put this * line in the linker script within this .fastcode output section: * * * (.text.example) * * Method 2. You can use GCC function attributes. These can be placed * after a function declaration (on the same line before the semicolon), * or before a function definition on the above line. Like so: * * int example(int a) __attribute__ ((section(".text.fastcode"))); * * Or: * * __attribute__ ((section (".text.fastcode"))) * int example(int a) * { * return a; * } * * Note that for the second method the appropriate line is already in * the .fastcode section of this linker script. So the choice depends on * whether you want to edit the linker script or the source code file. */ . = ALIGN(0x4); __fastcode_end = .; } >RAM AT>ROM

With the .fastcode section comes a few new concepts. Notice that following the section is “>RAM

AT>ROM”.

It’s time to introduce the idea of the Virtual Memory Address (VMA) and the Load Memory Address

(LMA). VMAs are what we’ve been dealing with previously; when a section is followed by something

like “>RAM” or “>ROM” only.

The VMAs are the addresses given to sections. So when the code is run, the CPU is looking to the

VMAs for the location of instructions. But the addresses that are given to the sections don’t

necessarily have to be where the data is actually loaded.

The LMAs are the address to which the data is loaded, and these are specified with the AT> keyword.

Like this: “AT>ROM”.

So with the .fastcode section: If you’ve told the CPU that the code is in RAM using “>RAM”, but

you’ve actually loaded it to ROM by appending “AT>ROM”, then you need to copy the code from

ROM to where you said it would be (in RAM) before the CPU tries to run it. If you recall, that’s

exactly what was done in the startup assembly code; any data that needed to be in RAM was copied

over from ROM.

Page 47: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

The only question that remains is how the assembly code knows where to copy the data from, and

where to put the copied data. This is accomplished in the .fastcode section with the

__fastcode_load, __fastcode_start and __fastcode_end symbols.

The latter two are just like the __ram_start symbol. These mark the start and end of the RAM

addresses where the “fastcode” should go. It’s the __fastcode_load symbol that’s different. Rather

than using the location counter on its own, which would give a relative VMA, __fastcode_load is

assigned to be “LOADADDR(.fastcode)”. As the function name “LOADADDR” suggests, this returns

the LMA of the specified section.

So these symbols give to the assembly code the area in RAM that the data needs to be copied to, as

well as the location in ROM of the first piece of data to be copied. This is enough information to get

the job done.

The only remaining thing that needs to be said about the .fastcode section is regarding how to

actually specify the code to be placed in the section – in order for faster execution from RAM (don’t

worry about what the .glue_7 and .glue_7t input sections mean for now). The comments in the

linker script should do a good enough job of explaining the two methods of placing code in the

.fastcode section.

***

102 103 104 105 106 107 108 109 110 111 112 113 114 115

.text : { . = ALIGN(0x4); * (.text) /* .text sections (code) */ * (.text*) * (.rodata) /* .rodata sections (constants, strings, etc) */ * (.rodata*) * (.glue_7) /* "Glue" ARM to Thumb (already in .fastcode) */ * (.glue_7t) /* "Glue" Thumb to ARM (already in .fastcode) */ . = ALIGN(0x4); _etext = .; /* Global symbol at end of code */ } >ROM

The text section is fairly straightforward. The .text, .rodata and .glue_7 sections from any input file

(as specified by the asterisk wildcard character) are placed in the .text output section. As mentioned

before, .text input sections are used for code. The remaining input sections (.rodata and .glue_7) are

for read-only data and interfacing ARM/Thumb code respectively.

You might notice that there are some double-ups: We’ve already placed the startup file’s .text

section in the .reset output section – now it will be getting placed again here. The .glue_7 input

sections were also already placed in the .fastcode output section...

What happens is that the first placement is the one that takes priority. Any repeated input sections

are ignored and are not included more than once.

You might also be unsure about how to know if you’ve included all of the input sections. How do you

know that there isn’t an input section that’s been missed? These are called “Orphan Sections”. You

Page 48: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

don’t need to worry too much about these, because the linker will decide how to place them

automatically.

***

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142

.data : { __data_load = LOADADDR(.data); __data_start = .; * (.data) /* .data sections (initialised variables) */ * (.data*) . = ALIGN(0x4); _edata = .; } >RAM AT>ROM .bss : { __bss_start__ = .; * (.bss) /* .bss sections (uninitialised variables) */ * (.bss*) * (COMMON) . = ALIGN(0x4); _ebss = .; __bss_end__ = .; } >RAM PROVIDE(end = _ebss); PROVIDE(_end = _ebss); PROVIDE(__end__ = _ebss);

The above part of the script is just more of the same, anything you don’t recognise at this point (e.g.

COMMON or PROVIDE) can be looked up in the linker documentation’s index if you’re interested.

***

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

.stack : { __stack_start__ = .; . += IRQ_STACK_SIZE; . = ALIGN(0x4); __irq_stack_top__ = .; . += FIQ_STACK_SIZE; . = ALIGN(0x4); __fiq_stack_top__ = .; . += SVC_STACK_SIZE; . = ALIGN(0x4); __svc_stack_top__ = .; . += ABT_STACK_SIZE; . = ALIGN(0x4); __abt_stack_top__ = .; . += UND_STACK_SIZE; . = ALIGN(0x4); __und_stack_top__ = .;

Page 49: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

. += C_STACK_SIZE; . = ALIGN(0x4); __c_stack_top__ = .; __stack_end__ = .; } >RAM /* Remove information from the standard libraries. */ /DISCARD/ : { libc.a (*) libm.a (*) libgcc.a (*) } }

To conclude the linker script, symbols are defined so that the startup assembly code knows where

the stacks are. Finally, input sections from the standard libraries are discarded to save on space.

Remember, if there are any repeats then the first placement takes priority – so the discarding won’t

remove any sections of the standard libraries that are actually being used.

Page 50: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Creating the “run_openocd.bat” Batch Fi le

This batch file is nothing too complicated once you get past the syntax. It is only a part of the project

to make running OpenOCD more convenient. If you run the batch file it will open an instance of

OpenOCD in a new Command Prompt window, if there isn’t already an instance running.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

@echo off REM File: run_openocd.bat REM Created on: 14/12/12 REM Author: Adam Goodwin REM REM Description: This batch file runs OpenOCD if it isn't already running. REM REM You must specify the OpenOCD executable, the path to the executable, and the REM configuration file to be used. REM Set the OpenOCD executable name here: set OCD=openocd-x64-0.6.1.exe REM Set the absolute path to the OpenOCD executable here: set OCDP=C:\Program Files\openocd-0.6.1\bin-x64\ REM Set the configuration file here: set OCD_CFG=uc-sam7-usbjtag.cfg REM Check if OpenOCD is running, storing the count of how many open instances of REM OpenOCD there are. FOR /F %%A IN ('tasklist /FI "IMAGENAME eq %OCD%" /NH ^| find /C "%OCD%"') DO ( set COUNT=%%A ) REM Run OpenOCD if necessary: if %COUNT% EQU 0 ( echo OpenOCD is not running. Starting OpenOCD... start "OpenOCD" /D . /MIN "%OCDP%%OCD%" "-f" "%OCD_CFG%" ) ELSE ( echo There is already %COUNT% instance^(s^) of OpenOCD running. )

The only changes you might need to make are to lines 12 and 15, where the OpenOCD executable

name and installation path are specified. Line 18 contains the configuration file OpenOCD is to use,

which you’ll only need to change if you’re not using an ECE USB-JTAG adapter (this configuration file

is discussed in the next section).

The remainder of the batch file first uses the “tasklist” and “find” programs to calculate the number

of running OpenOCD instances. Finally, the batch file uses the “start” command to run OpenOCD

(minimized) in a new window. The configuration file from earlier is specified as an argument to

OpenOCD.

If you have a Command Prompt open in your project directory, rather than typing something like

start “OpenOCD” /D . “C:\Program Files\openocd-0.6.1\bin-x64\openocd-x64-

0.6.1.exe” –f uc-sam7-usbjtag.cfg to run OpenOCD, you can instead just type

run_openocd.

Page 51: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Creating the “uc-sam7-usbjtag.cfg” OpenOCD Configuration Fi le

Here we write the OpenOCD configuration file, which was specified in the run_openocd.bat batch

file just previously. OpenOCD uses the configuration file to correctly communicate with the JTAG

adapter being used, as well as to ensure correct debugging of the target CPU.

The OpenOCD User’s Guide is the place to go for help on OpenOCD specifics. If you’ve downloaded

OpenOCD as per the instructions at the beginning of this guide you should find that there’s a copy

included in the zipped folder you downloaded.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

# File: uc-sam7-usbjtag.cfg # Created on: 28/11/2012 # Author: Adam Goodwin # # Description: OpenOCD configuration for the University of Canterbury ECE # USB-JTAG interface with a SAM7S target. # # Note that the adapter speed is set to 3kHz, which is quite slow. This is # done because the configuration file is making no assumptions about the clock # speed of the MCU. Upon reset, the MCU's clock is set to the Slow Clock, which # is 32.768kHz. According to the OpenOCD documentation, the maximum adapter # clock speed is one sixth of the MCU's clock speed for a SAM7S. Therefore 3kHz # has been chosen as it is safely under this limit. If the MCU's clock speed is # increased during its initialisation code, or via OpenOCD, then after this time # the adapter speed can also be increased to match the new limit. # # For a better understanding, imagine that the MCU has been reset and halted # before its initialisation code has had the chance to bring the Master Clock up # to a high speed. Or perhaps the program on the chip simply doesn't change the # source of the Master Clock to something other than the Slow Clock. In this # situation, if OpenOCD is configured to an adapter speed greater than one-sixth # of the Slow Clock, control via JTAG won't work. Therefore the worst-case # adapter speed of one-sixth of the Slow Clock is used. Again, it can be # increased once it becomes possible. # # For the same reason, the adapter speed must be set to 3kHz before using the # command "reset init". This command attempts to interact with the MCU while it # is running on the Slow Clock. # # See ecewiki.elec.canterbury.ac.nz/mediawiki/index.php/OpenOCD_configuration # and the other pages of the wiki for more information.

Remember to name the file appropriately for its use (and to match the filename to that expected by

the run_openocd.bat batch file).

The initial comment in the configuration script explains some of the details about clock speeds. The

gist is that the JTAG clock can be at most one-sixth of the CPU clock at any time. Because the MCU

runs on the Slow Clock (32.768kHz) upon reset, the OpenOCD configuration has to assume this

worst-case speed when it connects to the MCU. Once the processor is known to be at a higher

speed, OpenOCD can increase the JTAG clock frequency accordingly.

***

Page 52: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

34 35 36 37 38

# This section configures OpenOCD for using the university's USB-JTAG adapter. interface ft2232 ft2232_layout usbjtag ft2232_vid_pid 0x0403 0x6010

Here the configuration specific to the ECE Department’s USB-JTAG adapter is performed. Really, the

only lines you should need to change for a different adapter are lines 35, 36 and 37.

For those using an ECE USB-JTAG adapter, the above lines tell OpenOCD that the interface is based

on an FT2232 chip. Vendor and product ID values are also specified.

Depending on the adapter design, the FT2232’s GPIO pins can be used for different purposes. The

JTAG signals are always the same, but other IO can vary. Line 36 specifies that “usbjtag” is the GPIO

layout of the ECE adapter’s FT2232D chip. As explained by the OpenOCD User’s Guide, the “usbjtag”

layout is that of the “USBJTAG-1” adapter described in the OpenOCD diploma thesis.

In the thesis you can see from a schematic of USBJTAG-1 that the FT2232’s GPIOL0 line is used for

the Test Reset signal, and that the GPIOL2 line is used for the Reset signal (which would connect to

the NRST pin of a SAM7S MCU). In the schematic for the ECE USB-JTAG adapter you can see a similar

setup, except GPIOL1 is used for the Reset signal.

The point of explaining this is that it means you won’t be able to perform resets from OpenOCD

which require the use of the NRST pin of the MCU (for example the OpenOCD “reset run”

command). You can still perform software resets though, and so it is for this reason that this guide is

designed to make use of software resets only. If you want to enable the rest of the reset

functionality you will need to find a layout choice which is more compatible with the ECE adapter.

You can get a list of the layout options from the OpenOCD User’s Guide but, to work out the actual

FT2232 pin configurations that they represent, you will need to delve into the OpenOCD source code

– particularly the ft2232.c file. (Note that the link takes you to a layout which appears to be the same

as “usbjtag” but with GPIOL1 assigned to the Reset signal. This is the “signalyzer” layout, and should

therefore be a suitable choice. I have not thoroughly tested and confirmed this though.)

If you’re not using the ECE USB-JTAG adapter, then the chances are that you are using an off-the-

shelf commercial programmer. Look around your OpenOCD directory: In the “scripts\interface”

folder, you will hopefully find a configuration file that matches your USB-JTAG adapter.

Say for example you have an Olimex “ARM-USB-TINY” adapter. There is a configuration file in the

interfaces folder for this adapter, called olimex-jtag-tiny.cfg. For this adapter, you would remove

lines 35, 36 and 37, and would replace them with the following line:

source [find interface/olimex-jtag-tiny.cfg]

Page 53: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

This should be enough to adapt the configuration file to your setup. I can’t guarantee this though,

and so if this doesn’t work there really is no option but to read through the OpenOCD User’s Guide

to get a better understanding of OpenOCD, and then try to figure out the problem from there.

***

39 40 41 42 43 44 45

# The following are signal-related settings. adapter_khz 3 adapter_nsrst_delay 200 jtag_ntrst_delay 200 adapter_nsrst_assert_width 100 jtag_ntrst_assert_width 100

Line 40 sets the JTAG clock to 3kHz. As explained at the beginning of this section, the setting has a

theoretical maximum of one-sixth of the CPU clock. Because the CPU runs on the Slow Clock

(32.768kHz) after a reset, we can only be sure that the CPU is running at least this slowly when we

connect OpenOCD. For this reason, 3kHz is chosen as the adapter speed as it is safely under one-

sixth of 32.768kHz.

The remaining lines of the above configuration concern the timing of reset signals in milliseconds.

***

46 47 48 49 50 51 52

# This section configures OpenOCD for working with a SAM7 chip. source [find target/at91sam7sx.cfg] # OpenOCD recommends these settings to improve performance with FT2232 adapters. arm7_9 dcc_downloads enable arm7_9 fast_memory_access enable

The configuration file for SAM7S MCUs, included with OpenOCD, is run here. Performance improving

settings are also enabled (OpenOCD issues a warning if you do not enable these settings).

If you are using the ECE JTAG adapter and have chosen to use a different FT2232 layout, in order to

enable system resetting through OpenOCD, you might also have to add the following line at some

point after line 46:

reset_config separate

This is to override a reset_config setting used in the at91sam7sx.cfg configuration script. For some

reason that script assumes that the test logic is reset with any system resets (due to the use of

“srst_pulls_trst”). This means that when you instruct OpenOCD to perform a “reset halt”, OpenOCD

thinks it is not possible and so doesn’t actually do it. If you override the setting using the line above,

OpenOCD appears to perform a reset and halt correctly and without complaint.

Page 54: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

***

53 54 55 56

# Halt the MCU when GDB connects otherwise the connection fails. # $_TARGETNAME comes from at91sam7sx.cfg. $_TARGETNAME configure -event gdb-attach halt

Lastly for this file an event is configured, which results in OpenOCD halting the SAM7S MCU

whenever the GNU Debugger attempts to connect to OpenOCD. This is required if you find that,

without configuring the event, you are unable to connect GDB to OpenOCD for debugging (how to

use GDB for debugging is introduced at the end of this guide).

When attempting to run OpenOCD, an important thing to remember is to have the USB-JTAG

adapter connecting the MCU to your PC – make sure the MCU is powered as well. Otherwise you’ll

find that OpenOCD instantly closes down because it can’t find the target.

If you follow this guide and OpenOCD is running properly in every other respect, you might still find

that it gives a warning like “use ‘at91sam7s.cpu’ as target identifier, not ‘0’”. As far as I can tell, this

is a result of the at91sam7sx.cfg script being out of date or incorrect. You can get rid of the warning

by replacing the following line from at91sam7sx.cfg:

flash bank $_FLASHNAME at91sam7 0 0 0 0 0 0 0 0 0 0 0 0 18432

With:

flash bank $_FLASHNAME at91sam7 0 0 0 0 $_TARGETNAME 0 0 0 0 0 0 0 18432

This isn’t necessary, it’s just in case you see the warning and think there’s something wrong with

your configuration. If OpenOCD doesn’t give any other warnings or errors, you should be good to go.

Page 55: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Creating the Makefi le

The final file needed to complete the project is the makefile (the filename is just “makefile” with no

extension).

The reason this has been left until last is because it ties everything together – the makefile has been

designed so that it’s the only thing you’ll need to use Command Prompt for. You will be able to not

only compile and build the project with the makefile, but you’ll also be able to run OpenOCD,

program the MCU, and initiate debugging – all with make commands in Command Prompt.

If you are unfamiliar with makefiles and what they do, the GNU Make documentation should bring

you up to speed. Even if you are familiar with Make, you might not have heard of Make’s functions

before – so when you come across these remember that the documentation is where to look for

info.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

# File: Makefile # Created on: 12/12/2012 # Author: Adam Goodwin # # Description: This makefile is for compiling, linking and loading the # led_test_sam7s256 program for the SAM7S256 development board. By default, a # debug build is created. To specify a release build (containing less debugging # information) use the command "make BUILD=RELEASE". # # To adapt this makefile for other SAM7S projects, simply specify the # following: # - The tools to be used # - The project directory (the same folder containing the makefile) # - The directories for different files (relative to the project directory) # - The application name # - The individual source and header files of the project # The makefile comments show where to specify each of these items. # # Note: This makefile is based on the one used in the series of articles # "Building Bare-Metal ARM Systems with GNU" by Miro Samek. You can find the # articles at http://www.state-machine.com/resources/articles.php#ARM # The tools to be used (compiler, assembler, etc): CC = arm-none-eabi-gcc AS = arm-none-eabi-as LD = arm-none-eabi-gcc OBJCPY = arm-none-eabi-objcopy RM = rm -rf MKDIR = mkdir

The tools on lines 24 through to 29 are the names of the executables from the YAGARTO Toolchain,

and YAGARTO Tools package, which are going to be called upon later in the makefile.

In order, they are the GNU Compiler Collection, the GNU Assembler, the GNU Linker (which is

actually called via the compiler collection), the GNU “objcopy” utility, the “remove” tool, and the

“make directory” tool.

***

Page 56: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

# Set the project directory (relative to this makefile) here: PRJ_DIR = . # Set the directories, relative to the project directory, which contain each of # the following types of file: # Output file directory for release build: RLS_DIR = $(PRJ_DIR)/release # Output file directory for debug build: DBG_DIR = $(PRJ_DIR)/debug # Header files location: HDR_DIR = $(PRJ_DIR) # Assembly source files location: ASM_DIR = $(PRJ_DIR) # C source files location (for source to be compiled to ARM instructions): ARM_DIR = $(PRJ_DIR) # C source files location (for source to be compiled to Thumb instructions): TMB_DIR = $(PRJ_DIR)

Here the directories of the project are specified so that Make knows where to find your source files

and scripts. The project directory (PRJ_DIR) is just set to “.”, the current directory, which will always

give the directory where the makefile is located – as long as you run Make from the project

directory.

(So to ensure that running the makefile works: Make sure you put the makefile in the project folder,

along with all of the other files created so far, and only run Make from Command Prompt after you

have navigated to the project folder using the “cd” command.)

The rest of the directories here are located relative to the project directory. The release and debug

folders (RLS_DIR and DBG_DIR) don’t exist yet, but the makefile will create them later for holding

release and debug output respectively. These are the only output directories; the rest of the

directories should already exist and contain the expected files.

The remaining directories (HDR_DIR, ASM_DIR, ARM_DIR, and TMB_DIR) are set equal to the project

directory. You can optionally organise your source files into different folders, based on whether the

source files are header files, assembly files, C files to be compiled to ARM code, or C files to be

compiled to Thumb code. This organisation won’t cause them to be appropriately compiled or

assembled. You will still need to specify each of the source files, and what type they are, later in the

makefile.

***

50 51 52 53 54 55 56

# Specify the program name, this will be used as the base name for output files: APP_NAME = led_test_sam7s256 # Specify the linker script here, it will be assumed to be in the project # directory: LNK_SCRPT = led_test_sam7s256.ld

Page 57: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Here you specify the program’s name. It makes sense for it to share the name of the project, but it

can be anything. The output ELF and MAP files will have this name along with the appropriate

extensions.

Also specify here the linker script. It’s assumed by the rest of the makefile that the linker script is in

the project directory (PRJ_DIR).

***

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

# List here all of the project header files: APP_DEP = \ AT91SAM7S256.h \ dev_board.h \ delay.h # List here all of the project assembly source files: ASM_SRC = \ sam7s_startup.s # List here all of the project C files to be compiled to ARM code: ARM_SRC = \ sam7s256_init.c \ main.c \ delay.c # List here all of the project C files to be compiled to Thumb code: TMB_SRC =

This is where you list all of the project source files. Be sure that they are all where you said they

would be, based on the directories you entered before (lines 41 to 48).

This concludes the part of the makefile which you might have needed to change. Everything else

from this point forward can be left alone.

***

76 77 78 79 80 81 82 83 84 85 86

################################################################################ # This marks the end of the project-specific settings. For SAM7S projects, the # # rest of this makefile can remain untouched. # ################################################################################ # The CPU the software is being written for: ARM_CORE = arm7tdmi # Copy the list of header files but include the path this time. APP_DEP_WPATH = $(addprefix $(HDR_DIR)/, $(APP_DEP))

Line 82 assigns the processor of the SAM7S MCUs to a variable for later use. In line 85, the specified

header files from earlier are stored in a new variable. This time the full path to each file is included,

by using the “addprefix” function.

***

Page 58: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106

# If make is run like so: "make BUILD=RELEASE", then a release build of the # program will be made without debugging information. A debug build is the # default. ifeq (RELEASE, $(BUILD)) # Release configuration BIN_DIR = $(RLS_DIR) CCFLAGS = -c -mcpu=$(ARM_CORE) -mthumb-interwork -Os -ffunction-sections \ -Wall -I $(HDR_DIR) -D NDEBUG -o $@ ASFLAGS = -mcpu=$(ARM_CORE) -mthumb-interwork -o $@ LDFLAGS = -T $(PRJ_DIR)/$(LNK_SCRPT) -o $(BIN_DIR)/$(APP_NAME).elf \ -nostartfiles -Wl,-Map,$(BIN_DIR)/$(APP_NAME).map,--cref,--gc-sections else # Debug configuration BUILD = DEBUG BIN_DIR = $(DBG_DIR) CCFLAGS = -g -c -mcpu=$(ARM_CORE) -mthumb-interwork –O0 -ffunction-sections \ -Wall -I $(HDR_DIR) -o $@ ASFLAGS = -g -mcpu=$(ARM_CORE) -mthumb-interwork -o $@ LDFLAGS = -T $(PRJ_DIR)/$(LNK_SCRPT) -o $(BIN_DIR)/$(APP_NAME).elf \ -nostartfiles -Wl,-Map,$(BIN_DIR)/$(APP_NAME).map,--cref,--gc-sections endif

When you run Make, you can specify the build type – release or debug. The release build results in

less debugging information and a more optimised output executable, and puts all output in a

different folder to the debug build. This is accomplished by setting different compiler, assembler and

linker flags based on the build type.

The debug build is the default. You can tell Make to do a release build instead by running Make as

usual from the Command Prompt, but with the addition of “BUILD=RELEASE” to the command line

arguments.

In the following part of the guide I’ll give a quick rundown of what each of the arguments in the

compiler, assembler, and linker flags do. I’ll mix the debug and release builds in this explanation, but

you can look at the code above to see where and how each argument is used.

Note also that because the linker is being called via the compiler, some of the linker flags are actually

GCC options and not GNU Linker options. For all of the compiler flags, and these particular linker

flags, you can use the GNU Compiler Collection documentation to find out more. Use the GNU

Assembler and GNU Linker documentation from earlier in this guide to find out more about their

respective options.

CCFLAGS (Compiler Options): “ -g”

This option results in the compiler producing debugging information that can be used by GDB.

CCFLAGS (Compiler Options) : “ -c”

This option specifies that no linking should occur. At this stage, we only want to compile our source

files.

CCFLAGS (Compiler Options): “ -mcpu=$(ARM_CORE)”

Here the contents of the ARM_CORE variable are used to tell GCC what processor is being targeted.

This is so that the output object files contain instructions appropriate for an ARM7TDMI.

Page 59: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

CCFLAGS (Compiler Options): “ -mthumb- interwork”

This ensures that ARM and Thumb code will work together if their use is mixed in your program.

CCFLAGS (Compiler Options): “ -O”

This sets optimisation levels. See the GCC documentation for the different options available. Greater

optimisation can mean strange results when debugging.

CCFLAGS (Compiler Options): “ -f function-sections”

This puts functions into their own sections in the output file. If you recall, this is so that you can

place functions in the .fastcode section in the linker script.

CCFLAGS (Compiler Options): “ -Wall”

This enables several warnings.

CCFLAGS (Compiler Options): “ - I $(HDR_DIR)”

By using this option the compiler will look in the header file directory for your header files.

CCFLAGS (Compiler Options): “ -D NDEBUG”

This defines NDEBUG. If you include in your C source files the “assert.h” header file, you can perform

assertions for debugging. There may be some more complex setup required to do this for an

embedded project, but at the very least you can enclose any debug code in “ifndef NDEBUG” and

“endif” directives. When you make a release build, NDEBUG will be defined, and so the debug code

won’t be compiled.

CCFLAGS (Compiler Options): “ -o $@”

The “-o” option sets the output file name. Here the Make “Automatic Variable” of “$@” is specified.

In the “recipe” of a “rule”, this will evaluate to the rule target’s file name (more on this soon).

ASFLAGS (Assembler Options)

All of these are similar to some of the compiler options, so refer to those for more on the meaning of

these assembler options.

LDFLAGS (Compiler and Linker Opt ions): “ -T $(PRJ_DIR)/$(LNK_SCRPT)”

The “-T” option sets the linker script to be used by the compiler (which will be passed to the linker).

It is set to the linker script specified earlier in the makefile.

LDFLAGS (Compiler and Linker Opt ions): “ -o $(BIN_DIR)/$(APP_NAME).elf ”

The name and location of the output file is set, the same as with the compiler and assembler. The

ELF extension is given to the output file as this is the format specified in the linker script.

Page 60: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

LDFLAGS (Compiler and Linker Opt ions): “ -nostartf i les”

This tells GCC to tell the linker not to use the standard system startup files. This is because we are

making an embedded application with its own startup assembly routine.

LDFLAGS (Compiler and Linker Opt ions): “ -Wl,-Map,$(BIN_DIR)/$(APP_NAME).map, --cref, - -gc-sections”

The first part of this long option is “-Wl”. This is an option for GCC which tells GCC to pass the rest of

the option on to the linker. Where there are commas, the linker receives separate options delimited

with spaces.

So first the linker gets the option “-Map $(BIN_DIR)/$(APP_NAME).map”. Except, by this time, the

Make variables will already have been substituted out for their values. So with this example project,

the linker would actually receive “-Map ./debug/led_test_sam7s256.map”. The “-Map” option

creates a link map file to help show the layout of the executable.

The “--cref" option adds some additional information to the map file.

Finally, the “--gc-sections” option enables garbage collection of unused input sections.

***

107 108 109 110 111 112 113 114 115 116 117

# Automatically list all of the object code files to be created. ASM_OBJS = $(ASM_SRC:.s=.o) ARM_OBJS = $(ARM_SRC:.c=.o) TMB_OBJS = $(TMB_SRC:.c=.o) OBJS = $(ASM_OBJS) $(ARM_OBJS) $(TMB_OBJS) # Take the same lists, but include the path to the files. ASM_OBJS_WPATH = $(addprefix $(BIN_DIR)/, $(ASM_OBJS)) ARM_OBJS_WPATH = $(addprefix $(BIN_DIR)/, $(ARM_OBJS)) TMB_OBJS_WPATH = $(addprefix $(BIN_DIR)/, $(TMB_OBJS)) OBJS_WPATH = $(addprefix $(BIN_DIR)/, $(OBJS))

The source code variables have their extensions swapped using Make’s “patsubst” function (the

shorthand version), then these new filenames are stored in new variables. This is to form a list of the

object files which are to be made (line 111).

Then, similarly to with the header files, the object files are copied to new variables with the entire

path to the files included (using the addprefix function).

***

118 119 120 121 122 123 124 125 126 127 128

################################################################################ # What follows are the make targets and their rules. # ################################################################################ # Target "all". Rebuilds out of date files. Use the command "make all". .PHONY: all all: $(BIN_DIR)/$(APP_NAME).elf $(BIN_DIR)/$(APP_NAME).elf: $(PRJ_DIR)/$(LNK_SCRPT) $(OBJS_WPATH) @echo Linking... $(LD) $(OBJS_WPATH) $(LDFLAGS)

Page 61: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

@echo Done. @echo Finished. Output is $(BIN_DIR)/$(APP_NAME).elf. $(ASM_OBJS_WPATH): $(BIN_DIR)/%.o: $(ASM_DIR)/%.s @$(MKDIR) -p $(BIN_DIR) @echo Assembling $<... $(AS) $(ASFLAGS) $< @echo Done. $(ARM_OBJS_WPATH): $(BIN_DIR)/%.o: $(ARM_DIR)/%.c $(APP_DEP_WPATH) @echo Compiling to ARM $<... $(CC) -marm $(CCFLAGS) $< @echo Done. $(TMB_OBJS_WPATH): $(BIN_DIR)/%.o: $(TMB_DIR)/%.c $(APP_DEP_WPATH) @echo Compiling to Thumb $<... $(CC) -mthumb $(CCFLAGS) $< @echo Done.

Here are the makefile “rules”. Each block in the above section of the makefile is a rule. Line 124 is a

rule, lines 126 to 130 are a rule, lines 132 to 136 are a rule, etc. Before going any further, we will

briefly look at the structure of a makefile’s rules.

Makefi le Rule Components: “Targets”

For each rule there are one or more targets. These are everything before the first colon in a rule. So

the first rule in this makefile is for the “all” target. The second rule has the target

“$(BIN_DIR)/$(APP_NAME).elf” or “./debug/led_test_sam7s256.elf”.

The first target is used as the default “goal” of Make. The goal is the target that Make is to update,

by using the rule for that target. To reiterate: When you run Make, Make’s purpose is to update the

specified target – or the default target if none is specified. Except for “phony” targets, a target

should be a filename. So the processing of a rule (which has a non-phony target) should result in the

target file being updated.

You can specify different goals for Make by adding arguments to the command line. For example,

you could type “make all” in the command line to explicitly run Make with the “all” target as the goal

(this will give the same result as just typing “make”, because “all” is the first and default target

anyway).

Makefi le Rule Components: “Prerequisites”

Another component of a rule is the prerequisites. What follow the target (after the colon and on the

same line) are the prerequisites. So for the “all” target, the only prerequisite is

“$(BIN_DIR)/$(APP_NAME).elf”, which will become “./debug/led_test_sam7s256.elf”.

Before Make can process a rule, it must first process the prerequisites’ rules (if they have rules). So

because the “all” target’s prerequisite is “./debug/led_test_sam7s256.elf”, to complete the updating

of the “all” target Make must first process the rule for the “./debug/led_test_sam7s256.elf” target.

You can see that the rule for the “./debug/led_test_sam7s256.elf” target begins on line 126.

Page 62: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Makefi le Rule Components: “Recipes”

Recipes are below the first line of a rule. When all of the prerequisites of a target have been dealt

with, all that remains to finish updating the target file (and thus complete the rule) is to follow the

recipe. The recipe only needs to be followed if the target file does not exist, or if one of the

prerequisite files is more recent than the target file. Otherwise you’d just be creating the exact same

target file as the old version.

Each line in a recipe is a command to be sent to the command line, and must start with a tab

character so that Make knows the line is still part of the recipe.

***

So if we now look again at the section of the makefile from earlier, you can see that there are three

rules for creating the different object files needed to produce the “./debug/led_test_sam7s256.elf”

target (which is itself the prerequisite of the “all” target).

The rules for the object code targets are actually a little bit more complicated than the ordinary rules

I’ve described above because they are “static pattern rules”. Essentially this means that they are

generalised rules which handle many similar targets, by adjusting the prerequisites based on a

pattern.

There is a separate static pattern rule for making object files from assembly code, ARM C code, and

Thumb C code. So Make will consider there to be separate rules for each of the targets in the

variables “$(ASM_OBJS_WPATH)”, “$(ARM_OBJS_WPATH)”, and “$(TMB_OBJS_WPATH)”.

The Make manual, linked earlier, covers static pattern rules in much higher detail if you want to

learn more.

***

Notice the use of the tool and flag variables from earlier – e.g. “$(AS)” and “$(ASFLAGS)”. These are

expanded to their full values by Make, before being sent to the command line where they are

treated as commands that can be run.

Also under the assembly rule, in its recipe, you’ll notice that “$(MKDIR)” is used to create the debug

or release output directory – if it doesn’t already exist. This is performed here, as the directory needs

to exist before Make attempts to create any object output files – which are to be located in the

output directory.

It happens that, because of how the “$(OBJS_WPATH)” variable is defined, the assembly object files

are the first to be attempted (“$(OBJS_WPATH)” is a prerequisite of the output ELF file). The “@”

character is used to suppress echoing for the mkdir line, and many other lines, in the makefile’s

recipes. This is to stop any output from mkdir being printed.

If you are wondering what the “$<” automatic variable does, it expands to the name of the first

prerequisite – which will be the source file having the same name as the output object file for these

rules.

Page 63: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

***

148 149 150 151 152 153 154 155 156 157 158 159 160 161

# Target "clean". Deletes generated files. Use the command "make clean". .PHONY: clean clean: @echo Deleting files... \(*.o, *.elf, *.map\) -@$(RM) $(BIN_DIR)/*.o \ $(BIN_DIR)/*.elf \ $(BIN_DIR)/*.map @echo Done. # Target "rebuild". Rebuilds files, even if not out of date. Use the command # "make rebuild". .PHONY: rebuild rebuild: clean all

The phony target “clean”, because it doesn’t correspond to a filename, will always have its recipe

run when requested using “make clean”. The “clean” target’s recipe removes any of output files

created by the makefile.

The “rebuild” target is just the combination of the “clean” and “all” targets. The “rebuild” target has

no recipe, it simply performs its purpose by having prerequisites.

***

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

# Target "program". Programs the device. Use the command "make program". Note # that OpenOCD will be started if it is not already running. .PHONY: program program: all openocd @arm-none-eabi-gdb --batch \ -ex "target remote localhost:3333" \ -ex "monitor adapter_khz 3" \ -ex "monitor reset init" \ -ex "monitor adapter_khz 3000" \ -ex "load" \ -ex "monitor adapter_khz 3" \ -ex "monitor reset init" \ -ex "monitor resume" \ $(BIN_DIR)/$(APP_NAME).elf

The “program” target is for programming the MCU by simply typing “make program” from the

command line. The first prerequisite is “all”, to ensure that the ELF executable is up-to-date, and the

second prerequisite is “openocd”, which ensures that OpenOCD will be running to allow

programming (the “openocd” target is explained further through the guide).

The recipe for “program” looks quite large, although it is actually only one line broken up into

several. The recipe runs the GNU Debugger in batch mode, so that GDB will exit upon completion.

The majority of the rest of the arguments to GDB are “-ex” options, which execute individual GDB

commands (these could have been written into a script, but I decided there have been enough

scripts already and so I incorporated them into the makefile).

You can find information on GDB commands in the GDB documentation here.

Page 64: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

The GDB commands start on line 167 by connecting to the OpenOCD server through port 3333. Next,

the GDB “monitor” command is used to pass a command to OpenOCD. The adapter speed is set to

the low value of 3kHz, and the “reset init” command is issued to OpenOCD (the implementation of

this command can be seen in the “at91sam7sx.cfg” script of the OpenOCD directory). For our

purposes, the most important feature of this command is that it brings the MCU’s clocks up to speed

(note that it assumes an external oscillator of 18.432MHz).

After this command is issued, the adapter speed is brought up to 3MHz on line 170. Following this,

GDB’s “load” command is issued to load the specified executable to the MCU. The MCU is then

reset, and allowed to run. The final argument to GDB is to specify “./debug/led_test_sam7s256.elf”

or “./release/led_test_sam7s256.elf” as the executable to use for debugging symbols. This is also the

executable that GDB’s “load” command will load.

***

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191

# Target "debug". Runs the GDB debugger. Use the command "make debug". This # assumes that you don't want to debug startup code, and stops the program at # the main function. Note that OpenOCD will be started if it is not already # running. .PHONY: debug debug: openocd @arm-none-eabi-gdb \ -ex "target remote localhost:3333" \ -ex "monitor adapter_khz 3" \ -ex "monitor reset init" \ -ex "monitor adapter_khz 3000" \ -ex "break main" \ -ex "continue" \ $(BIN_DIR)/$(APP_NAME).elf

Similar to the “program” target, the “debug” target lets you start debugging just by issuing the

command “make debug”.

This time the only prerequisite is the “openocd” target. It is assumed that you have already

programmed the MCU with the most recently built executable.

Aside from not running GDB in batch mode (to allow you to continue issuing debug commands after

the initialisation is finished), the initialisation commands are much the same as they were for the

“program” target.

The differences begin on line 188, where a breakpoint is added at the main function. Keep in mind

that only 2 hardware breakpoints are available when you are debugging. After the breakpoint is

added, the MCU is allowed to continue until the breakpoint is reached, where it will halt and await

further debugging instructions.

***

Page 65: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

# Target "debug_startup". Runs the GDB debugger. Use the command # "make debug_startup". This target is similar to "debug", except here the first # breakpoint is at the beginning of the _reset function in sam7s_startup.s. This # allows you to debug startup code. Remember to use "monitor adapter_khz 3000" # from within GDB once the initialisation code has brought the clocks up to # speed. Note that OpenOCD will be started if it is not already running. .PHONY: debug_startup debug_startup: openocd @arm-none-eabi-gdb \ -ex "target remote localhost:3333" \ -ex "monitor adapter_khz 3" \ -ex "monitor soft_reset_halt" \ -ex "monitor mww 0xFFFFFD00 0xA5000004" \ -ex "break _reset" \ -ex "continue" \ $(BIN_DIR)/$(APP_NAME).elf

Again similar to a previous target, the “debug_startup” target is much like the “debug” target. The

changes begin on line 203. Instead of a “monitor reset init” command, which would bring the clocks

up to speed, the “monitor soft_reset_halt” command is used (which the “reset init” command

actually uses itself, before moving onto clock configuration). The “soft_reset_halt” OpenOCD

command resets the CPU, then moves the program counter back to the reset exception vector to

allow you to debug from the beginning of the program.

The purpose of using the “soft_reset_halt” option is that, in conjunction with a peripheral reset, the

MCU can be put into a state which is essentially the same as when it has only just been powered on.

This allows us to check that the startup code is doing the configuration correctly, without the

possibility of anything working being due to configuration performed by the “reset init” command.

Note: To get the MCU to an initial state, you could also use the OpenOCD command “reset halt”.

This (along with the “reset” and “reset run” commands) attempts to use the NRST pin, which is not

always available (either because it is not enabled, or because the USB-JTAG adapter does not

support it). For this reason I have stuck to using software-based resets in this guide.

On line 204 the Reset Control Register, at address 0xFFFF FD00, is being loaded with the value

0xA500 0004. This is the setting of the PERRST bit of the register to one, with the password of 0xA5.

This performs a peripheral reset, thereby ensuring that the remap region is mapped to flash and the

clock settings are at the default. The effects of any startup code that managed to run before the

“soft_reset_halt” command took place will also be undone.

Finally a breakpoint is placed at the _reset function and the program is allowed to continue until this

point. Note that once you have stepped through the startup code that configures the clocks, you will

be able to increase the JTAG clock above 3kHz.

***

209 210 211 212 213 214 215

# Target "openocd". Runs OpenOCD with the specified configuration file. Use the # command "make openocd". Ensure the USB-JTAG interface is connected between the # board and computer. Note: OpenOCD is only started if it is not already # running. .PHONY: openocd openocd: @echo Running OpenOCD if it is not already running...

Page 66: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232

-@cmd /C run_openocd @echo Done. OpenOCD is running. # Target "show". Shows the values of the makefile variables. Use the command # "make show". .PHONY: show show: @echo BUILD = $(BUILD) @echo APP_DEP_WPATH = $(APP_DEP_WPATH) @echo ASM_OBJS = $(ASM_OBJS) @echo ARM_OBJS = $(ARM_OBJS) @echo TMB_OBJS = $(TMB_OBJS) @echo ASM_OBJS_WPATH = $(ASM_OBJS_WPATH) @echo ARM_OBJS_WPATH = $(ARM_OBJS_WPATH) @echo TMB_OBJS_WPATH = $(TMB_OBJS_WPATH) @echo Output file = $(BIN_DIR)/$(APP_NAME).elf

The final two phony targets are “openocd” and “show”. The “openocd” target runs OpenOCD, if it is

not already running, by making use of the batch file we wrote. The “show” target is for displaying

the value of makefile variables.

To summarise the ways you can use the makefile, the commands you can enter in Command Prompt

that utilise the makefile are as follows:

Command Result

make Builds the debug build of the project.

make all Same as above.

make clean Deletes all of the debug build output files.

make rebuild Equivalent to “make clean” followed by “make all”.

make program Runs “all” then “openocd”. Following this the debug program is loaded to the MCU.

make debug Runs “openocd” and starts GDB for a debugging session of the debug executable.

make debug_startup Similar to “make debug”, but initially breaks at _reset instead of main.

make openocd Runs OpenOCD, if it isn’t running already, by using a batch file.

make show Shows the values of the debug build makefile variables.

BUILD=RELEASE By appending this to any of the other commands, you can use the release build instead of the debug build.

Congratulations! You’ve nearly finished the guide. All that’s left now is to see if the program works.

This is done in the next section.

Page 67: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Testing and Debugging

This section will be very (very) brief, and won’t go into much detail. I’ll give you a few instructions on

how to see if the program has worked, and from there you will now hopefully have enough

knowledge to continue on your own.

First, your project directory should look something like this:

All of the files should be the same or similar.

If you shift and right-click on an empty area of the folder, you should get the option to open a

command window. If not, run Command Prompt and use the “cd” command to navigate to your

project directory.

Now, make sure the USB-JTAG adapter is connected to your PC and to the microcontroller.

Page 68: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Checking the Hardware and OpenOCD

First let’s make sure that OpenOCD is working. Into your Command Prompt, type “run_openocd”

and press enter. You should get a message saying that OpenOCD is not running, and that it is now

being started. Check your taskbar for another Command Prompt window that should have opened.

It should look like this:

If you have this, then you should be extremely relieved – OpenOCD is working and you shouldn’t

need to do any more messing around.

If you have something close to this, but there are errors, do your best to try to figure out how to fix

the problem. The OpenOCD User’s Guide should be able to help significantly. All of the

documentation linked throughout this guide is linked again in the Web Links section for easy

reference.

If you don’t have an OpenOCD window at all, try running the command again and keep an eye on the

taskbar. You’ll probably notice that the OpenOCD window is popping up and closing again very

quickly. This means that OpenOCD is closing after a significant error, such as not being able to find

the MCU.

In your Command Prompt window, try running OpenOCD directly so that it doesn’t close down

straight away. This will be a command such as the following, depending on your installation version:

> “C:\Program Files\openocd-0.6.1\bin-x64\openocd-x64-0.6.1.exe” –f uc-

sam7-usbjtag.cfg

Remember to use the quotation marks (to avoid problems with the space in the “Program Files”

directory).

Page 69: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Now you should see something like this:

I only have two suggestions for this issue. First, open Device Manager as you did during installation

and check that there isn’t the little yellow triangle denoting a problem with the driver for “USB Serial

Converter A”.

Second, make sure that the USB-JTAG adapter is connected to both your PC and the MCU, and check

that the connections are correct.

The remainder of the guide assumes you’ve got OpenOCD working, and is concerned with

programming the MCU and debugging.

Page 70: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Programming the Board

Programming the board should be as simple as running the command “make program”. You could

run “make” first, to more easily see if everything builds correctly, as otherwise the output from

“make program” could be quite cluttered.

If you run “make” alone, then a successful build would look as follows:

Then, after running “make program”, (and if you still have OpenOCD running from before) the

output should look something like this:

You should also see the LEDs on your board flashing on and off every two seconds (with three quick

flashes initially). If not, you’ll have to use what information you have to work out what’s going

wrong.

Page 71: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

If OpenOCD is connecting to the board correctly, then you can use Telnet to manually give OpenOCD

commands which could help you debug any problems you’re having. In your Command Prompt

window type “telnet localhost 4444” to do this. You might have to enable Telnet, as it is not enabled

by default on Windows anymore.

Under Windows 7, you can enable Telnet by going to Control Panel, then under the Category view

selecting “Programs”. From there select “Turn Windows features on or off”. Tick the box for Telnet

Client then click “OK”.

A couple of OpenOCD commands you might find useful after connecting to the OpenOCD server

through Telnet are the “mww” and “mdw” commands, for writing and displaying words to or from

memory. You might first have to issue the “halt” command to use these commands.

The command “flash info 0” can also be helpful. If some sectors of flash are write protected, this

could be the cause of your program not running as expected. Use the OpenOCD User’s Guide to find

other commands that can help.

In some circumstances running “make program” will fail at the point where the program is supposed

to be loaded (using GDB’s “load” command). I have had this happen when the MCU’s flash memory

is completely erased, for example. If it looks like everything should be working, but “make program”

fails, then try using Telnet to manually get OpenOCD to load the program. For example this

command through Telnet should work (you might have to give the full path to the ELF file):

> flash write_image led_test_sam7s256.elf

Remember, you can also email me with questions. As this is an early version of this guide, I’m sure

there will be things I’ve overlooked, and the guide can be updated with any improvements.

Page 72: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Debugging the Program

All that remains now is to briefly introduce debugging with GDB. Once again, the remainder of the

guide assumes you have completed the previous steps successfully, with the board programmed

correctly and the LED-flashing program running.

If you’ve unplugged the JTAG adapter or the board, reconnect them, and in your command line

enter the command “make debug”. If you’ve closed OpenOCD from before, it will reopen, and

eventually you should get something like the following:

The majority of the output is just the result of the commands for setting up OpenOCD through GDB

(from the makefile recipe). The important part is what should be at the bottom; the program should

have hit the breakpoint at the main function, and GDB should be waiting for further input from you.

From here you can debug the code. You can step through line by line, add and remove breakpoints,

and let the program continue freely. These options are demonstrated below:

Here the “step” command was used three times, to step further into the main function. After this,

“break delay” was used to add a breakpoint at the delay function, and the program was allowed to

continue (until it hit this breakpoint) by the use of the “continue” command. Finally “clear delay”

was used to remove the breakpoint, and the program was continued again indefinitely.

Page 73: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Debugging the Startup Code

To debug the startup code type “make debug_startup” from the command line. The principles are

the same as when the command “make debug” was used.

Here is what the last of the output should look like:

If you type the command “monitor flash info 0” you should see the following (it will take a few

seconds, because the JTAG speed is only 3kHz at the moment):

The reason for doing this is that you can see the Master Clock is currently 32kHz, the same as the

Slow Clock. So the initial commands to GDB, from the makefile, were successful in putting the MCU

into a pseudo-just-powered-up state.

Page 74: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Step through the program until after the clock configuration in the initialisation code, then use the

flash info command again. What you should see is that the clocks are now up to speed.

What this means is that you can now use “monitor adapter_khz 3000” to speed up the JTAG clock

for faster debugging.

You can also use the “info breakpoints” command to see what breakpoints are set; you should see

that there is still a breakpoint at the beginning of the startup code. This is no longer needed, and so

you can use the “delete” command to free up the breakpoint for later use. Finally, if you set a

breakpoint at main with “break main”, then use “continue”, you are now back to where you would

have been had you used the “debug” makefile target:

This is as far as I will go into debugging with GDB. I’ll leave you with one more useful command I

haven’t yet covered though. This is the print command, which you can use to see the value of a

variable. You can also use it to see the value of the program counter, by using “print $pc”, and you

can also use it to see the value of the stack pointer, by using “print $sp”. For other commands you

should look through the GDB documentation.

Page 75: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Debugging Using OpenOCD

This section briefly talks about some ways that you can use OpenOCD for debugging, if ever the need

arises. Perhaps you have to run some code from the remap region, or maybe there is some other

reason you cannot use GDB.

If you have completed the previous section of this guide successfully, you can probably skip this

section and only use GDB for debugging. However, if you need to use the alternative of debugging

with OpenOCD (like I did at some stages of writing this guide) you can read this section to get a head

start. This section of the guide is written in the context of having code that needs to run from the

remap region.

***

Under some circumstances you may have code which is run from the remap region. You will not be

able to use GDB for debugging this code. This is due to there being no debugging symbols defined in

the ELF file for the remap region (addresses 0x0000 0000 to 0x000F FFFF) because of how the linker

script is written. GDB will not be able to tell what is happening if you try to debug this code.

One solution is that you could change the linker script to define the ROM memory region as starting

at 0x0000 0000 instead of 0x0010 0000. Be aware that this would reverse the problem, and would

not allow you to debug any code run from outside of the remap region.

Rather than changing the linker script you can instead use OpenOCD, through Telnet, for a rough

form of debugging. You will have no debugging symbols to help you, and will essentially only be able

to use the program counter to guess where you are in your code, but this should be enough to see if

something seriously wrong is going on.

First you should take note that the program counter value displayed by OpenOCD is two bytes

behind in ARM mode – i.e. it doesn’t show the real-time program counter, but shows the value of

the program counter at the time of fetching the current instruction. In other words, the program

counter value displayed by OpenOCD is the address of the instruction that is ready to be executed.

If you intend to follow along with this section of the guide, you should have the LED flashing program

loaded to the board and running. You should then launch OpenOCD and connect to the server from

another Command Prompt window by using the command “telnet localhost 4444”.

Page 76: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Recall that you can use the “mdw” command to view the contents of memory at an address. This can

be used to see what instruction is about to be executed.

Looking at an example:

Here I used the “reset init” command to get the microcontroller back to an initial state. This

command begins with “soft_reset_halt”, which resets the CPU then halts the program as soon as

possible. It then moves the program counter back to the reset vector.

The rest of the “reset init” command sets up the MCU back to an initial state by resetting the

peripherals, and does a bit more configuration similar to our startup assembly code. The end result

is that you have the MCU halted and in a state almost as though it has only just been powered on,

except the Master Clock is running a lot faster than the Slow Clock.

You can use the command “adapter_khz 3000” at this time to speed up JTAG communication.

After the “reset init” and “adapter_khz 3000” commands I then used the “step” command once to

proceed one instruction beyond the reset vector. The program counter jumped to 0x0000 003C,

implying that the first line of the startup assembly code’s _reset function is located at this address.

That would be this line from sam7s_startup.s:

99 LDR r0, =_reset /* Pass the reset address as the 1st arg */

I then used the “mdw 0x03c” command to read the value of the current instruction, which turned

out to be 0xE59F 00B4.

Now, to be sure that this is the “LDR r0, =_reset” instruction that we expect, we might have to

decode it using the ARM Architecture Reference Manual. This would be tedious, so you can instead

use a tool from the University of Cambridge which will do the decoding for you.

Page 77: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Here is the result for 0xE59F 00B4 from the evaluator (make sure you select the appropriate

architecture and exception mode etc):

So the instruction is similar to what we expect; it’s a load register instruction. But is it loading the

address of the _reset function to the R0 register as we expect from line 99 of sam7s_startup.s?

You might recognise that this is the same sort of instruction which was used in the initialisation

code, which was there defined as LDR_PC_PC_0x18.

In this case the base register is once again the program counter, but this time the destination

register is R0 and the offset is 180 (0xB4).

So where is the instruction loading a value from? The program counter when the instruction was

fetched was 0x0000 003C. So the program counter plus the offset of 180 (plus the additional 8 due

to the pipeline) gives:

Location of load value in memory = 0x0000 003C + 0xB4 + 0x08 = 0x0000 00F8.

So the instruction is loading the value from 0x0000 00F8 into the register R0. Let’s see what the

word being loaded into R0 actually is:

Result

Page 78: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Sure enough it is 0x0010 003C, the address of the reset vector. So we have verified where we are in

the startup code (line 99) and we know that the instruction about to be executed is correct.

This is obviously quite a tedious process, but it has demonstrated how you can use OpenOCD for

some very low-level debugging. From here you could step through the program within OpenOCD and

keep track yourself of where you are in your source code, verifying as you go that everything is as

expected.

Here are a few more pointers about OpenOCD: You can set breakpoints in OpenOCD by using the

“bp” command. Something like “bp 0x00100050 4 hw” will set a hardware breakpoint (because of

the “hw” argument) at address 0x00100050. The “4” argument sets the length of the breakpoint to

four bytes which is one word or instruction.

Using the “bp” command with no arguments lists all of the current breakpoints, and the “rbp”

command removes a breakpoint at the specified address (e.g. “rbp 0x00100050”). Remember that

there are only two hardware breakpoints available.

***

This is as far as the guide will go into debugging. The GDB documentation extensively covers the

debugging commands, and other features of GDB, and so it is the place to go for any further details

on debugging with GDB. Once again the OpenOCD User’s Guide is where you should look for help on

using OpenOCD.

Page 79: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Conclusion and Contact Information

Congratulations on making it through to the end of this guide – I hope you have had successful

results because of it. For programming the SAM7S family of microcontrollers, you have now

hopefully gained enough knowledge of the basics that you can continue to progress further on your

own.

The resources and documentation linked to throughout this guide are included once again on the

next two pages for convenience. These should prove valuable as you continue work on your SAM7S

project.

If you need more help with this tutorial, or have any other feedback, feel free to contact me via

email at [email protected].

At this time I’d like to once again mention my thanks to James Lynch for his Using Open Source Tools

for AT91SAM7S Cross Development tutorial (Lynch, 2007), and Miro Samek for his Building Bare-

Metal ARM Systems with GNU tutorial (Samek, 2007). See the references section of this guide for the

full details on these tutorials.

Page 80: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Web Links

In this section I’ve brought together all of the links to software and documentation used throughout

this guide. After finishing the guide and starting your own SAM7S project, this page should provide

you with a lot of good places to look for help if you get stuck or need resources.

AT91SAM7S Datasheet (SAM7S Series Complete):

http://www.atmel.com/devices/SAM7S256.aspx?tab=documents

ARM Documentation Site (For ARMv5 Architecture Reference Manual, Registration Required):

http://infocenter.arm.com/help/index.jsp

Using Open Source Tools for AT91SAM7S Cross Development:

http://www.at91.com/component/resource/article/Tools/27-Development%20Tools/1057-

using-open-source-tools-for-at91sam7-cross-development.html

Building Bare-Metal ARM Systems with GNU:

http://www.state-machine.com/arm/Building_bare-metal_ARM_with_GNU.pdf

Eclipse IDE:

http://www.eclipse.org/

Java:

http://www.java.com/en/download/index.jsp

YAGARTO:

http://www.yagarto.de/

OpenOCD:

http://openocd.sourceforge.net/

Freddie Chopin’s Website (for OpenOCD Binary Download):

http://www.freddiechopin.info/

libusb-win32:

http://sourceforge.net/apps/trac/libusb-win32/wiki

Page 81: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

OpenOCD User’s Guide:

http://openocd.sourceforge.net/documentation/online-docs/

Olimex Development Board (Used for this Guide):

https://www.olimex.com/Products/ARM/Atmel/SAM7-H256/

AT91LIB Version 1.9:

http://www.atmel.com/tools/AT91SAMSOFTWAREPACKAGE.aspx

Atmel PLL Filter Calculator:

http://www.atmel.com/tools/ATMELPLLLFTFILTERCALCULATOR.aspx

GNU Compiler Collection Documentation:

http://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/

GNU Assembler Documentation:

http://sourceware.org/binutils/docs/as/

GNU Linker Documentation:

http://sourceware.org/binutils/docs/ld/

GNU Make Documentation:

http://www.gnu.org/software/make/manual/#content

GNU Debugger Documentation:

http://sourceware.org/gdb/current/onlinedocs/gdb/

University of Cambridge Arm Instruction Evaluator:

http://svr-acjf3-armie.cl.cam.ac.uk/main.cgi

Page 82: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

Version 2 Changes

The major differences between this and the previous version of the guide are briefly outlined below:

- Changed the “B _reset” instruction in sam7s_startup.s to “LDR pc, =_reset”. This is to ensure an

absolute branch to the location of _reset in flash (rather than a relative branch inside the remap

region), which is the same as how the reset exception vector in RAM works.

The reason for doing this was because building with no optimisation (or more specifically, building

without the -fomit-frame-pointer GCC option) would cause a data abort exception. The exception

would occur shortly before the return from the initialisation C function was supposed to happen,

provided the startup and initialisation code was running from the remap region.

I can only assume that without the optimisation, code relating to the frame pointer (which would

otherwise have been omitted) was trying to relatively access flash immediately before the return

from the initialisation function. But, because the initialisation function remaps memory, the relative

addressing was suddenly accessing RAM instead of the expected flash – causing the abort.

- In sam7s256_init.c I have changed the __ram_start variable to be defined as a uint32_t instead of a

uint8_t. This also means I have changed the vector tables to increment __ram_start by multiples of

one instead of four. The reason for this was because higher optimisation levels enable the option “-

Wstrict-aliasing”. When __ram_start was uint8_t, the vector tables were casting it to a uint32_t then

setting it to a 32-bit value. This was causing a warning about “dereferencing a type-punned pointer”

at higher optimisation levels. I decided not to leave the code in a state where warnings occur.

- Added to the makefile a target for debugging startup code. As a side consequence of the reset vector

change in sam7s_startup.s, debugging startup code is now possible in all cases (previously a boot

from flash would cause the startup code to run from the remap region where there are no debugging

symbols). Have also disabled optimisation completely for debugging.

- The Debugging Using OpenOCD section was originally included because of the problems now

eliminated by the change in sam7s_startup.s. Rather than removing the section I have instead

partially rewritten it, because it still contains some potentially useful information.

- Added an alternative method for loading the program to the MCU using OpenOCD and Telnet.

Page 83: AT91SAM7S Quick Start Guide for Beginnersecewiki.elec.canterbury.ac.nz/mediawiki/images/b/b6/AT91SAM7S_Q… · models of SAM7S MCU where appropriate. The project can be put together

References

Lynch, J. P. (2007). Using Open Source Tools for AT91SAM7S Cross Development Revision C. Retrieved from AT91SAM Community website: http://www.at91.com/component/resource/article/Tools/27-Development%20Tools/1057-using-open-source-tools-for-at91sam7-cross-development.html

Samek, M. (2007). Building Bare-Metal ARM Systems with GNU. Retrieved from Quantum Leaps website: http://www.state-machine.com/arm/Building_bare-metal_ARM_with_GNU.pdf