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