Next: Config File Guidelines, Previous: Running, Up: Top [Contents][Index]
To use OpenOCD with your development projects, you need to do more than just connect the JTAG adapter hardware (dongle) to your development board and start the OpenOCD server. You also need to configure your OpenOCD server so that it knows about your adapter and board, and helps your work. You may also want to connect OpenOCD to GDB, possibly using Eclipse or some other GUI.
Today’s most common case is a dongle with a JTAG cable on one side (such as a ribbon cable with a 10-pin or 20-pin IDC connector) and a USB cable on the other. Instead of USB, some dongles use Ethernet; older ones may use a PC parallel port, or even a serial port.
In the same vein, make sure the voltage levels are compatible. Not all JTAG adapters have the level shifters needed to work with 1.2 Volt boards.
In the best case, the connector is keyed to physically prevent you from inserting it wrong. This is most often done using a slot on the board’s male connector housing, which must match a key on the JTAG cable’s female connector. If there’s no housing, then you must look carefully and make sure pin 1 on the cable hooks up to pin 1 on the board. Ribbon cables are frequently all grey except for a wire on one edge, which is red. The red wire is pin 1.
Sometimes dongles provide cables where one end is an “octopus” of color coded single-wire connectors, instead of a connector block. These are great when converting from one JTAG pinout to another, but are tedious to set up. Use these with connector pinout diagrams to help you match up the adapter signals to the right board pins.
For USB-based JTAG adapters you have an easy sanity check at this point:
does the host operating system see the JTAG adapter? If you’re running
Linux, try the lsusb
command. If that host is an
MS-Windows host, you’ll need to install a driver before OpenOCD works.
Talk with the OpenOCD server using
telnet (telnet localhost 4444
on many systems) or GDB.
See GDB and OpenOCD.
There are many ways you can configure OpenOCD and start it up.
A simple way to organize them all involves keeping a single directory for your work with a given board. When you start OpenOCD from that directory, it searches there first for configuration files, scripts, files accessed through semihosting, and for code you upload to the target board. It is also the natural place to write files, such as log files and data you download from the board.
There are two basic ways of configuring OpenOCD, and a variety of ways you can mix them. Think of the difference as just being how you start the server:
Here is an example openocd.cfg file for a setup using a Signalyzer FT2232-based JTAG adapter to talk to a board with an Atmel AT91SAM7X256 microcontroller:
source [find interface/ftdi/signalyzer.cfg] # GDB can also flash my flash! gdb_memory_map enable gdb_flash_program enable source [find target/sam7x256.cfg]
Here is the command line equivalent of that configuration:
openocd -f interface/ftdi/signalyzer.cfg \ -c "gdb_memory_map enable" \ -c "gdb_flash_program enable" \ -f target/sam7x256.cfg
You could wrap such long command lines in shell scripts, each supporting a different development task. One might re-flash the board with a specific firmware version. Another might set up a particular debugging or run-time environment.
Important: At this writing (October 2009) the command line method has problems with how it treats variables. For example, after -c "set VAR value", or doing the same in a script, the variable VAR will have no value that can be tested in a later script.
Here we will focus on the simpler solution: one user config file, including basic configuration plus any TCL procedures to simplify your work.
A user configuration file ties together all the parts of a project in one place. One of the following will match your situation best:
Three main types of non-user configuration file each have their own subdirectory in the scripts directory:
Best case: include just two files, and they handle everything else. The first is an interface config file. The second is board-specific, and it sets up the JTAG TAPs and their GDB targets (by deferring to some target.cfg file), declares all flash memory, and leaves you nothing to do except meet your deadline:
source [find interface/olimex-jtag-tiny.cfg] source [find board/csb337.cfg]
Boards with a single microcontroller often won’t need more than the target config file, as in the AT91SAM7X256 example. That’s because there is no external memory (flash, DDR RAM), and the board differences are encapsulated by application code.
For example, there may be configuration files for your JTAG adapter and target chip, but you need a new board-specific config file giving access to your particular flash chips. Or you might need to write another target chip configuration file for a new chip built around the Cortex-M3 core.
Note: When you write new configuration files, please submit them for inclusion in the next OpenOCD release. For example, a board/newboard.cfg file will help the next users of that board, and a target/newcpu.cfg will help support users of any board using that chip.
Reuse the existing config files when you can. Look first in the scripts/boards area, then scripts/targets. You may find a board configuration that’s a good example to follow.
When you write config files, separate the reusable parts (things every user of that interface, chip, or board needs) from ones specific to your environment and debugging approach.
gdb-attach
event handler that invokes
the reset init
command will interfere with debugging
early boot code, which performs some of the same actions
that the reset-init
event handler does.
arm9 vector_catch
command (or
its siblings xscale vector_catch
and cortex_m vector_catch
) can be a time-saver
during some debug sessions, but don’t make everyone use that either.
Keep those kinds of debugging aids in your user config file,
along with messaging and tracing setup.
(See Software Debug Messages and Tracing.)
A few project-specific utility routines may well speed up your work. Write them, and keep them in your project’s user config file.
For example, if you are making a boot loader work on a board, it’s nice to be able to debug the “after it’s loaded to RAM” parts separately from the finicky early code which sets up the DDR RAM controller and clocks. A script like this one, or a more GDB-aware sibling, may help:
proc ramboot { } { # Reset, running the target's "reset-init" scripts # to initialize clocks and the DDR RAM controller. # Leave the CPU halted. reset init # Load CONFIG_SKIP_LOWLEVEL_INIT version into DDR RAM. load_image u-boot.bin 0x20000000 # Start running. resume 0x20000000 }
Then once that code is working you will need to make it boot from NOR flash; a different utility would help. Alternatively, some developers write to flash using GDB. (You might use a similar script if you’re working with a flash based microcontroller application instead of a boot loader.)
proc newboot { } { # Reset, leaving the CPU halted. The "reset-init" event # proc gives faster access to the CPU and to NOR flash; # "reset halt" would be slower. reset init # Write standard version of U-Boot into the first two # sectors of NOR flash ... the standard version should # do the same lowlevel init as "reset-init". flash protect 0 0 1 off flash erase_sector 0 0 1 flash write_bank 0 u-boot.bin 0x0 flash protect 0 0 1 on # Reboot from scratch using that new boot loader. reset run }
You may need more complicated utility procedures when booting from NAND. That often involves an extra bootloader stage, running from on-chip SRAM to perform DDR RAM setup so it can load the main bootloader code (which won’t fit into that SRAM).
Other helper scripts might be used to write production system images, involving considerably more than just a three stage bootloader.
Sometimes you may want to make some small changes to the software
you’re developing, to help make JTAG debugging work better.
For example, in C or assembly language code you might
use #ifdef JTAG_DEBUG
(or its converse) around code
handling issues like:
It’s rarely a good idea to disable such watchdogs, since their usage needs to be debugged just like all other parts of your firmware. That might however be your only option.
Look instead for chip-specific ways to stop the watchdog from counting while the system is in a debug halt state. It may be simplest to set that non-counting mode in your debugger startup scripts. You may however need a different approach when, for example, a motor could be physically damaged by firmware remaining inactive in a debug halt state. That might involve a type of firmware mode where that "non-counting" mode is disabled at the beginning then re-enabled at the end; a watchdog reset might fire and complicate the debug session, but hardware (or people) would be protected.1
WFI
instruction (or its coprocessor equivalent, before ARMv7).
You may want to disable that instruction in source code,
or otherwise prevent using that state,
to ensure you can get JTAG access at any time.3
For example, the OpenOCD halt
command may not
work for an idle processor otherwise.
To work with boards like this, enable a short delay loop the first thing after reset, before "real" startup activities. For example, one second’s delay is usually more than enough time for a JTAG debugger to attach, so that early code execution can be debugged or firmware can be replaced.
Your application may want to deliver various debugging messages over JTAG, by linking with a small library of code provided with OpenOCD and using the utilities there to send various kinds of message. See Software Debug Messages and Tracing.
Chip vendors often provide software development boards which are highly configurable, so that they can support all options that product boards may require. Make sure that any jumpers or switches match the system configuration you are working with.
Common issues include:
Such explicit configuration is common, and not limited to booting from NAND. You might also need to set jumpers to start booting using code loaded from an MMC/SD card; external SPI flash; Ethernet, UART, or USB links; NOR flash; OneNAND flash; some external host; or various other sources.
Your board.cfg file may also need to be told this jumper
configuration, so that it can know whether to declare NOR flash
using flash bank
or instead declare NAND flash with
nand device
; and likewise which probe to perform in
its reset-init
handler.
A closely related issue is bus width. Jumpers might need to distinguish between 8 bit or 16 bit bus access for the flash used to start booting.
Plus you should of course have reset-init
event handlers
which set up the hardware to match that jumper configuration.
That includes in particular any oscillator or PLL used to clock
the CPU, and any memory controllers needed to access external
memory and peripherals. Without such handlers, you won’t be
able to access those resources without working target firmware
which can do that setup ... this can be awkward when you’re
trying to debug that target firmware. Even if there’s a ROM
bootloader which handles a few issues, it rarely provides full
access to all board-specific capabilities.
Note that many systems support a "monitor mode" debug that is a somewhat cleaner way to address such issues. You can think of it as only halting part of the system, maybe just one task, instead of the whole thing. At this writing, January 2010, OpenOCD based debugging does not support monitor mode debug, only "halt mode" debug.
See chapter 8 "Semihosting" in ARM DUI 0203I, the "RealView Compilation Tools Developer Guide". The CodeSourcery EABI toolchain also includes a semihosting library.
As a more polite alternative, some processors have special debug-oriented registers which can be used to change various features including how the low power states are clocked while debugging. The STM32 DBGMCU_CR register is an example; at the cost of extra power consumption, JTAG can be used during low power states.
Next: Config File Guidelines, Previous: Running, Up: Top [Contents][Index]