[TUT][SOFT][GCC] AVR debugging using gdb and AVR Dragon under Linux (Ubuntu)

1 post / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

I have been working on putting together a cost effective tool chain that would allow me to develop and debug native C code on Arduino UNO using the AVR Dragon debugger I had sitting around for a few years now. As a Windows user for as long as I can remember, I also decided to take up the challenge of doing this under Linux (Ubuntu to be precise). As you can imagine, it has been a painstaking journey that required a lot of patience and persistence. Now that I have finally got a working code development and debugging under Linux, I wanted to put together this tutorial for the benefits of others as well.

 

AVR Freaks has been very generous to me over the years and this is my humble effort to be able to give a little back. I hope many people will find this useful and beneficial in their work one way or another.

 

1 - HARDWARE TOOLS

 

Following is what I worked with while preparing this tutorial. You can see the full set up in the photo provided below.

 

- Ubuntu 14.04 machine

- AVR Dragon ICE device – parallel programming mod has been made to this device to rescue atmega328p devices that are bricked while working. (Arduino UNO board has atmega328p MCU installed)

- Arduino UNO board (connected to a power supply)

- ISP cable – built this cable to interface Dragon with the UNO board

 

AVR dragon and Arduino UNO interface using an ISP cable

 

I must note that a small hardware mod on Arduino UNO is required before getting all of this to work. Because this tutorial uses debugWire, and the debugWire is connected to the reset line, the reset button on Arduino UNO needs to be detached from the reset pin of the device. All that needs to be done is to remove the 100 nF capacitor marked as C5 in the following schematics

https://www.arduino.cc/en/uploads/Main/arduino-uno-schematic.pdf

 

2 - SOFTWARE TOOLS

 

As the first step (after the Ubuntu 14.04 is installed and is running reliably of course), the AVR tools need to be installed. These tools are as follows:

avr-gcc: The actual C/C++ compiler

avr-binutils: A collection of tools including the assembler, linker and some other tools to manipulate the generated binary files.

avr-libc: A subset of the standard C Library with some additional AVR specific functions. The libc-avr package also includes the AVR specific header files.

avr-gdb: This is the debugger.

avrdude: A Program to download/upload/manipulate the ROM and EEPROM of an AVR MCU.

 

I used the following command to achieve this on the Ubuntu system.

 

sudo apt-get install gcc-avr binutils-avr gdb-avr avr-libc avrdude

 

If you need the most up-to-date tools, you may be better off building the tool chain yourself. following link tells you how you can achieve this. However, my notes here are based on using the existing tool chain as mentioned on the apt-get line just above.

 

Install Eclipse IDE for C/C++ Developers from the Eclipse Downloads page. At the time of writing, Mars.2 Release (4.5.2) was used. Configuration of Eclipse to work on AVR projects is covered in the next section.

 

A few more useful links:

Link to AVR Dragon debugger

Link to avrdude

Link to avr-gcc and gcc

Link to avarice

Link to avr-gdb and gdb

Link to avr-libc and another one from Atmel

Link to Arduino UNO interactive hardware visual reference

Link to Arduino UNO (rev 3) schematics

 

3 - CONFIGURING ECLIPSE

 

Start Eclipse and specify a workspace. It is recommended that you specify a new and dedicated workspace only for the AVR projects. Apparently, this protects your workspaces where you develop on other platforms except the AVR.

On the main menu of Eclipse (this is the very top bar outside the main window of Eclipse usually!) Click on Help > Install New Software. In the field that is called “Work with:” type in http://avr-eclipse.sourceforge.net/updatesite and press Enter. After a few seconds “AVR Eclipse Plugin” should appear. Tick the button beside it and hit Next at the bottom. Install the plugin by following the instructions. Once the installation is completed you will need to restart Eclipse as you will be prompted. You can access more information on this process using this link.

 

After Eclipse restarts, create a new C project. On the main menu, click File > New > C Project. Go to AVR Cross Target Application and click Empty Project. Note in that window under Toolchains: “AVR-GCC Toolchain” should already be pre-selected. Provide a name for your project. For this example, I will use the name project_test for argument’s sake. This project will be created under the default location as it is marked on the same window. After naming your project, click on Next at the bottom. On the next window, you will select the Debug or the Release configurations. For now make sure both are selected. While working on the project, depending on what you are trying to do, you can activate/deactivate each of them as needed. Click Finish. The project will be created with no source files in it.

 

