Skip to content
/ DebugDue Public

Emulates a Bus Pirate OpenOCD JTAG adapter with an Arduino Due

Notifications You must be signed in to change notification settings

rdiez/DebugDue

Repository files navigation

Overview

The main goal of this project is to provide an up-to-date, comfortable but fully-featured bare-metal C programming environment for the Arduino Due. However, most of the techniques used are generic and can be applied to other Cortex-Mx microcontrollers, or even to other bare-metal embedded environments. This project already supports building and debugging a minimal firmware running on a simulator (Qemu-emulated Stellaris board).

The makefile that builds the GCC-based toolchain is completely generic (it knows nothing about the Arduino Due), so that you can use it to create bare-metal toolchains for other ARM Cortex microcontrollers, like the STM32 family. In fact, the toolchain builder makefile is probably the most valuable outcome of this project.

A secondary goal of this project is to convert the Arduino Due into a JTAG adapter by emulating a Bus Pirate from Dangerous Prototypes , so that it is compatible with OpenOCD .

This means that you can use an Arduino Due (acting as a JTAG adapter) in order to debug with GDB your own code running on a second Arduino Due.

You will find more information about the Arduino Due on my website at https://rdiez.miraheze.org/wiki/Hacking_with_the_Arduino_Due .

Bare Metal C Environment

The bare metal programming environment has the following features:

  • Event loop (super loop) architecture.

    The developer must manually call every library function and has therefore full control at any point in time.

    There are no OS background tasks or threads to worry about.

  • C 17 support, including the STL library.

    I regularly use the Standard Template Library, which is small and fast. I wouldn't recommend using the huge iostream library though.

    Which version of the C standard you can use actually depends on the selected GCC version.

    C is not actually a must for your firmware, you can still take the toolchain and most of the example code and write in plain C if you like.

  • C exceptions.

    See separate section below for more information.

  • Newlib or Picolibc with malloc support.

    The Newlib distribution includes Libgloss, but this project does not use it.

    In this project configuration, there is no support for a filesystem or file descriptors. Syscalls like isatty or lseek are not available. If you need such features, you should probably look for an RTOS .

    You can use sprintf and the like in your code, but no printf to stdout or any other FILE-based routines like fgetc. It is not hard to implement basic support for FILE-based stdout for logging purposes, but it is usually not worth the trouble.

    Note that you can get a long way without such file features. With a library like lwIP , you can build a device with TCP/IP support and even with an embedded web server and still not miss those syscalls.

  • Assertions.

    Assertions can increase the binary size considerably. There is a way to make debug binaries smaller by reducing assertion comfort, see the toolchain builder makefile for more information.

  • Debug console.

    Some useful generic commands are available: memory usage, CPU load in the last 60 seconds, compiler version used to build, last reset cause, uptime display, etc.

    Your code can write log messages to the console asynchronously, which means that normal execution carries on while the message characters are sent to the USB virtual serial port in the background (interrupt driven). If the background console output buffer overflows, console text will be lost, but the user will always see an indication that some text was lost at the position where the overflow occurred.

  • Small library of common functions for embedded development.

    Most basic functions you need to get started have been implemented: Busy wait loop helpers, basic text parsing, I/O utilities, stack usage tools, uptime calculation, a template-based circular buffer implementation, etc.

  • Short start-up delay for easy JTAG debugging right after reset.

    Atmel and STM32 microcontrollers start with a low CPU frequency. One of the first initialisation tasks is to drastically increase this frequency. Without a short pause afterwards, JTAG probes that do not support the RCLK (adaptive clock speed) signal cannot connect to the board and stop the firmware early enough after a hardware reset with the SRST signal, which can make debugging from the start difficult.

  • Firmware cross-compiled with the standard GCC and GNU Autotools .

    The configure.ac and Makefile.am files are clean and properly commented.

    There is support for debug and release firmware builds.

  • LTO release builds.

  • The firmware build script can automatically start a debugger over a JTAG connection.

    Alternatively, you can program your Arduino Due with the 'bossac' tool.

    The build script can also start Qemu and attach GDB in order to debug the emulated Stellaris board from the start.

  • In order to help trim the firmware size, the build script can optionally generate a readelf dump, and objdump with code disassembly, a list of ELF objects sorted by size, and a list of all text strings found in the final binary.

  • The build script can optionally use ccache, which usually shortens recompilation times.

  • The build script can skip reprogramming the target if the binary has not changed.

    Restarting the same firmware under the debugger is almost instantaneous.

