KEIL uVison : Very Simple Quick Tutorial:
-Aviral Mittal avimit att yhaoo dat camm
Connect @ https://www.linkedin.com/in/avimit/
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){}
}


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.