Developing for legacy ARM devices with VisualGDB

This tutorial shows how to develop and debug firmware for legacy ARM devices that are not directly supported by the VisualGDB wizard. Most of the modern ARM Cortex-based devices are supported directly and can be simply selected in the VisualGDB wizard. However if you are targeting an older device that we don’t support directly, follow the instructions below to get it to work.

In order to build the firmware for your ARM-based device you can use the ARM toolchain that comes with VisualGDB, but you will need to locate and provide the following information:

  • Compiler flags.
    These flags tell the compiler which ARM core to target (e.g. -mcpu=arm7tdmi). The flags relevant to the target are -mcpu, -mfpu, -march, -mfloat, -meabi. The relevant flags can also include preprocessor macro definitions (-D flag), include paths (-I flag) entry point specification (–entry flag) and additional libraries (-l flag).
  • Peripheral header files for your device.
    These headers define the locations of the peripheral registers so that your program can refer to them by structured names like PIOA->PIO_ODR instead of raw addresses.
  • Startup code.
    This is typically an assembly (sometimes C) file that does basic initialization of your device: copying initial values of variables from FLASH to RAM, defining interrupt handler tables, sometimes even setting up the system clocks.
  • Linker scripts.
    These scripts define how to place code and data in memory. They typically consist of a device-specific section defining the addresses and sizes of RAM and FLASH and a generic section defining that sections like .text go to FLASH, sections like .data – to RAM, and so on. The linker script typically has the extension of .lds, .ld or .x and is passed to GCC via the -T flag.
  • Driver library (optional).
    Often the device manufacturer provides a library wrapping the register-level operations into higher-level function. E.g. a call to manufacturer-provided USART_Configure() function can replace manual computation of prescalers and mode flags put in the hardware registers.

All components listed above can be typically extracted from the code example packages provided by the device manufacturer. If the manufacturer provides samples for different compilers, download the ones targeted for GCC (sometimes referred to as GNU).

We will now show how to setup a project for the AT91SAM9G25 device using the sample files obtained from the Atmel website:

  1. Download the GNU Software package from the Atmel AT91SAM9G25 tools page.
  2. If you don’t have our ARM toolchain installed yet, download and install it now.
  3. Open the sam9g25-ek\examples\usart_serial folder and examine its contents. It contains a source file and a Makefile in the build\gcc subdirectory. The easiest way to get all GCC flags in one line is to adjust the Makefile to use arm-eabi- instead of arm-none-eabi- prefix (to match arm-eabi-gcc.exe in our toolchain) and build it by running make from command line:
  4. The build will fail due to missing libraries, but the log.txt file will contain the GCC arguments:

    You can also extract those flags by looking through the Makefile.
  5. The Makefile did not specify the CPU type (-mcpu). We will look up the CPU core from the Atmel PDFs (ARM926EJ-S) and add a corresponding -mcpu flag: -mcpu=arm926ej-s
  6. We will now group the extracted GCC arguments according to their meaning:
    Argument Value
    Include directories C:\Atmel\sam9g25-ek\libraries\libchip_sam9xx5
    Preprocessor macros sam9g25 TRACE_LEVEL=4 ddram
    Entry point argument -Wl,–entry=entry
    Linker script C:\Atmel\sam9g25-ek\libraries\libboard_sam9xx5-ek\resources\gcc\sam9g25\ddram.ld
    Additional libraries gcc c chip_sam9g25_gcc_relboard_sam9g25_ek_gcc_rel
    CPU type flag -mcpu=arm926ej-s

    We will simplify the build process and instead of building separate chip_sam9g25 and board_sam9g25_ek libraries we’ll include their source files into the project.

  7. Now that we have all the necessary flags (except the startup code) we can create a Visual Studio project. Start Visual Studio and use the VisualGDB Embedded Project Wizard:01-wizard
  8. Continue with the default settings on the first page:02-prjtype
  9. On the next page choose the ARM toolchain (VisualGDB will install it automatically if missing) select “Specify flags manually” and enter the flags extracted from the Makefile: 03-flags
  10. On the next page proceed with the default empty sample:04-sample
  11. On the Debug Method page select “GDB Simulator”. We will later configure OpenOCD to do real JTAG debugging, but first we’ll get the project to build:05-gdbsim
  12. Press Finish to generate the project. Delete the default CPP file and add main.c from the usart_serial example. Build the project:06-build
  13. Although the build succeeds, the resulting image won’t be usable because the linker could not find the entry point (entry) and hence discarded all other code as unreferenced. This happened because we did not include the startup code into our project. If you search the source package for files containing the word “entry”, you will find that it is defined in sam9g25-ek\libraries\libboard_sam9xx5-ek\source\board_cstartup_gnu.S. Add this file to the project and build it again:07-linkerr
  14. Now we will get several “unresolved reference” errors because we did not include the files from the chip and board libraries yet. We will now add the source files from those libraries to our project. As some of the files from the libraries are meant for other devices, we can use one of 2 ways to determine the necessary files:
    • Add all files and then remove those that don’t compile
    • Add files that define functions reported as undefined

    We will use the first approach for the chip library and the second one for the board library:

    • From the libchip_sam9xx5 library we add *.c except the USB files
    • From the libboard_sam9xx5-ek library we add the following files:
      • board_lowlevel.c
      • board_memories.c
      • dma_hardware_interface.c
      • dmad.c
  15. If you build the project now, the linker will complain about the missing definition of “end”. This happens because the Atmel samples were tested with an older version of the C library that used a different name (_end instead of end):08-end
  16. To fix this, edit the linker script (ddram.ld) to define the end symbol the same way it defines _end:
  17. Now you should be finally able to build the sample project:09-build
  18. Now we will configure JTAG debugging. Open VisualGDB Project Properties, go to the Debug Settings and select OpenOCD there. If it’s missing, click on “Install support for additional debug methods” link to install it automatically. Then specify the interface and target scripts according to your setup:10-openocd
  19. The Atmel source package also provides the GDB initialization script that configures DDRAM. Add a command calling it (source <full-path-to-ddram.gdb>) to the post-target-selection commands:11-gdb
  20. Press OK to save the settings. Now you should be able to debug your device using VisualGDB.