About C Exceptions

Contrary to popular belief, C exception handling does not need many resources. I have been generating dynamic error messages in readable English using C exceptions on microcontrollers with as little as 16 KiB SRAM for years, with 'plenty' of memory to spare.

You do need to patch GCC though in order to disable the C exception emergency buffer, or you will lose 2 KiB RAM, or maybe more, depending on your target. The toolchain builder makefile that comes with this project does that for you.

There is this persistent prejudice that support for C exceptions "doubles the code size", but that is not true, unless you are comparing with the traditional C language approach of not checking for errors at all!

There is an initial penalty to pay for the code that handles stack unwinding. On an 32-bit ARM Cortex-M3 microcontroller, that adds around 8 kB of code size, 8 bytes of initialised data size and 28 bytes of BSS size (tested on GCC 11.2).

C exceptions use malloc, but the 'nano' allocator needs only around 1.5 kB of code size.

The compiler automatically generates exception unwinding tables, which are optimised for space. Such tables tend to be similar across routines, so that the linker should collapse many duplicates.

If you handle errors the traditional way, with if ( failed ) then... statements, then both C and C normally end up needing more Flash/ROM space than the C exception way. Most C compilers use a technique called "zero cost exception handling" which replaces such if ( failed ) checks with exception unwinding tables. That is the reason why the resulting code is usually smaller, and even faster in the normal case (no errors/exceptions).

The drawback is that throwing an exception is slow and uses malloc, but exceptions should be the exception!

You can use -fno-exceptions to disable C exception support and drop all the overhead listed above.

Limitations of the Interrupt Context

The 'bare metal' environment has no operating system with thread management and concurrency protection. There is nothing standing between a CPU interrupt and your interrupt handler. You get low latency and full control, but you cannot use any C runtime library routines that could break on reentrancy.

Items forbidden in interrupt context are:

  • Dynamic memory management with malloc, free and the like.

  • C exceptions, mainly because the use malloc.

  • C library functions that use timezones like localtime.

  • Any file I/O using a FILE structure is at least suspect.

  • Anything that could touch errno, since there is only one global instance.

  • Many other innocent-looking C library functions. Some are listed below.

rand maintains hidden state and is generally not reentrant.

Newlib's sprintf can suddently call malloc for floating-point arguments. Picolibc does not have this problem. You will find more information on this here .

Initialisation and Termination

You should generally refrain from using atexit or __cxa_atexit in your software.

Do not define global or static C object instances that need constructor or destructor calls. Such destructor calls will generate hidden calls to atexit . Instead, initialise everything manually.

Otherwise, you'll have a hard time trying to control the initialisation and destruction order in a portable manner, as the initialisation order of such objects is not guaranteed in the language standard.

atexit destructors will make it harder to detect memory leaks on termination, and atexit will always waste memory, because it is hard to know in advance how many atexit slots the firmware will need, so Newlib will normally overallocate.

In case you are wondering, you should implement clean termination even on firmware that is never supposed to terminate. You can implement termination as part of a debug or test routine that restarts the firmware. It will help you design cleaner code. Without proper termination, it will be very hard to identify eventual memory or resource leaks.

Toolchain Builder Makefile

You will find a makefile to build your own GCC-based, cross-compiling toolchain in the Toolchain subdirectory. See the installation instructions below for more information on how to build one yourself.

Instead of building your own, you could download a pre-built toolchain for ARM Cortex microcontrollers. Popular toolchains are:

https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain

https://launchpad.net/gcc-arm-embedded

However, building your own toolchain is not difficult with the automated makefile that this project provides. Here are some reasons why it is better to build your own:

  • You are not downloading binaries from any private companies or unofficial volunteers.

    The toolchain builder makefile from this project downloads only source code, and only from their official sites. You can confirm this simply by inspecting the makefile contents.

    There is support for building toolchains offline with pre-downloaded sources.

  • No root privileges necessary.

    You can build several toolchains, and install them into separate subdirectories under your home directory. So you can test a new toolchain while keeping the old one ready.

  • Debug into any linked libraries.

    If you keep the toolchain sources on your disk, you will be able to step into all toolchain-provided libraries like Newlib when debugging.

  • You have complete control.

    You know exactly what goes into your toolchain, and what compiler flags you are using for all components.

    For some reason, pre-built toolchains tend to be poorly documented. You cannot easily find out what compiler flags Newlib, libstdc or libgcc were built with. But they all get linked into your final binary.

    The build flags in this project's makefile are optimised for small embedded targets, see for example --enable-newlib-nano-malloc in the makefile. But you can easily change all such flags yourself.

    There some also some bonuses in this project's toolchain for convenience or code size. There is a patch to reduce the RAM footprint when you use C exceptions. Assertion code size can be reduced, search for ASSERT_TYPE_FULL in the source code for assert options. And all toolchain components that land into your firmware are built ready for LTO.

  • You have more flexibility.

    You can comfortably choose which component versions you want in your toolchain. This is useful if you hit a bug in one of them, which has happened to be more often than I thought.

    Or you can use the latest versions straight away, without waiting for a new pre-built toolchain to be released.

    You can also generate a debug toolchain, in order to troubleshoot the toolchain components themselves.

  • A little more performance when building and debugging.

    The toolchain is built with -march=native by default, so it performs optimally on your current CPU. There is often a measurable difference when -march=native is used. If you look at the benchmarks under https://www.phoronix.com/ , you'll realise that many of the test binaries are built with this flag.

The toolchain builder makefile does not support building a multilib toolchain. You will need a separate toolchain for each CPU type you need (one for Cortex-M0, one for Cortex-M4F, and so on).

DebugDue Fimware

This firmware converts the Arduino Due into a JTAG adapter. It is similar to the Bus Pirate when used as a JTAG adapter.

The Arduino Due has a faster USB port and a faster CPU, so it can easily outperform the Bus Pirate.

I made a quick test, and GDB's 'load' command reports a transfer rate of 24 KiB/s with the DebugDue firmware (release build) when programming a second Arduino Due board, compared to 8 KiB/s with the Bus Pirate. When programming an STM32429I-EVAL1 evaluation board, which has an STM32F429 microcontroller, GDB reports 31 KiB/s compared to 24 KiB/s with its built-in SWD adapter, and I have obtained rates of around 84 KiB/s with an Atmel AT91SAM9G20-EK board when writing to SDRAM instead of Flash.

Note that I had to manually patch OpenOCD version 0.8.0 as of May 2013 in order for setting "buspirate_speed fast" to work with the Bus Pirate board, otherwise you have to revert back to "buspirate_speed normal", and then you get just 1 KiB/s when using the Bus Pirate as a JTAG adapter. This should no longer be necessary for OpenOCD 0.9.0. If you modify OpenOCD in order to use bigger USB packets, you can reach slightly higher speeds with the DebugDue.

There are some caveats when using the Arduino Due with the DebugDue firmware as a JTAG adapter:

  • The JTAG interface can only handle 3.3 V signals.

  • There is no JTAG clock speed setting, just like the current Bus Pirate / OpenOCD combination (as of May 2013).

    The JTAG clock always runs at maximum speed, which may be too fast for some devices. I made some imprecise measurements, and the resulting TCK rate is around 3 MHz. This means there is no JTAG adaptive clocking support either.

  • The JTAG signals are driven by software and their timings are not clean.

    For example, TCK does not stay 50 % of the time high. The lowest-level JTAG bit shifting routine should be rewritten in assembly. Help in this area would be appreciated.

  • The Arduino Due pull-ups are too weak to be of any use, see comments about setting 'buspirate_pullup' below.

