SITE HOME
This tutorial is a step by step guide on how to create/compile your
first project in KEIL uVision IDE.
It wont use any of the supplied software from KEIL, whatever
bare-minimum is required will be written from scratch, in the
interest of more understand ability.
The objective of the tutorial is to compile your very basic
C-program using Keil, then simulate it within the Keil uVision tool.
It will also demonstrate the use of bare-minimum startup assembly
code that the Keil uVision IDE requires for a basic C-application to
compile successfully. This assembly code is also written from
scratch. A full explanation of the assembly code and syntax is also
provided.
Another objective of this tutorial is to demonstrate how to produce
'binary executable' files corresponding to the C-Code, which can be
read by verilog memory models to support the RTL simulation of the System on
Chip Design Tutorial.
A very very basic program is written in C.
The function of the C program will be to send a constant hex value
to a constant address location multiple times.
This C program is compiled, and the corresponding binary file also
called the '.axf' file is produced. The produced 'axf' file is then
converted into text, and the contents analyzed.
The reader should be aware of what ARM processor is, what a
micro-controller is, and a bit of familiarity with C language and
assembly knowledge is also required.
A limited version of KEIL is available free for download for anyone.
This is the link to armKEIL to
download the free version.
1. After downloading and installing KEIL, open the uVision using the
Keil uVision5 icon on your desktop.
1.1 Make a directory called 'Tutorial_My' where
your KEIL has been installed. This will generally be C:>Keil_v5
1.2 Make a directory called Csource_My' inside
'Tutorial_My' directory.
1.3 Make a directory called 'Common_My' inside
'Tutorial_My' directory
2. Menu->Project->New uVision Project...
A pop-up will open asking you to enter File Name
3. Select a directory: browse to C>Keil_v5>Tutorial_My
Enter your name of the project: eg 'firstproject'
and press 'enter key'
Another pop-up will open asking you to select
Device. We will use Cortex-M4, so select
ARM->ARM Cortex M4 -> ARMCM4
After selecting ARMCM4 click OK
4. Another window will pop-up showing you various 'Software
Components'; Ignore it Click 'Cancel'
5. The main project window will now look something like this:
6. Writing First C Code:
6.1 File -> New
A window opens within Keil called "Text1".
Copy+Paste following code in this window:
typedef
unsigned long uint32_t;
int main ()
{
int ii;
for(ii=0;ii<305419896;ii++) {
*((uint32_t
*)0x40E00018) = 0x87654321;
asm("NOP");
}
while(1){}
}
Note that at times
asm("NOP")
does not work, and you might get som errors, so you may have to try the following instead
__asm("NOP");
To understand the above code line by line, visit the Embedded C Fundamentals
page.
6.2 Save the above source code as first.c in the directory
'Csoruce_My'
Click on Save button
Another Pop-up Opens.
Browse to the directory 'Csource_My'
and then enter filename as 'first.c' then press enter
7. Add your new c source file 'first.c' to the project
Click on 'Target 1', it expands to 'Target 1
-> Source Group 1'
Double click on 'Source Group1', a pop-up opens:
Browse to your first.c inside 'Csource_My'
directory. Select 'first.c'. (Note that the extension '.c' may not
be visible)
Click on 'Add'. Then close the window by clicking
'X' at the corner.
Now if you click on Source Group 1, you will see
your file first.c listed:
8. Build the project
Right Click on 'Targtet 1' and select 'Build
Target'
This attempt to build the project will fail,
as shown with the following error message:
linking...
.\Objects\firstproject.axf: Error: L6320W: Ignoring --entry
command. Cannot find argument 'Reset_Handler'.
.\Objects\firstproject.axf: Warning: L6320W: Ignoring --first
command. Cannot find argument '__Vectors'.
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
9. Adding the missing information:
The above compile fails because there isn't a
'Reset_Handler' and there isn't __Vectors declaration.
So what is a 'Reset_Handler' and what is
'__Vectors declaration. And how do we provide it to the Keil
Compiler.
The first thing that the ARM processor does after
its reset is released is that it fetches whatever is at the address
0x0000_0000, and assumes that it is the 'Stack Pointer'
Value. So whatever is present at location 0x0000_0000 will end up in
the stack pointer.
Then the contents of the location 0x0000_0004 are
treated as a jump location. The code residing at this jump
location is called 'Reset Handler'. So, the compiler is actually
complaining that you have not provided 'Reset Handler' and you have
not provided '__Vectors', one of the __Vectors
being the stack pointer value. Their are other '__Vectors' that will
be required, but for the bare minimum the Stack Pointer Value will
suffice.
9.1. Write an assembly file 'startup.s' to give the compiler bare
minimum. i.e. give it a 'Reset Handler' and the Stack Pointer, and
the size of stack.
Following is a bare-minimum start up code that
the user must use to be able to sensibly compile an application
which can be run.
More on 'startup.s' code
& syntax is explained here.
Click on File ->New at the uVision window.
This will again open a text editor, with default 'Text2' as the
name.
Cut+Paste the following code into it:
Stack_Size EQU 0x00000401
AREA STACK,NOINIT,READWRITE,ALIGN=3
__stack_limit
Stack_Mem SPACE Stack_Size
__initial_sp
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD
Reset_Handler ; Reset Handler
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
Reset_Handler PROC
EXPORT Reset_Handler [Weak]
IMPORT __main
LDR R0, =__main
BX R0
ENDP
EXPORT __stack_limit
EXPORT __initial_sp
END
Save the above code as startup.s in
the C:>Keil_v5>Tutorial_My>Common_My directory.
Then as you added first.c to the project add this
startup.s to
the project.
Double Click on 'Source Group 1', browse to your
startup.s and click on 'Add'. (remember to select 'All Files (.*),
or you wont see your startup.s listed there.
Now close the Add Files to Group window by
clicking on 'x' at the corner.
Notice that the file 'startup.s' begins to show
in your Project Browser window as show above.
10. Build target again:
Right click on 'Target 1', select 'Build Target'.
This time you will see no errors:
Build started: Project:
firstproject
*** Using Compiler 'V5.06 update 6 (build 750)', folder:
'C:\Keil_v5\ARM\ARMCC\Bin'
Build target 'Target 1'
assembling startup.s...
linking...
Program Size: Code=336 RO-data=24 RW-data=0 ZI-data=1124
".\Objects\firstproject.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed: 00:00:00
The output of this exercise is a file called 'firstproject.axf'.
This is a binary file can can be converted into text using the
'fromelf' utility from ARM/Keil:
This exe file can be found in your Keil Install area. If you have
chosen defaults during installation you will find it in C:\Keil_v5\ARM\ARMCC\bin\fromelf
Use the following command on your dos
window.
C:\Keil_v5\ARM\ARMCC\bin\fromelf
--text -c -s -a -d -t -z --output firstproject.txt
firstproject.axf
The above command converts your 'axf' file to a human readable txt
file: the full contents of the txt file can be seen here:
The generated axf file from the keil compiler can also be very
compact, without any debug info, when you chose certain options
while generation, and it will give you addresses/opcodes as it will
be loaded into a memory. A text version of this compact axf file can
be seen here.
It has a lot of things in it, but you can look at contents of your
'main' function in your C-program: It looks like this:
RESET
__Vectors
0x00000000: 20000461
a.. DCD 536872033
0x00000004: 00000089
.... DCD 137
$t
!!!main
__Vectors_End
__main
0x00000008: f000f802
.... BL
__scatterload ; 0x10
0x0000000c: f000f82c
..,. BL
__rt_entry ; 0x68
.
.
Reset_Handler
0x00000088:
4800
.H
LDR r0,[pc,#0] ; [0x8c] = 0x9
0x0000008a:
4700
.G
BX r0
$d
0x0000008c: 00000009
.... DCD 9
.
.
main
0x00000134:
2000
. MOVS
r0,#0
0x00000136:
e004
..
B 0x142 ; main + 14
0x00000138:
4904
.I LDR
r1,[pc,#16] ; [0x14c] = 0x87654321
0x0000013a:
4a05
.J LDR
r2,[pc,#20] ; [0x150] = 0x40e00000
0x0000013c:
6191
.a STR
r1,[r2,#0x18]
0x0000013e:
bf00
.. NOP
0x00000140:
1c40
@. ADDS
r0,r0,#1
0x00000142:
4904
.I LDR
r1,[pc,#16] ; [0x154] = 0x12345678
0x00000144:
4288
.B CMP
r0,r1
0x00000146:
dbf7
.. BLT
0x138 ; main + 4
0x00000148:
bf00
.. NOP
0x0000014a:
e7fe
..
B 0x14a ; main + 22
$d
0x0000014c: 87654321
!Ce. DCD 2271560481
0x00000150: 40e00000
...@ DCD 1088421888
0x00000154: 12345678
xV4. DCD 305419896
You can notice that the Reset_Handler resides at 0x00000088. You will also notice
that the address 0x0000_0004 has the address of reset handler '0x00000004:
00000089'. Actually the location 0x0000_0004 contains
an address of 0000_0089, instead of 0000_0088. The LSB is ignored,
and assumed as ;'0' instead of '1' as the value of '1' at LSB
indicates something else, may be an indication of instruction type.
So 0x0000_00089, will cause the processor to jump to 0x0000_0088.
You can also notice that all the 'Reset_Handler' at 0x0000_0088 is
doing is, to make the processor to jump at 0x0000_0008 (again
0000_0009 in the code, but lsb is ignored and take as '0' instead of
'1'). The user has an opportunity to put more code in
'Reset_Handler' but for the purpose of this tutorial this will
suffice.
11. Simulation:
You don't have to use a hardware processor board,
if you only want to learn and debug your code.
For this Keil has provided 'Simulation support'.
To run a simulation:
Open the Debug Options window my clicking on the
magic stick as shown below:
Options window will open: Select 'Debug' Tab.
Click on 'Use Simulator' as shown below:
Now select Debug-> Start/Stop Debug Session on
your Main Keil Window:
A small info window will pop-up warning you that
Keil is running in 'EVALUATION MODE'. Click OK.
Your Keil window will look something like this:
Now click on the 'Run to Cursor Line' Icon as
shown.
Error while simulation:
*** error 65: access violation at 0x40E00018 :
no 'write' permission
Solution:
Stop your debug session:
Insert a breakpoint (this is not the part of the
solution, but it will give you visibility while your code executes
successfully)
Double click on 'first.c'
Right Click on the line 'asm("NOP")', select
Insert/Remove Breakpoint
Re-Start the debug session, then
Debug -> Start/Stop Debug Session
Debug -> Memory Map : A pop-up window will
open.
Fill in the Map Range input box with the
following, check Read, Write boxes, and click on 'Map Range'
0x40000000 to 0x40FFFFFF
Note: This error should not happen, this is happening
because, the program is writing at a location which isn't declared
as a RW location, but for the sake of completion
of the tutorial, this fix may be acceptable. This method will also
help the user to fix other problems (if any) of similar nature. The
user can temporarily solve this problem like this and then try to
root cause it. To fix this problem one way is to make the location 0x40E00018 a
read/write location by declaring a global variable at this location
as shown below:
typedef unsigned long
uint32_t;
int variable1
__attribute__((at(0x40E00018))) = 0xbbaaccdd;
int main ()
Note that this variable i.e. 'varaible1' is declared outside user's
main function.
There are other ways to solve this problem, e.g. the user may want
to declare a broader region of memory which has the above address as
R/W, say for example the user may want to declare a region starting
0x4000_0000 to 0x4100_0000 as a RW region. But for now this will
suffice.
Now run your simulation, i.e click on the Run to
Cursor Line' Icon {}
You will see value of ii increasing on each
break, as shown below:
While you simulate your code using Keil, one good
practice is to write the output of your program into some memory
location.
For example in the above C-code the program
writes to memory location
*((uint32_t
*)0x40E00018) = 0x87654321;
User can view the memory contents in Keil Window.
User can setup breakpoints and see the contents of memory.
To View contents of memory location: Click on
Memory icon on bottom right of the program window, as shown below,
and type in the star address of the memory location. This should be
done while the program is being stopped using a debug break point:
The following picture shows that the user is trying to see the
contents of memory location starting at 0x40000000
It is also possible to read some default
registers from the cortex-m4 core. For example the following code
reads the default CPUID register from location 0xE000ED00
int variable1
__attribute__((at(0x40000000))) = 0xbbaaccdd;
int main ()
{
uint32_t cpuid;
cpuid = *((uint32_t
*)0xE000ED00); //Read CPUID Reg into cpuid variable. CPUID by
default is present at 0xE000_ED00
asm("NOP");
*((uint32_t *)0x40000000)
=cpuid; //Write the CPUID reg value to memory location
0x4000_0000
while(1){}
}
It is possible to issue extra commands in the Keil Options window to
produce hexadecimal executable file(s) which can be read by verilog
memory models. The System
On Chip Design Tutorial will make use of this feature. To
generate the the hex files:
Click on 'options for target' icon and then select 'user' tab, and
fill in as shown below:
BTW the full command in the above window are not visible: Here it
is:
formelf -cvf
.\Objects\prj4.axf --vhx --8x1 -o image.hex
Click Here to Make
Comments or ask Questions
SITE HOME
This is the End of This Tutorial.
Any feedback/comments will be of great help.
NEXT =>
Click Here to go
to common errors that are encountered while using Keil Compiler
with solutions.