At this point you can either create new *.h and *.c files or import existing ones that are located in another directory to your projects directory. I will explain importing an existing file called toggleLed.c in this example. Right click on the project name and select Import. Select General > File System and click Next. Then browse to the *.c and *.h files you would like to import to your project. Make sure the files to be imported are located in a different directory to your project. Otherwise you will get an error message. When you import the files, a copy of those files will be created under the new project’s directory and the original files will remain untouched, which is a good thing. After selecting the files to import, click Finish and the window will be closed.

 

On the main menu, select Project > Properties. You can make the same selection by right clicking the name of the project on Eclipse IDE. Then click on C/C++Build and select Settings. There are a number of options we need to specify here.

 

Under “Additional Tools in Toolchain” make sure the following options are ticked and then click Apply at the bottom:

AVRDude

Print Size

Generate HEX file for Flash memory

 

Under “AVR Assembler” option click Debugging. Choose standard debugging info under “Generate Debugging Info”. Then choose stabs under “Debug Info Format”. Similarly repeat this for the same Debugging options under “AVR Compiler”

Under “AVR Compiler” select Optimizations and set the optimization level to “No Optimizations”. Make sure the following 4 options are all selected on that same page and click Apply for changes to take effect.

Pack structs

Short enums

Each function in its own section

Each data item in its own section

 

Once the C/C++ Build configuration is complete, select AVRDude under AVR option. Then click on the Programmer tab. The field under “Programmer configuration” will be empty. Click New to create a new one. Type in a configuration name for the programmer. I will call it My Dragon config. You can also type in a brief explanatory description under the name field. We will be using AVR Dragon in ISP mode so select that option from the menu. It may also be worth defining another configuration for AVR Dragon in PP mode at some point in case you need to rescue some bricked UNO MCUs. On the same page, type in usb under “Override default port” field. It is recommended to use baudrate of 19200 so change that under “Override default baudrate” option. Another important point not to miss on this screen is “Delay between avrdude invocations” which I have set to 800 milliseconds to be safe. (Note: This long delay causes the device uploads to proceed a bit slower but it ensures that the AVR Dragon and the Arduino UNO boards sync reliably. When this delay is around 150 milliseconds or so, I have observed that Dragon and UNO fail to talk to each other as Arduino UNO seems to respond quite slowly) Press OK when all the changes are made.

 

Under Flash/EEPROM tab, you may wish to select “do not upload flash memory image” if you prefer not to upload the flash image to UNO automatically after each compile and link process. Similarly, it makes sense to select the “do not upload eeprom image” on the same page. Click on Apply once done.

 

On the Fuses tab, select “do not set fuse bytes” option. You can set the fuse bytes once as and when needed but you do not need to set them every time you perform a code upload to UNO. It is worth mentioning the debugWire fuse setting at this point (since we will use the debugWire on Arduino UNO to program and debug our device). Within the Fuses tab, click on the “direct hex values” radio button. To the right of that radio button (after the low, high and ext. fields), there is an icon with a black chip icon corresponding to “Load from MCU”. Click on that to read the fuse values already programmed on the MCU. Once the fuse values are successfully read, the low, high and ext. fields will display the corresponding values. Once those values are read, click on the first icon that reads “Start editor”, which will bring up a user-friendly fuse editor window. In that window, simply make sure the “DWEN – Debug Wire enable” is selected as “Yes”. Then click OK and go back to the previous screen and close that screen as well. In order to program the fuse bytes, you will simply need to Select “AVR” tab on the main menu (while the Eclipse IDE is active of course) and click on “Upload Project to Target Device”. After programming the Fuse Bytes, you can go back to the Fuse Byte screen and disable programming the fuse bytes by selecting “do not set fuse bytes” option.

 

Select AVRDude under AVR option again and continue. On the Lockbits tab, select “do not set lockbits”.

 