You will find more information about my experience with the Arduino Due on my website at https://rdiez.miraheze.org/wiki/Hacking_with_the_Arduino_Due .

Empty Firmware

There is an "EmptyFirmware" directory that generates a dummy firmware which performs minimal initialisation, prints some basic information (including the code and data size values explained below) to the Arduino Due's "programming" USB virtual serial port and then stops (it waits forever). This project exists for the following reasons:

1) The project can serve as a starting point for other projects.
2) With the Empty Firmware you can reliably stop the CPU over the JTAG interface.

That is the best way to measure OpenOCD and GDB transfer speeds or to stress-test the JTAG interface.

3) You can use this project to perform code size experiments.

The Empty Firmware is very small and you can easily see how much code size isolated features or components contribute to the final binary size.

You should get byte sizes similar to the following with an optimised (release) build with asserts turned off:

Code size:             14,204
Initialised data size:  1,304
BSS size:               2,160
Total:                 17,668

This firmware also has C exception support, which automatically pulls in malloc() support from Newlib.

Adding a call to sprintf() pulls in the corresponding formatting code from Newlib and yields the following sizes:

Code size:             23,300
Initialised data size:  1,360
BSS size:               2,160
Total:                 26,820

That means an increase of 9,152 bytes the first time you touch a printf-style function.

Qemu Stellaris Firmware

There is an "QemuFirmware" directory that generates a dummy firmware which performs minimal initialisation, prints some basic information like the firmware code and data sizes to the emulated serial console, and then stops (it waits forever).

With this project you can start developing and debugging embedded firmware in an accurate simulation without any hardware whatsoever. There is no programming (flashing) time, the firmware starts immediately.

Installation Instructions

Installing a Binary File

Pre-built binary files for the Arduino Due are available in a separate repository at https://github.com/rdiez/DebugDueBinaries . These binaries can be flashed with the standard Arduino Due tools. Jump straight to section "Flash the new firmware" below in order to flash a pre-built binary file.

Building by Hand

1) Build the toolchain (the GCC compiler for the target embedded system).

Change to the 'Toolchain' subdirectory and type "make help" for the prerequisites and detailed information.

These are the commands I normally use to build a toolchain:

make download-tarballs-from-internet

< delete any old build and installation directories >

make CROSS_TOOLCHAIN_DIR=~/DebugDue/toolchain-test-bin  \
     CROSS_TOOLCHAIN_BUILD_DIR=~/DebugDue/toolchain-test-build  \
     TOOLCHAIN_TARGET_CPU=cortex-m3  \
     --no-builtin-variables  --warn-undefined-variables  \
     --output-sync=recurse  -j 5  \
     all

My old laptop with an Intel Core i3 M 380 running at 2.53 GHz, with a traditional (rotational) hard disk, needs 35 minutes to build a toolchain.

Building on Cygwin takes longer. A much faster Intel Core i3-6100 CPU running at 3.70 GHz needs 1 hour 7 min.

For a toolchain with GCC 7.3, the installation directory needs 169 MB, and the build directory 2.1 GB. But sizes can vary depending on the build flags.

If you are experimenting with toolchains and will be building them often, you may find the following scripts handy: background.sh (limits overall performance impact and provides a convenient notification) and MountMyRamDiskIfNecessary.sh (speeds up the build considerably).

2) Download and unpack the Atmel Software Framework into some directory of your choice.

This step is not necessary for the Qemu Stellaris Firmware.

I have tested against ASF versions 3.19.0.95, 3.35.1.54, 3.40.0 and 3.49.1.105. You will probably have to manually edit file sam/utils/compiler.h and surround the definitions of macros min and max with #ifndef __cplusplus , so that they look like this:

