You are on page 1of 34

Topic Video 07

C Programming for Embedded Systems

Monday, 11 October 2010

What happens when you compile?

Monday, 11 October 2010

The role of the Preprocessor

The preprocessor performs the following tasks:

Expands all macros (#dene / macro operand) Inserts the include les inline. Creates a temporary C le.

Monday, 11 October 2010

The role of the Compiler

The compiler translates the C code into assembler and performs optimization on the assembly code. Using a standard assembly translation of the C control structures.

Monday, 11 October 2010

The role of the Assembler

Converts the assembly source code into relocatable binary. This binary code is executable by the target, but it has not been allocated space in the target.

Monday, 11 October 2010

The role of the Archive Utility

The archive utility is able to package your relocatable binary into a library that can be used at a later date.

Monday, 11 October 2010

The role of the Linker and Locator

The linker is responsible for sorting out where the code and variables are to be allocated in the memory of the Target. Code is generally allocated into ROM and variables into RAM. The linker checks to ensure that the code is smaller enough to t inside the ROM of the Target. It also ensures that there is adequate space in RAM for both the program variables and stack. Because function parameters are passed via the stack and local variables also live on the stack. The linker is able to calculate how big the stack must be.

Monday, 11 October 2010

How to create a successful Build System?

The most important thing that is required is a host system. The hosts system should have:

A Cross-compiler (runs natively on the host, but builds binaries for our Target). A set of Target Libraries (Optional) A Debugger (JTAG/BDM), A Simulator/ Emulator (optional) and A Binary Loader for our Target.
8

Monday, 11 October 2010

What does the Build System require?

Knowing what the compiler does it is clear that it will require some information about our Target. Things like:

Where is the memory for code located? Where should variables go? How much space do I have for each? How can I access the I/O registers from inside C? How can I setup the Interrupt Vector Table? How can I embedded assembly instructions in my C code?
9

Monday, 11 October 2010

Memory Map

How do we describe the physical hardware to our C compiler (the linker). Many mainstream IDEs allow this to be done through a project settings menu. In Code Warrior this is done automatically when you choose the derivative of the HS12 during the new project wizard. In GCC this requires a text le to be created.

Monday, 11 October 2010

10

Memory Map
MEMORY { page0 (rwx) : ORIGIN = 0x0, LENGTH = 2k /* I/O Registers */ /* EEPROM */ /* Code space [Flash] */ /* Variable Space */

eeprom (rwx) : ORIGIN = 0x0C00, LENGTH = 1k text (rwx) : ORIGIN = 0x4000, LENGTH = 16k data : ORIGIN = 0x2000, LENGTH = 4k

} PROVIDE (_stack = 0x03FFF);

/* Starting point of Stack */

memory.x
Monday, 11 October 2010 11

Accessing I/O Registers



In C it is possible to dene variables that reside in a predetermined memory location. This is done by assigning a pointer to a particular location and then dereferencing that pointer. Since most of our I/O registers are 8 bit in size they are the same size as an unsigned char variable. Since the I/O registers are not constant and change as a result of external events they can be considered volatile unsigned chars.
12

Monday, 11 October 2010

Accessing I/O Registers

so an IO register can be mapped to a variable in the following fashion:

#dene PORTA *(unsigned char volatile *) (0x0000)

The value 0x0000 refers to the memory location $0000 that is of type volatile unsigned char which is given the name PORTA.

Monday, 11 October 2010

13

Accessing I/O Registers



The PORTA data register can then be accessed from within C as if it was a normal variable. Reading from PORTA C= PORTA; Writing to PORTA PORTA=0xFF;

Monday, 11 October 2010

14

Interrupts in C

Interrupt service routines are dened in C as standard void functions. void ISR(void) But a standard C function is the same as a subroutine in assembly. It is terminated with an RTS. How then do we tell the compiler this is an ISR and therefore should terminate with an RTI instead. Every compiler uses a different method of declaring interrupt service routines, so it pays to look it up in the compilers manual. Some examples


Monday, 11 October 2010

void __attribute__((interrupt)) ISR(void);

(GCC)

void __interrupt(vector_address) ISR (void); (Tasking 8051)

15

Interrupts in C

Interrupt service routines are dened in C as standard void functions. void ISR(void) But a standard C function is the same as a subroutine in assembly. It is terminated with an RTS. How then do we tell the compiler this is an ISR and therefore should terminate with an RTI instead. Every compiler uses a different method of declaring interrupt service routines, so it pays to look it up in the compilers manual. Some examples


Monday, 11 October 2010

Double underscore void __attribute__((interrupt)) ISR(void);

(GCC)

void __interrupt(vector_address) ISR (void); (Tasking 8051)

15

Interrupts in C

Interrupt service routines are dened in C as standard void functions. void ISR(void) But a standard C function is the same as a subroutine in assembly. It is terminated with an RTS. How then do we tell the compiler this is an ISR and therefore should terminate with an RTI instead. Every compiler uses a different method of declaring interrupt service routines, so it pays to look it up in the compilers manual. Some examples


Monday, 11 October 2010

Double underscore (GCC) void __attribute__((interrupt)) ISR(void); void __interrupt(vector_address) ISR (void); (Tasking 8051)

15

Interrupts in C

Interrupt service routines are dened in C as standard void functions. void ISR(void) But a standard C function is the same as a subroutine in assembly. It is terminated with an RTS. How then do we tell the compiler this is an ISR and therefore should terminate with an RTI instead. Every compiler uses a different method of declaring interrupt service routines, so it pays to look it up in the compilers manual. Some examples


Monday, 11 October 2010

No spaces void __attribute__((interrupt)) ISR(void);

(GCC)

void __interrupt(vector_address) ISR (void); (Tasking 8051)

15

Dening the Vector Table



Some compiler link the ISR to the vector table automatically for you. Others, like the GCC compiler require you to do it manually. In GCC the ISR is inserted in the Vector table by dening the table in GCC assembly.

Monday, 11 October 2010

16

Dening the Vector Table


.sect .vectors .globl vectors .globl vectors_addr vectors_addr=0XFFF0 def=0x0000; vectors: .word def ; FFF0 .word ISR ; FFF2 .word def ; FFF4 .word def ; FFF6 .word def ; FFF8 .word def ; FFFA .word def ; FFFC .word _start ; FFFE (reset) Vector Table in GCC-AS .word is the equivalent to DC.W Tells Linker to put this in the vectors section of the memory map. Denes vectors and vector_addr to be global variables. Variables

vector.s
17

Monday, 11 October 2010

Dening the Vector Table


.sect .vectors .globl vectors .globl vectors_addr vectors_addr=0XFFF0 def=0x0000; vectors: .word def ; FFF0 .word ISR ; FFF2 .word def ; FFF4 .word def ; FFF6 .word def ; FFF8 .word def ; FFFA .word def ; FFFC .word _start ; FFFE (reset) Vector Table in GCC-AS .word is the equivalent to DC.W Tells Linker to put this in the vectors section of the memory map. Denes vectors and vector_addr to be global variables. Variables

Same name as as our ISR

vector.s
17

Monday, 11 October 2010

Dening the Vector Table


.sect .vectors .globl vectors .globl vectors_addr vectors_addr=0XFFF0 def=0x0000; vectors: .word def ; FFF0 .word ISR ; FFF2 .word def ; FFF4 .word def ; FFF6 .word def ; FFF8 .word def ; FFFA .word def ; FFFC .word _start ; FFFE (reset) Vector Table in GCC-AS .word is the equivalent to DC.W Tells Linker to put this in the vectors section of the memory map. Denes vectors and vector_addr to be global variables. Variables

vector.s
17

Monday, 11 October 2010

Dening the Vector Table


.sect .vectors .globl vectors .globl vectors_addr vectors_addr=0XFFF0 def=0x0000; vectors: .word def ; FFF0 .word ISR ; FFF2 .word def ; FFF4 .word def ; FFF6 .word def ; FFF8 .word def ; FFFA .word def ; FFFC .word _start ; FFFE (reset) Vector Table in GCC-AS .word is the equivalent to DC.W Tells Linker to put this in the vectors section of the memory map. Denes vectors and vector_addr to be global variables. Variables

Program starts in the function named _start.

vector.s
17

Monday, 11 October 2010

Dening the Vector Table


.sect .vectors .globl vectors .globl vectors_addr vectors_addr=0XFFF0 def=0x0000; vectors: .word def ; FFF0 .word ISR ; FFF2 .word def ; FFF4 .word def ; FFF6 .word def ; FFF8 .word def ; FFFA .word def ; FFFC .word _start ; FFFE (reset) Vector Table in GCC-AS .word is the equivalent to DC.W Tells Linker to put this in the vectors section of the memory map. Denes vectors and vector_addr to be global variables. Variables

vector.s
17

Monday, 11 October 2010

Assembly in C

If you want to include assembly code into your C program this can be done using a keyword asm. Each compiler is different so it pays to look it up.
__asm ( assembler template : output operands : input operands : list of clobbered registers ); Tasking 8051 compiler /* optional */ /* optional */ /* optional */

Monday, 11 October 2010

18

Assembly in C

asm ( assembler template : output operands : input operands : list of clobbered registers ); GCC Compiler /* optional */ /* optional */ /* optional */

Monday, 11 October 2010

19

Assembly in C
GCC Example
#dene SaveContext(TaskID)\




asm("LDD 8,SP\n\tSTD %0"::"m"(Tasks[TaskID].context.CCR));\ asm("LDD 8+2,SP\n\tSTD %0"::"m"(Tasks[TaskID].context.D));\ asm("LDD 8+4,SP\n\tSTD %0"::"m"(Tasks[TaskID].context.IX));\ asm("LDD 8+6,SP\n\tSTD %0"::"m"(Tasks[TaskID].context.IY));\ asm("LDD 8+8,SP\n\tSTD %0"::"m"(Tasks[TaskID].context.PC));\


asm("TFR SP,D\n\tADDD #8+10\n\tSTD%0"::"m"(Tasks [TaskID].context.SP));

Monday, 11 October 2010

20

Pros

Allows fast software developments. Easy readable code. Access to C prolers and debuggers. Can be prototyped on the host. Simpler Better error detection

Monday, 11 October 2010

21

Cons

Limited control over resulting binary. Simplest programs suffer from bloat. Code tends to run slower in C than it would if develop in Assembler. Code is bigger in size. Compilers can be expensive. Can be difcult when mixing assembler with C...
22

Monday, 11 October 2010

Putting it all together


An example using GCC

Ensure you have installed the gcc-68hc1x compiler + binutils-68hc1x (available from http://www.gnum68hc11.org/m68hc11_pkg_rpm.php). Having created both the memory.x and vector.s les. Firstly assemble the vectors.s le m6811-elf-as -m68hcs12 -mshort vectors.s -o vectors.o

Monday, 11 October 2010

23

Putting it all together


An example using GCC

Type in this simple program


#dene PORTA *(unsigned char volatile *) (0x0000) #dene DDRA *(unsigned char volatile *) (0x0002) void main(void){ unsigned char a=0; DDRA=0xFF; while(1) PORTA=a++; }

Test.c
Monday, 11 October 2010 24

Putting it all together


An example using GCC

The C source is compiled using the following command (ensure memory.x and vectors.o is in the same directory).
m6811-elf-gcc -mshort -m68hcs12 -o Test.elf Test.c vectors.o memory.x

Converted to an SREC using m6811-elf-objcopy -O srec Test.elf Test.s19

Monday, 11 October 2010

25

Putting it all together


An example using GCC

Finally the resulting le Test.s19 can be uploaded to the Adapt9S12X using a linux tool called binload. A modied version of binload is available on request from your demonstrators.

Monday, 11 October 2010

26

Need Further Assistance?


Ask your Demonstrator, Post a question on the Forum, Email the Convener, or Make an appointment.
Monday, 11 October 2010 27

You might also like