On the Advanced tab, tick the boxes that you need. In order to avoid signature errors during upload, it will make sense to enable the Device Signature Check option (-F) here.

 

On the Other tab, make sure the “Enable erase cycle counter” option is left unselected unless you have a good reason to do otherwise.

If you would like to add any other options flag for the avrdude command, use the “Other options” field to do that. In my example, this field is left blank.

Once done, click Apply. At this point it will make sense to perform a quick sanity check to make sure Eclipse is able to talk to Arduino UNO via AVR Dragon. To do this, select “Target Hardware” option under AVR. Click “Load from MCU” and wait for a few seconds. If all goes well, the MCU Type should read ATmega328. If there is a connection problem you may see an error message such as:

 

Programmer “dragon_isp” could not connect to the target hardware.

Please check that the target hardware is connected correctly.

Reason:

avrdude: failed to sync with the AVR Dragon in ISP mode

 

If all has gone well until this point, you should be able to compile the toggleLed.c source file which is part of the project_test. Just to make sure we are on the same page, the source code in my toggleLed.c source file is as follows:

 


#include <avr/io.h>
#include <util/delay.h>

#define BLINK_DELAY_MS 250 /* in msec */

int main (void)
{
    int counter = 0;

    /* set pin 5 of PORTB for output*/
    DDRB |= _BV(DDB5) | _BV(DDB1); /* Bit Value of DDB5 (UNO pin 13) and DDB1 (UNO pin 9) is used here. */
    DDRD |= _BV(DDD6); //OC0A (UNO pin 6) is configured as output

    PORTB &= ( ~_BV(PORTB5) & ~_BV(PORTB1) ); // output is at zero at start up

    while(1)
    {
        PORTB ^= _BV(PORTB5);
        _delay_ms(BLINK_DELAY_MS);
        ++counter;
    }//end while

}//end main()

All this code does is to toggle the LED connected to pin 13 of Arduino UNO on and off. It’s that simple! There is some redundant code lines in this source file but don’t worry about them for now. If you compile this code and run it, the LED should toggle at a rate of 2 blinks per second basically. The local variable called counter is also incremented at each LED state change for no apparent reason. However, the reason will become obvious later on during debugging.

 

When you build the code, you should see two files under the Release directory under project_test folder called project_test.elf and project_test.hex. These confirm that the build process has been successful. Note that until now we have not flashed code to the Arduino UNO board.

 

We can now move to the debugging phase in the following section.

 

4 - CONFIGURING avarice and avr-gdb

 

Normally, one would like to be able to debug an application on Arduino UNO using the Eclipse IDE. However, despite my best efforts and countless attempts, I have not managed to achieve this yet. Maybe in the future if and when I do, I will be able to share how that can be done. For now, I use the Eclipse environment to write and build code. Then for debugging purposes, I use the terminal to run avrdude, avarice and avr-gdb. Although having to use the terminal to debug an embedded application may not sound appealing, as someone who is used to working with professional debugging tools that work in Windows I did not find it too hard. As a matter of fact, given the power of gdb commands, I think one would not spend too much time debugging using an ugly black terminal screen so this option is acceptable.

 

In the rest of this piece, I will be describing this debugging operation.

 

After building your code, there are 3 basic steps you need to follow to start debugging your application on the device.

 

Step 1 – Flash your debuggable binary to the device first.

 

Since you already have your debuggable binary (i.e. project_test.elf file) ready, open a terminal and invoke the following command to upload the elf file to the device.

 

sudo avrdude –F –V dragon_isp –p ATMEGA328P –P usb 115200 –U flash:w:project_test.elf

 

Above command will kickstart the program upload to Arduino UNO. Depending on the upload speed, this can take about 10-15 seconds. After the code is successfully uploaded, you may need to power cycle the Arduino UNO board before executing Step 2. Otherwise, you may receive the following error:

 

AVaRICE version 2.13, Jun 22 2016 11:17:20

 

JTAG config starting.

Found a device: AVRDRAGON

Serial number:  00:a2:00:01:46:4c

set paramater command failed: DEBUGWIRE SYNC FAILED

Failed to activate debugWIRE debugging protocol

USB bulk write error: error submitting URB: No such device

USB daemon died

 