#ifndef __cplusplus  // The user added this to avoid a clash with std::min when compiling C   sources.
 #define min(a, b)   Min(a, b)
#endif

...

#ifndef __cplusplus  // The user added this to avoid a clash with std::max when compiling C   sources.
#define max(a, b)   Max(a, b)
#endif

Without this amendment, compiling your C projects will probably fail later on with an error message related to min or max. This kind of fix is in place for sam0/utils/compiler.h , but not for sam/utils/compiler.h , at least for ASF version 3.34.2.53.

3) Build the firmware for the Arduino Due like most Autotools projects:
a) Make sure the new toolchain's 'bin' subdirectory is in the PATH.

This is so that autoconf can find GCC etc. for the Arduino Due. You can check that the PATH is right manually by verifying that this command works:

arm-none-eabi-gcc --version
b) Run autogen.sh in the 'Project' subdirectory in order to generate the configure script.
c) Build the firmware. It is best to build out of the source tree like this:
mkdir obj
cd obj
../Project/configure --prefix=/some/bin/directory --with-project="DebugDue" --host="arm-none-eabi" --with-target-arch="arm-none-eabi" --with-atmel-software-framework=<directory where the ASF is>
make  --no-builtin-rules  -j $(( $(getconf _NPROCESSORS_ONLN)   1 ))
make install

The binaries files land in the prefix directory you specified, inside a subdirectory called share . Look for files debugdue.bin and emptydue.bin .

4) Flash the new firmware.

Note that this step and most of the steps below are not necessary in case of the Qemu-emulated Stellaris firmware.

First of all, plug the USB cable into the Arduino Due's "programming" USB socket (as opposed to the "native" socket).

You will need the Bossac tool in order to download the firmware into the Arduino Due. The easiest way to get it is probably to install the official Arduino software environment. Usage example for Linux:

stty -F /dev/ttyACM0 1200 && bossac --port=ttyACM0 --force_usb_port=false --verify --write "filename.bin" --boot=1 --reset

Note that you need to leave the "/dev/" prefix out in the --port argument.

Alternatively, you could use the Arduino Due's JTAG port to download the firmware with GDB, but then you need some other JTAG adapter. I have used the Bus Pirate for that purpose.

5) Test whether the new firmware is working properly.

Plug the USB cable into the Arduino Due's "native" USB socket (as opposed to the "programming" socket).

If you are running Windows, you will be prompted the first time to install a driver for the new device, just point it to the "WindowsDriver" subdirectory. The .INF file in that subdirectory is actually just a special text file that associates DebugDue's USB IDs to Windows' own USB CDC driver, so that a standard "COMx" virtual serial port is automatically created upon connection. Unfortunately, you cannot choose a fixed serial port number (the "x" in the name), so that it will change from time to time depending on how many virtual serial ports currently exist on the system.

Linux and Mac OS do no need any drivers. On linux, a new virtual serial port device is automatically created with a name like "/dev/ttyACM0", just look for the right device name under the "/dev" directory. On Linux, the system automatically creates a symbolic link like /dev/serial/by-id/usb-Arduino_Due_JTAG_Adapter_DebugDue1-if00 , see further below for more information.

Now you should be able to connect to the virtual serial port with your favorite serial port console emulator.

For example, this Linux client allows you to conveniently quit the client-side terminal emulator (the local 'socat' command) with Ctrl C:

socat -t0  STDIO,raw,echo=0,escape=0x03  file:/dev/ttyACM0,b115200,cs8,parenb=0,cstopb=0,clocal=0,raw,echo=0,setlk,flock-ex-nb

On Windows, you can use Putty like this:

putty -serial COM8

The DebugDue firmware does not implement the usual command-line editing comfort features yet, like command history and all the standard cursor key movements.

Due to a protocol limitation, there is no welcome banner. Press the Enter key at least once to see the cursor ('<'), or type "help" for a list of available commands.

6) Find a way to address the DebugDue USB virtual serial port comfortably.

