Porting Code Created for Cortex-M3 (LMI devices) with the GNU-Toolchain to the RealView-Toolchain (MDK-ARM)

PRELIMINARY

by Martin Thomas, Kaiserslautern, Germany
<mthomas(at)rhrk(dot)uni-kl(dot>de>
http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects


Preface

This text has been made because I have got some questions from people who want do create code with the GNU-Toolchain (gcc et al) but also want to participate in the Stellaris-Contest 2006. The contest-rules demand that the project's code must compile/link with the RealView tools.

Don't get me wrong: the Keil debugger and simulator are useful tools and I have used them sometimes during a "first contact" with a new device. The RealView compiler is known to be rather good. But for hobbyists and even for some (most?) "Part-time professionals" the full license is simply too expensive.

I do not use the RealView-Tools and do not know much about Keil's MDK-ARM, so take the following for what it's worth.

The described method has been tested with the evaluation- version of MDK-ARM 3.03beta as on the LMI Stellaris CD 1.01. (I have had the opportunity to use a licensed version too, but only for a few days.)

(If I find time to create a contest-project I will use this method too.)

Limitations of MDK-ARM eval

At first I've tried to find out about the limitations of the evaluation-version. From the uVision online-help:

The Evaluation Version Restrictions are:

  1. Image ROM size of the generated application is limited to 16KB maximum.
  2. The linker does not accept scatter-loading description files for sophisticated memory layouts.
  3. The base address for code/constants is restricted to 0xXX000000, 0xXX800000, or 0x00080000.
  4. It is not possible to generate position independent code or data.
  5. The compiler does not generate a listing file.
  6. You cannot use the Evaluation Version to create commercial products.

Restriction 1 is rather obvious and the most important one. O.K. Keil/ARM wants to earn money with the product. With 16kB a lot can be done but for a larger project esp. when using arrays for fonts or bitmaps the ROM-Image can easily extend 16kB even if the size of the program- code is rather small.

Because of restriction 2 one can not simply replace the linker-script by a scatter-file. (This has been my initial plan.)

I suppose the restrictions 3 and 4 are just there to ensure that no one can use "tricks" to circumvent restriction 1.

I do not understand restriction 5. If one wants to evaluate the quality of a toolchain and can not look into the listings to see how well (or bad) the compiler works an evaluation-version misses it's purpose ... off topic here.

The reason for restriction 6 is obvious. And the reason why I use the GNU-Toolchain even for projects which would "fit" into 16kB. (Yes, sometimes I can sell software and designs or work on contract-basis. The web-pages do not show everything I have done...)

Example: Porting a project created with GNU-Tools to Keil/RV

I will show the needed steps by the "interrupts"-example. O.k., I know the Keil MDK already includes a version of this example for the RV-toolchain and the example fits into 16kB too - But hey, it's just an example: You should get the idea how to convert your own project from this.

Starting point is my GNU-port of the interrupts-example which consists of the following files:

In addition the projects uses the Stellaris Driver-Library compiled with the gcc (Codesourcery-release 2006-q1).

Keil uVision is an IDE so the Makefile will be replaced by project-settings. startup_gcc.c will be replaced by Startup.s and the linker-settings are also done in the IDE.

  1. Backup your project!
  2. Start uVision
  3. No makefile-support so a uVision projects has to be created: Project->New Project
  4. Select the Path of the project: For his example I have used a copy of the interrupts-example located in: C:\WinARM\examples\lm3s811_evalboard\examples\interrupts_rv
  5. In filename (German Windows: Dateiname) type the name of your project. I have used interrupts_rv.
  6. In the following dialog select the device Luminary Micro-> LM3S811
  7. At the right side of the screen (Project Workspace) select the "Files" tab. Expand the tree for "Target 1"
  8. Right-click on "Source Group 1" and select "Add files to Group..." from the context menu. Now select the c-Source- files. You may have to browse thru the directories for pdc.c, uvision.c and osram96x16.c. Do not add startup_gcc.c.
  9. Ups? no startup_gcc.c? Yes. With the full version it might be possible to re-use startup_gcc.c and include some preprocessor-options so it could be compiled with gcc and the RV compiler but since scatter-files are not available in the evaluation version I don't see a method to place the the interrupt-vectors and the stack at absolute positions in memory with "c-only"-code. So let's use the "Keil-method": Copy the "generic" Startup.s from Keil for Luminary devices into the project directory. On my system this file is located in C:\Keil\ARM\RV30\Startup\Luminary.
  10. Include Startup.s into the source-files (see 8, Select "Asm source files" from file-type drop-down)
  11. Now all files are "in place". Let's try to build the project to see what happens: Project->Build target. Here I've got the error message that CA can not be executed. Reason: wrong toolchain selected. uVision also supports the old Keil compiler and (somehow) the GNU-Toolchain. Select Project->Components, Environment..., select "Folders/Extensions"-Page and mark "Use Realview Compiler". Project->Build target again, much better now but still an error...
  12. Here I've got the error-message: cannot open..."diag.h". This is because the search-path for include-files is not yet complete. Right-click on "Target 1" in the Project-Workspace and select "Options for Target...", select Page "C/C++", type ..\..\utils in the field "Include Paths" - just this one since the driver-library- headers are already somewhere in the uVision default include-path. If your project uses additional header- files in other directories, add these directories here too.
  13. Project->Build target (or hit F7). This compiles now. But linking fails. Obviously the functions of the driver-library are not available to the linker. So let's add it too: Right-click on "Source Group 1" in the Project-Workspace and select "Add files...", set file-type to "Library file" and search for the file DriverLib.lib. On my system it's located in C:\Keil\ARM\RV30\LIB\Luminary\DriverLib.lib Hit F7. No more linker-errors.
  14. The project-specific modifications from startup_gcc have to be ported to the Startup.s assembler-code. These are the stack-size and the pointers to the interrupt-handlers. Let's start with the stack-size: Open Startup.s in the editor (double-click in Proj.- Workspace). In the startup_gcc.c the stack size is given as 64 with the unit DWORDS (4 Bytes). In Startup.S the value is given in bytes. So we have to set the stack-size to 4*64=0x100 (which is the default in the Keil template: Stack EQU 0x00000100)
  15. There are three interrupt-handlers used in the example: IntGPIOa, void IntGPIOb and IntGPIOc (there my be others and more in your project but you should get the idea). The ISR's have to be marked as "available" (EXTERN) in the Startup.s and the interrupt-vectors for have to point to them. Change the default Startup.S from:
    ;******************************************************************************
    ; The vector table.
    ;******************************************************************************
    Vectors
            DCD     StackMem + Stack            ; Top of Stack
            DCD     Reset_Handler               ; Reset Handler
    [...]
            DCD     IntDefaultHandler           ; SysTick Handler
            DCD     IntDefaultHandler           ; GPIO Port A
            DCD     IntDefaultHandler           ; GPIO Port B
            DCD     IntDefaultHandler           ; GPIO Port C
            DCD     IntDefaultHandler           ; GPIO Port D
            DCD     IntDefaultHandler           ; GPIO Port E
            DCD     IntDefaultHandler           ; UART0
    [...]
    
    to:
    ;******************************************************************************
    ; The vector table.
    ;******************************************************************************
            EXTERN  IntGPIOa
            EXTERN  IntGPIOb 
            EXTERN  IntGPIOc
    ; more EXTERNs here if needed, remind the spaces before EXTERN
    
    Vectors
            DCD     StackMem + Stack            ; Top of Stack
            DCD     Reset_Handler               ; Reset Handler
    [...]
            DCD     IntDefaultHandler           ; SysTick Handler
            DCD     IntGPIOa           ; GPIO Port A ; project-specific handler
            DCD     IntGPIOb           ; GPIO Port B ; project-specific handler
            DCD     IntGPIOc           ; GPIO Port C ; project-specific handler
            DCD     IntDefaultHandler           ; GPIO Port D
            DCD     IntDefaultHandler           ; GPIO Port E
            DCD     IntDefaultHandler           ; UART0
    [...]
    
  16. One last thing I have noticed in the examples that come with uVision is the setting of the "entry-point" in the linker-options. I don't think its important for the shown example since the startup-code is "in place". But it can do no harm: Proj.-Worksp->right-click "Target 1"-> select "Options for..."->select Page "Linker"-> type --entry Reset_Handler into the "Misc Controls" field.
  17. rebuild -> upload to flash -> test.

Futher information can be found from the examples included in the MDK-ARM (C:\Keil\ARM\RV30\Boards\...).

That's it for now. Feedback welcome.




To my ARMv7/Cortex M3-Projects page


Martin Thomas
mail-adress: mthomas /at/ rhrk /dot/ uni-kl /dot/ de


07995 hits since October 19, 2006
Last mod.: Sunday, 05-Nov-2006 17:56:42 CET