If the device is successfully flashed, you should see an output similar to the following:

 

avrdude: AVR device initialized and ready to accept instructions

 

Reading | ################################################## | 100% 0.16s

avrdude: Device signature = 0x1e9514

avrdude: Expected signature for ATmega328P is 1E 95 0F

avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed

         To disable this feature, specify the -D option.

avrdude: erasing chip

avrdude: reading input file "project_test.elf"

avrdude: input file project_test.elf auto detected as ELF

avrdude: writing flash (746 bytes):

Writing | ################################################## | 100% 4.12s

avrdude: 746 bytes of flash written

avrdude: safemode: Fuses OK (H:06, E:95, L:BF)

avrdude done.  Thank you.

 

Step 2 – Start avarice to create the bridge between the device and gdb

 

After a successful code upload to UNO as shown in Step 1, invoke the following command at the same terminal screen to run avarice. Avarice connects to the UNO board over the debugWire on one side and talks to the avr-gdb on the other via a TCP connection. In other words, it acts as a translator between the debugger and the target hardware.

 

avarice –part atmega328p –debugWire –dragon :4242

 

If all goes well, you should see a response similar to the following on the terminal:

 

AVaRICE version 2.13, Jun 22 2016 11:17:20

 

JTAG config starting.

Found a device: AVRDRAGON

Serial number:  00:a2:00:01:46:4c

Reported debugWire device ID: 0x950F

Configured for device ID: 0x950F atmega328p -- Matched with atmega328p

JTAG config complete.

Preparing the target device for On Chip Debugging.

Waiting for connection on port 4242.

 

Note that after starting successfully, the avarice expects a connection from the avr-gdb to port 4242. This can be seen on the last line of the above terminal output.

 

Step 3 – Start the gdb server and connect to avarice

 

On a new terminal screen type avr-gdb project_test.elf

Note that you need to provide the same elf file to avr-gdb as well to make sure the correct list of symbols is known to the tool.

After invoking the avr-gdb command as above, you will see the (gdb) prompt as below where you can type in your commands.

 

GNU gdb (GDB) 7.6.50.20131218-cvs
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <
http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=avr".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<
http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<
http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from project_test.elf...done.
(gdb)

 

Type in the following at the prompt to connect to the avarice that should be running on the other terminal screen.

 

target remote localhost:4242

 

You should see the following upon establishing a successful connection:

 

Remote debugging using localhost:4242

0xfffffffe in ?? ()

 

The last line may also indicate the reset vector located at address 0x00000000 and another function name. Do not worry if you see a difference.

Note that once the connection is successfully established between the avarice and the avr-gdb, the terminal screen mentioned at Step 2 should be updated with the following line (the port indicated may be completely different to what is mentioned here. It will be different to 4242 as well so don’t be alarmed).

 

Connection opened by host 127.0.0.1, port 38164.

 

To make sure you are in the correct debugging session, type in list at the (gdb) prompt. You should see a few source code lines (with the line numbers) in toggleLed.c where main() function is located. This confirms that you have successfully started your debug session. However, you may also see strange looking errors as below upon typing command “list” as follows:

 

(gdb) list

35    ../../../libm/fplib/fp_round.S: No such file or directory.

 

Knowing that the fp_round.S file is not something we have created, this indicates there is something going wrong here. Let’s go back to our source code and try to insert a random breakpoint somewhere. I chose line 22 in toggleLed.c file where the following line of code is supposed to exist:

 

++counter;

 

At the gdb prompt, type in the following to insert a breakpoint:

 

break toggleLed.c:22

 

Following is the response I receive, which clearly indicates our debugger is not in sync with the source file we think we are working on!

 

No line 22 in file “toggleLed.c”.

 

Another way to confirm that there is an issue is to access a local variable such as “counter” in our example. Below you will see the invoked command and the self-explanatory response by the gdb where the local variable cannot be found.

 

print counter

No symbol “counter” in current context.

 

When I first encountered this issue, I was quite disappointed and had no idea how to sort it out. After digging a bit deep and spending a few hours searching for the answer, I found the solution.