When you connect the Arduino Due to your Linux PC, you will get a new virtual serial port like /dev/ttyACM0 or /dev/ttyACM1, depending on how many serial ports currently exist on the system. If you wish to write your own scripts to automate JTAG tasks, it is desirable that the assigned device name is always the same.

The easiest way is to look under /dev/serial/by-id. Normally, when running the DebugDue firmware, you will automatically get a link like /dev/serial/by-id/usb-Arduino_Due_JTAG_Adapter_DebugDue1-if00, but the exact name may be different on your system. This link will stay the same even if you connect the Arduino Due to another USB port. Alternatively, you could use script FindUsbSerialPort.sh .

If you want to address your devices based on the USB port they are connected to, look under /dev/serial/by-path.

On many Linux systems, normal users do not have enough permissions to access USB virtual serial ports. In order to allow all users to access such ports, you will have to create a Linux udev rule. You can also specify a fixed name for a device, in order to get the same "/dev/debugdue1" filename every time you connect your DebugDue-running Arduino Due.

The DebugDue firmware uses the standard Arduino Due Vendor ID, but uses a Device ID of 0x1234 (you can change it when configuring the build). It also reports a serial number of "DebugDue1". In order to get a fixed "/dev/debugdue1" device name, create a new file called /etc/udev/rules.d/47-DebugDue.rules (as root) with the following content:

SUBSYSTEM=="tty" ATTRS{idVendor}=="2341" ATTRS{idProduct}=="1234" ATTRS{serial}=="DebugDue1" MODE="0666" SYMLINK ="debugdue1"

You can also add an entry for the standard Arduino Due "programming" USB port. For example:

SUBSYSTEM=="tty" ATTRS{idVendor}=="2341" ATTRS{idProduct}=="003d" MODE="0666"

Restarting udev with "sudo restart udev" should not be necessary for the new rule file to be taken into account.

Theoretically, you can add a GROUP="some_group" option in order to restrict access to a particular user group, but I could not make it work on my system.

The next time you plug the Arduino Due, if it is running the DebugDue firmware, an entry like /dev/ttyACM1 will still be created, but there will also be a /dev/debugdue1 link pointing to the right one.

If you are using the second example rule, you will not get a user-defined link, but the automatically-created links under /dev/serial/by-id and so on should now work for any user account.

7) Connect the JTAG pins to the target device.

You can see the JTAG signal pin numbers on the Arduino Due and their current state (as if they all were inputs) with command "JtagPins". This is the kind of output generated:

Input status of all JTAG pins:
TDI   (pin 42): high  |  GND2  (pin 43): low
 -    (pin 44):  -    |  nTRST (pin 45): high
TMS   (pin 46): high  |  nSRST (pin 47): high
TDO   (pin 48): high  |  VCC   (pin 49): high
TCK   (pin 50): high  |  GND1  (pin 51): low

That is the same pin layout as the 10-pin Altera USB Blaster connector, with some Atmel additions from the AVR JTAG header. Pin 1 on the USB Blaster (TCK) corresponds then to pin 50 on the Arduino Due.

If you are connecting to the JTAG interface of a second Arduino Due, the layout is different, look at the Arduino Due's schematic for details. You will probably need an adapter like Olimex' ARM-JTAG-20-10, because the JTAG header is smaller than usual, it has a 1.27 mm pitch (Samtec 0.05" micro header) instead of the normal 2.54 mm (0.1"). Power your target (second) Arduino Due by connecting its "programming" USB socket to your PC.

With the command above you can also verify that at least the ground signals (GND1 and GND2) are low and the VCC signal is high after connecting the JTAG cable.

In contrast to the Bus Pirate, the Arduino Due has no analog switch chip on board, so the VCC, GND1 and GND2 signals are _not_ used as reference voltages when driving the JTAG interface. You need to make sure beforehand that the voltage levels are the same on both the Arduino Due and the JTAG target board.

8) Connect to the DebugDue with OpenOCD.

First of all, you may need to build OpenOCD with support for the Bus Pirate.

If you are building OpenOCD version 0.10.0, you will need to patch it manually. This is no longer necessary in version 0.11.0. Edit the pre-generated 'configure' script (as you will probably not be regenerating it with Autoconf), and remove the --with-ext and --without-ext arguments from the line that contains the following text:

--disable-install-jim --with-ext="eventloop array clock regexp stdlib tclcompat" --without-ext="default"

Without those arguments, Jim Tcl's default modules will be installed. This change was made for OpenOCD version 0.10.0 and has been reverted afterwards, but a new stable version has not been released yet as of september 2018.

Build OpenOCD. For example:

./configure --enable-buspirate --prefix="$HOME/SomeDir/openocd-0.10.0-bin"  CFLAGS="-Wno-error"  CXXFLAGS="-Wno-error"
make --output-sync=recurse  -j $(( $(getconf _NPROCESSORS_ONLN)   1 ))
make install-strip

You will need to disconnect the command console from the DebugDue's USB virtual serial port beforehand, if you have it open.

I have tested these instructions with OpenOCD version 0.8.0. Configure OpenOCD as if your JTAG adapter were a Bus Pirate, but bear in mind that:

  • Option 'buspirate_speed' has no effect, the USB transfer speed will always be the maximum available.

  • Option 'buspirate_vreg' has no effect, as the Arduino Due has no voltage regulator to supply power to other devices.

  • Option 'buspirate_pullup' only affects JTAG signals TDI, TDO, TCK and TMS, like in the Bus Pirate.

    The reset signals TRST and SRST are not affected (have no pull-up option).

    Note that the built-in pull-ups on the Atmel ATSAM3X8 are too weak (between 50 and 150 KOhm) to be of any use. On my dodgy test set-up, and once looked at TCK with the oscilloscope, and the rising edges were very long curves, the speed was not enough to run reliably at as low as 24 KHz.

  • If you set option 'buspirate_mode' to 'open-drain', DebugDue will use the Atmel ATSAM3X8's "Multi Drive Control" (Open Drain) mode.

    Therefore, you need to enable the internal pull-ups (which are probably too weak, see above) or have external ones in order to guarantee a high level on the lines when necessary.

  • Command 'buspirate_led' has no effect.

    The only LED on the Arduino Due is used as a heartbeat indicator at the moment.

From this point on, follow the standard OpenOCD instructions in order to connect to your target board.

If you are connecting to a second Arduino Due target, check out the files under OpenOCD/SecondArduinoDueAsTarget/ . You should be able to connect to it with a command like this:

cd <DebugDue root dir where this README file is>
openocd  --command "set DEBUGDUE_SERIAL_PORT /dev/serial/by-id/usb-Arduino_Due_JTAG_Adapter_DebugDue1-if00"  -f "OpenOCD/SecondArduinoDueAsTarget/DebugDueInterfaceConfig.cfg"  -f "target/at91sam3ax_8x.cfg"  -f "OpenOCD/SecondArduinoDueAsTarget/OpenOCDJtagConfig.cfg"

If there are no errors, you should see at the bottom the following output:

Info : Buspirate switched to FAST mode
Info : Buspirate Interface ready!
Info : This adapter doesn't support configurable speed
Info : JTAG tap: sam3.cpu tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4)
Info : sam3.cpu: hardware has 6 breakpoints, 4 watchpoints

Press Ctrl C to quit OpenOCD at this point.

You may find it difficult to control your second Arduino Due over JTAG if it is not running the right kind of firmware. The reason is that, if the CPU is sleeping or not running at a high-enough frequency, the JTAG connection will not work well. Note also that the DebugDue firmware does not support adaptive JTAG clocking, which could help in this situation. If you hold the ERASE button for at least 220 ms on the target Arduino Due, you will experience this issue. OpenOCD will detect the JTAG chain correctly, but you will get errors like this if you try to connect over OpenOCD/JTAG with GDB:

Error: JTAG-DP OVERRUN - check clock, memaccess, or reduce jtag speed