Basically before we build the code, we need to tell the compiler to generate more debug information into the elf file. Therefore, we need to go back to the avr-gcc step described earlier within the Eclipse tool and add the option –ggdb which will produce debugging information specifically intended for gdb. There are other options that may be worth exploring as well:

 

-g produces debugging information in the operating system’s native format (stabs, COFF, XCOFF, or DWARF 2).

-ggdb3 produces extra debugging information, for example: including macro definitions (i.e., gdb for level 3)

-ggdb2 (i.e., gdb for level 2).

 

I have not explored any of them except the –ggdb and that’s why the rest of this section will be based on this tried and tested option.

 

In Eclipse project properties for project-test, go to C/C++ Build option and click on Settings. Under AVR Compiler go to Miscellaneous and under “Other flags” type –ggdb. Then click OK at the bottom. After making this change, rebuild the project to generate the new elf file.

 

Once you have the new elf file rewind and go through Steps 1, 2 and 3 again. Then try the following at the gdb prompt.

 

When we invoke the list command again, you should start seeing part of the source file that you are debugging with the line numbers. This is a clear indication that we are on the right track!

 

(gdb) list

1   

2   

3    #include <avr/io.h>

4    #include <util/delay.h>

5   

6    #define BLINK_DELAY_MS 250 /* in msec */

7   

8    int main (void)

9    {

10        int counter = 0;

(gdb)

 

When you press enter, the listing of the source code shall continue as below.

 

11   

12        /* set pin 5 of PORTB for output*/

13        DDRB |= _BV(DDB5) | _BV(DDB1); /* Bit Value of DDB5 (UNO pin 13) and DDB1 (UNO pin 9) is used here. */

14        DDRD |= _BV(DDD6); //OC0A (UNO pin 6) is configured as output

15   

16        PORTB &= ( ~_BV(PORTB5) & ~_BV(PORTB1) ); // output is at zero at start up

17   

18        while(1)

19        {

20            PORTB ^= _BV(PORTB5);

(gdb)

 

When you try to see the local variable “counter” now, following is what you should get:

 

(gdb) print counter

No symbol "counter" in current context.

(gdb)

 

Oops! That does not seem right. Did we miss something? Not really. This is expected since we did not start running our code yet! In other words, we did not make an entry into main() function yet while debugging and therefore the current scope does not know about the local variables in main(). By the way, even if we had run our program before inserting the –ggdb option as described above, we would not have been able to access the variable then due to the missing symbol information. (trust me, I tried that too during my explorations and path finding :) )

Before running the code freely, let’s insert a breakpoint to line 22 like before.

 

(gdb) break toggleLed.c:22

Breakpoint 1 at 0x1c6: file ../toggleLed.c, line 22.

(gdb)

 

OK. It all looks good and the response clearly indicates a breakpoint has been added where we want it. Now let’s start running the code as follows.

 

(gdb) continue

Continuing.

 

Breakpoint 1, main () at ../toggleLed.c:22

22            ++counter;

(gdb)

 

You can see that the code has stopped on line 22 where we have a breakpoint. Perfect! This is the line that increments counter by one. Now let’s see the value of counter at that point.

 

(gdb) print counter

$1 = 0

(gdb)

 

The debugger indicates that counter= 0, which is expected. Let’s run the debugger until the next breakpoint (which is where we are now). Since we are in an infinite while loop, we will stop here again.

 

(gdb) continue

Continuing.

 

Breakpoint 1, main () at ../toggleLed.c:22

22            ++counter;

(gdb)

 

Code stopped again. Let’s have a look at the same variable again. This time we would expect counter to be incremented by one.

 

(gdb) print counter

$2 = 1

(gdb)

 

Success! It all works as expected. No nasty surprises.

 

Well, if you have been patient enough to come this far and obtained the results I have reported, hopefully you will have the confidence to dig deeper into debugging more complex applications besides the simple LED project provided as an example here.

 

There is quite a few resources to learn about the GDB debugger on the net, however, I suggest you take a look at this one which is simple and comprehensive enough to help you in many projects.

 

THANK YOU FOR READING & GOOD LUCK DEBUGGING! :) :)

 

 

 

Last Edited: Wed. Aug 9, 2017 - 03:48 PM