After this error, any OpenOCD command like step is likely to fail with an error such as "target not halted".

The easiest way to overcome this problem is to flash the "EmptyFirmware" project (file emptydue.bin) with Bossac, see above for details. The EmptyFirmware, like the full DebugDue project, ramps up the CPU speed at the very beginning and then makes a short "busy" pause (without sleeping). This way, OpenOCD stands a good chance of connecting over JTAG right after resetting the board with the hardware SRST signal and stopping the CPU before the firmware runs too far away. The very early pause location enables you to debug comfortably most of the firmware initialisation sequence. You should use the same technique in your own firmware, or it will be hard (or maybe downright impossible) to debug it over JTAG.

Note that, if you use a software reset method (OpenOCD's "reset_config none"), you will not encounter such problems, but then you will need to manually reset the microcontroller peripherals. The code in this project implements such a method.

9) Prepare a comfortable development environment.

First of all, make sure that script Tools/run-in-new-console.sh works fine on your system. You may have to install Konsole (KDE's terminal emulator) or amend the script to use some other terminal program. You can test it as follows:

Tools/run-in-new-console.sh "echo Press ENTER... && read"

If everything works correctly, you should see a new console window asking you to press the ENTER key.

Then edit script DebugDueBuilder.sh, which lives next to this README file is. If you are programming a second Arduino Due over JTAG, you will have to make minimal modifications to routine user_config(). For other targets, you will have to make more amendments.

Try the modified script out with the following command:

./DebugDueBuilder.sh --project=DebugDue --clean --build --program-over-jtag --debug

That should build the firmware from scratch, send it to the target device and start debugging it.

In the GDB window, press Ctrl C to halt the firmware. You can then use commands myreset or myhaltafterreset to restart the firmware.

The idea is that, no matter what development environment you are using (Emacs, Vim, Eclipse, ...), you should configure the usual compile/run/etc. keys to run the script with different arguments. For example:

F7 (compile):          ./DebugDueBuilder.sh --project=DebugDue --build
F5 (run)    :          ./DebugDueBuilder.sh --project=DebugDue --build --program-over-jtag --debug --debugger-type=ddd --cache-programmed-file
Ctrl Alt F7 (rebuild): ./DebugDueBuilder.sh --project=DebugDue --clean --build
Ctrl R (run to line):  ./DebugDueBuilder.sh --project=DebugDue --build --program-over-jtag --debug --add-breakpoint="Main.cpp:123" --cache-programmed-file

This way, you will not have to type any console commands in order to run or debug your firmware. You will not get the full comfort you are used to when writing standard PC applications, but it will come close enough.

Still To Do

  • Stack Backtrace on Crash

    If the firmware crashes, I would like to get a call stack (a backtrace) on the debug console.

    On the PowerPC architecture, you can get a backtrace programmatically if I turn off GCC's stack frame optimisation. However, it looks like that is not easy to do on the ARM architecture. Or am I mistaken here? Is there perhaps a way to tell GCC to generate some sort of stack frame for this purpose?

    Otherwise, is it possible to dump the tip of the stack in such a way that a PC tool can then later on reconstruct the call stack, if the .elf file with debug information is available?

Changelog

This changelog only covers the released binary files for the DebugDue firmware. Sources are updated more frequently.

  • Version 1.0.0, released on 11 may 2013, git tag "v1.0.0". First public version.

  • Version 1.1.2, released on 29 june 2014, git tag "v1.1.2". General improvement.

Feedback

Please send feedback to rdiezmail-arduino at yahoo.de

The project's official web site is https://github.com/rdiez/DebugDue

License

Copyright (C) R. Diez 2012-2022, rdiezmail-arduino at yahoo.de

The source code is released under the AGPL 3 license.

Please note that some of the files distributed with this project may have other authors and licenses.

This document is released under the Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) license.

About

Emulates a Bus Pirate OpenOCD JTAG adapter with an Arduino Due

Resources

Stars

Watchers

Forks

Packages

No packages published