07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
Debugging an Arduino project with GDB on Classic
ATtiny and Small ATmega MCUs
Bernhard Nebel
2 Feb 2022 GPL3
A tutorial for people who finally want to debug their Arduino projects that run on AVR MCUs using the GNU project debugger GDB
The current Arduino IDE does unfortunately not support debugging. Even the new beta version supports debugging only for ARM
MCUs. With a few easy steps, it is nevertheless possible to use avr-gdb in order to debug your Arduino project on many AVR chips.
In this article, I will focus on MCUs that support debugWIRE, i.e., the classic ATtinys and the ATmegaX8 family.
Download source code - 6.2 MB
Introduction
The Arduino IDE is very simple and makes it easy to get started. After a while, however, one notes that a lot of important features
are missing. In particular, the current IDE does not support any kind of debugging. So what can you do when you want to debug
your Arduino project on small ATmegas (such as the popular ATmega328) or classic ATtinys? The usual way is to insert print
statements and see whether the program does the things it is supposed to do. However, there should be more sophisticated ways.
The chips mentioned support the debugWIRE protocol, which one can use to access the on-chip debugging hardware on these
MCUs.
There is support from Microchip in the form of hardware debuggers and proprietary IDEs (Microchip Studio and MPLAB) on a
professional level. The hardware debuggers are not exactly for free, while the IDEs are. However, you would be forced to a quite
different software eco system and some people are not thrilled by these IDEs. So what are the alternatives?
The most popular open-source debugger is GDB, The GNU Project Debugger. In fact, this is the debugger you find in many IDEs for
embedded development, e.g., PlatformIO and Arduino IDE 2.0. So the question is in how far it is possible to interface the mentioned
AVR MCUs to GDB.
In this article, I will address the above question. In addition, I will show you how to make use of it by setting up a debugging
environment, connecting your target system to a host, and finally debugging your Arduino project on the target system.
Background
A source-code debugger such as GDB enables you to look at the inner workings of a program while it executes. You can start and
stop execution, you can place breakpoints at which execution stops, you can make single steps, you can inspect variables, and you
may also change the value of a variable. This is all usually done while the program that is to be debugged runs on the same
machine as the debugger.
Debugging Embedded Systems
When one wants to debug embedded systems, the above scenario does not work any longer. Now we need to run the debugger on
the development machine, the host, while the program executes on the target. Such a scenario is supported by GDB in the form of
[Link] 1/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
remote debugging. The debugger communicates via the GDB remote serial protocol (RSP) with the target in order to control
execution and for gathering information, which means that we need some software on the target that receives and interprets this
protocol.
If the target runs its own operating system, then a separate process could be used for it. For many different architectures, a program
called gdbserver is available, which will do that. On a bare metal system without an operating system, this is not a possibility.
For bare metal systems, you have two options. First, you can link a library, a so-called gdb-stub, into the program that is to be
debugged. This library is then responsible for what gdbserver would do, i.e., controlling execution and communicating with the host
using RSP. In the context of Arduino software and more general AVR MCUs, Jan Dolinay has designed such a gdb-stub and
reported about it on CodeProject. This stub works for ATmega328(P), ATmega1284(P), ATMega1280, and ATMega2560. It is a
great step forward concerning debugging Arduino projects, but it has, of course, inherent limitation. One of them is that the serial
line is used by the remote serial protocol for communicating with the host and therefore cannot be used by the user program.
Another limitation is that you cannot use it to debug ATtinys.
The second option for bare metal systems is to use a hardware debugger, which plays the role of a gdbserver. Almost all modern
MCUs contain on-chip debugging (OCD) hardware. This hardware gives access to the MCU internals such as registers and
memory, and it can control execution. The access is provided by another communication protocol, often JTAG, an industry standard.
The hardware debugger than translates between JTAG on the target side and GDB RSP on the host side. Often, another program
plays the role of a mediator, e.g., openOCD.
The debugWIRE Protocol
The MCUs, we are concerned about here, namely, the classic ATtinys and the ATmegaX8 family, do not provide a JTAG interface.
They have instead a proprietary interface to their OCD features called debugWIRE. The basic idea of debugWIRE is that one uses
the RESET line as a communication line between the target and the hardware debugger. The idea of using only a single line that is
not used otherwise is very cool because it does not waste any of the other pins for debugging purposes (as does e.g. the JTAG
interface). However, using the RESET line as a communication channel means, of course, that one cannot use the RESET line to
reset the MCU anymore. Furthermore, one cannot any longer use ISP programming to upload new firmware to the MCU or change
the fuses of the MCU. Firmware uploads are possible over the debugWIRE interface, they are a bit slower, however.
When you intend to use the debugWIRE interface, you have to have a basic understanding of what state the MCU could be in with
respect to the debugWIRE protocol. Depending on the state, the RESET line and ISP programming may work or not. There are
basically three states the MCU could be in:
1. The normal state in which the DWEN (debugWIRE enable) fuse is disabled. In this state, you can use ISP programming to
change fuses and to upload programs. By enabling the DWEN fuse, one reaches the transitional state.
2. The transitional state is the state in which the DWEN fuse is enabled. In this state, you could use ISP programming to
disable the DWEN fuse again, in order to reach the normal state. By power-cycling (switching the target system off and on
again), one reaches the debugWIRE state.
3. The debugWIRE state is the state in which you can use the debugger to control the target system. If you want to return to
the normal state, a particular debugWIRE command leads to a transition to the transitional state, from which one can reach
the normal state using ordinary ISP programming.
Existing debugWIRE Interfaces
There exist a few open-source systems that implement parts of the debugWIRE protocol based on RikusW work on reverse
engineering the protocol, but most of them have some limitations. DebugWireDebuggerProgrammer is an Arduino-based solution
that provides an ISP programmer and a debugWIRE debugger. Unfortunately, it does not provide a GDB RSP interface. dwire-
debug is a C-program that can be used under Linux, Windows and macOS. It accesses targets over the serial line, whereby the TX
and RX lines are joined using a diode. The program provides an RSP interface, but allows only one breakpoint. Furthermore, under
macOS, I was not able to communicate reliably over the joined lines. And then, there is a Pascal implementation similar to dwire-
debug called debugwire-gdb-bridge, which I did not try out.
Recently, I have developed an Arduino sketch called dw-link that turns an Arduino Uno, Nano, or Pro Mini into a hardware debugger.
It does not suffer from OS dependencies that could make serial communication difficult. Instead, it just relies on using RSP over the
serial line and it is easy to install. The setup will then look as in the following picture, where the role of the hardware debugger is
played by an Arduino Uno with dw-link running on it.
[Link] 2/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
The steps necessary to debug a program on the target are then:
1. installing dw-link,
2. setting up a debugging environment, e.g., PlatformIO or Arduino IDE & avr-gdb,
3. setting up the hardware, and
4. start debugging.
Installing dw-link
Use the source-code archive of the article or download the code from the GitHub repository. If you want to use the Arduino IDE to
upload the firmware to your hardware debugger, extract the archive at a place, where the Arduino IDE expects sketches. You can
then use the IDE to upload the sketch dw-link to the board you want to use as a hardware debugger, i.e., an Uno, Nano, or Pro
Mini board.
If you use PlatformIO, you can extract it, e.g., in your PlatformIO project folder, and then open the project. It already contains a
[Link] file that supports you in compiling and uploading the firmware to an Arduino Uno, Nano, or Pro Mini.
There is also extensive documentation in the repository that supports you in setting up everything.
The example folder contains some example sketches and configuration files. As a running example, we will use the sketch
[Link]:
C++
#include <Arduino.h>
#ifndef LED_BUILTIN
#define LED 4
#else
#define LED LED_BUILTIN
#endif
byte thisByte = 0;
void setup() {
pinMode(LED, OUTPUT);
}
void loop() {
int i=random(100);
digitalWrite(LED, HIGH);
delay(1000);
digitalWrite(LED, LOW);
thisByte++;
thisByte = thisByte + i;
delay(100+thisByte);
}
[Link] 3/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
Put this sketch into a folder where your Arduino IDE can find and compile it. The sketch is meant to run on an ATtiny85, but can, of
course be compiled for any MCU that is covered by the Arduino IDE.
Setting Up the Debugging Environment
There are (at least) two possible ways to set up a debugging environment. The first one, installing PlatformIO, is straightforward and
requires in the end a bit of configuration. The main advantage is that the entire toolchain including the debugger will be installed in
the background. Further, it makes debugging very easy because one gets a very accessible GUI that gives access to almost all
important details. In contrast, using the Arduino IDE and avr-gdb takes a bit of work to get everything right. However, you can stay in
the Arduino universe and only have to learn something about using avr-gdb. This approach is, however, very console oriented.
Installing and configuring PlatformIO
PlatformIO is an IDE aimed at embedded systems and is based on Visual Studio Code. It supports many MCUs, in particular almost
all AVR MCUs. And it is possible to import Arduino projects, which are then turned into PlatformIO projects. Projects are highly
configurable using the configuration file [Link], that is, a lot of parameters can be set for different purposes. However, that
makes things in the beginning a bit more challenging.
Installing PlatformIO is straightforward. Download and install Visual Studio Code. Then start it and click on the extension icon (four
squares) on the left, search for the PlatformIO extension and install it, as is described here. Check out the quick start guide. Now we
are all set.
Next time, when you start VSCode again, you can import the [Link] sketch. Click on the Home symbol in the lower navigation
bar, which brings up the home screen of PlatformIO. Choose then to import an Arduino project. You need to choose a board and
then the directory from which you want to import the Arduino project.
After the import, you will have a folder with entries such as include, lib, src, etc. Your Arduino sketch can be found in the sub-folder
src. In addition, there is the important configuration file [Link]. It is set up for using it with the board you have specified. If you
want to use it with the hardware debugger, you need to add a few things. The best way is probably to replace the existing file with
the [Link] file that you find in the folder example/pio-files. You need also to specify the serial port you are using to access the
hardware debugger and you need to specify the type of your target board in the [Link] file. The folder example/pio-files
contains also a file extra_script.py, which you need to copy to the project folder of the [Link] sketch as well.
Installing avr-gdb and Changing the Arduino IDE Configuration Files
Assuming that you are working with the Arduino IDE and/or Arduino CLI, the simplest way of starting to debug your code is to install
and use the GNU debugger. You only have to download the debugger and make a few changes to some of the configuration files.
Please take notes on what you change because these changes will vanish when you upgrade to a new version of the Arduino
package.
If you want to debug anything other than the standard Arduino boards, then you also have to download the respective core. For
instance, if you want to debug classic ATtinys, you need to install ATTinyCore. If you want to debug MCUs from the ATmegaX8
family, you need to install MiniCore.
The first important change for making debugging possible is that you enable the IDE/CLI to export ELF files from your projects.
These are machine code files that contain machine-readable symbols and line number information. You can enforce the export of
ELF files by adding a file [Link] to one of the platform specification folders, which needs to contains the
following lines:
[Link]=cp
"{[Link]}/{build.project_name}.elf" "{sketch_path}"
[Link]=cp
"{[Link]}/{build.project_name}.elf" "{sketch_path}"
[Link]=cmd
/C copy "{[Link]}\{build.project_name}.elf" "{sketch_path}"
These three lines make sure that you receive an ELF file in your sketch directory when you select Export compiled
Binary under the menu Sketch. When you are working with the CLI, then the -e option will do it.
[Link] 4/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
The file with the three lines (which you find in the example/configuration-files repository folder) needs to be copied to the following
Arduino folder (NAME being the name of the core, VERSIONNUMBER being the version of the core):
macOS: ~/Library/Arduino15/packages/CORENAME/hardware/avr/VERSIONNUMBER
Linux: ~/.arduino15/packages/CORENAME/hardware/avr/VERSIONNUMBER
Windows: C:\Users\USERNAME\AppData\Local\Arduino15\packages\ACORENAME\hardware\avr\VERSIONNUMBER
Note that for ATtinyCore starting with version 2.0.0, you do not need to do this since this core already exports the ELF files.
Because of the compiler optimization level that is used by the Arduino IDE, the machine code produced by the compiler does not
follow straightforwardly your source code. For this reason, it is advisable to use the optimization flag -Og (compile in a debugging
friendly way) instead of the default optimization flag -Os (optimize to minimize space). If you are using the console line interface
arduino-cli, then this is easily achieved by adding the following option to the arduino-cli compile command:
--build-property build.extra_flags="-Og"
I noticed recently, that another optimization setting can significantly impact your "debugging experience." Usually, link-time
optimization is enabled in the Arduino IDE, which has the effect that most information about class inheritance and object attributes
vanishes. So, if you want to debug object-oriented code, then it makes sense to disable this kind optimization and add the flag -
fno-lto.
So, what can you do, if you do not want to use the CLI interface, but the IDE? You can modify the [Link] file (residing in the
same directory as the [Link] file) and introduce for each type of MCU a new menu entry debug that when enabled adds
the build option -Og.
In order to simplify life for you, I have provided a Python script (in the folder examples/configuration-files) called
[Link]. Copy this script into the same folder as where you copied [Link], change into this folder,
and execute the script by calling python3 [Link]. This will add a menu entry for each listed MCU/board in the
[Link] file. Again, for ATtinyCore starting with version 2.0.0, this is not necessary because you already can choose the
optimization options.
Now you have to restart the Arduino IDE. If you select an menu entry in the boards menu, then you will notice that there is a new
menu option Debug.
Finally, you need to install avr-gdb, the AVR version of the GDB debugger. It used to be part of the Arduino IDE toolchain. However,
it is not any longer. Depending on your OS, you can get the executable as follows:
macOS: Use homebrew to install it.
Linux: Install avr-gdb with your favorite packet manager.
Windows: You can download a complete AVR-GCC toolchain including the debugger avr-gdb from Microchip or from Zak's
Electronic Blog~*.
Setting Up the Hardware
The software is now in place. What remains to be done is to connect the hardware debugger to the host and to the target. The
connection to the host is straight-forward, because we just need to connect the two using a USB cable. When you want to start the
debugging session, you will need the name of the serial port. However, I assume you are able to figure that out for yourself. The
connection to the target is a bit more involved.
Requirements on the RESET Line
Since the RESET line of the target system is used as an open-drain, asynchronous, half-duplex serial communication line, one has
to make sure that there is no capacitive load on the line when it is used in debugWIRE mode. Further, there should be a pull-up
resistor of around 10 kΩ. According to reports of other people, 4.7 kΩ might also work. And the RESET line should, of course, not
be directly connected to Vcc and there should not be any external reset sources on the RESET line.
If your target system is an Arduino Uno, you have to be aware that there is a capacitor between the RESET pin of the ATmega328
and the DTR pin of the serial chip, which implements the auto-reset feature. This is used by the Arduino IDE to issue a reset pulse
[Link] 5/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
in order to start the bootloader. One can disconnect the capacitor and auto-reset-line by cutting a solder bridge labeled *RESET EN*
on the board (see picture), but then you cannot use the automatic reset feature of the Arduino IDE any longer.
A recovery method may be to either put a bit of soldering on the bridge or better to solder two pins on the board and use a jumper.
Alternatively, you could always manually reset the Uno before the Arduino IDE attempts to upload a sketch. The trick is to release
the reset button just when the compilation process has finished.
Other Arduino boards, such as the Nano, are a bit harder to modify, while a Pro Mini, for example, can be used without a problem,
provided the DTR line of the FTDI connector is not connected. In general, it is a good idea to get hold of a schematic of the board
you are going to debug. Then it is easy to find out what is connected to the RESET line, and what needs to be removed. It is
probably also a good idea to check the value of the pull-up resistor, if present.
So, what is the worst-case scenario when using debugWIRE? It could happen that you successfully bring your target chip into
debugWIRE mode using ISP programming (see below), but then you cannot communicate over the RESET line. In particular, you
may not be able to put the target back in a state, in which ISP programming is possible. Your MCU is bricked. It still works with the
firmware programmed last time. However, the only way to reset the MCU is now to power-cycle it. Further, it is impossible to
reprogram it using ISP programming.
There are two ways out. First you can try to make the RESET line compliant with the debugWIRE requirements. Then you should be
able to connect to the target using the hardware debugger. Second, you can use high-voltage programming, where 12 volt have to
be applied to the RESET pin. So you either remove the chip from the board and do the programming offline or you remove any
connection from the RESET line to the Vcc rail and other components on the board. Then you can use either an existing high-
voltage programmer or you build one on a breadboard.
Connecting the Debugger to the Target
I use an ATtiny85 on a breadboard as the example target system and an Uno as the example debugger. However, any MCU that
supports debugWIRE would do as a target. And one could even use an Arduino Uno as a target, provided the modifications
described in the previous subsection are done.
[Link] 6/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
First of all, notice the capacitor of 10 µF or more between RESET and GND on the Uno board. This will disable auto-reset of the
Uno board. This is optional and is helpful to speed up the connection process to the host.
Second, note the LED and resistor plugged in to pin 7 and 6. This is the system LED which is used to visualise the internal state of
the debugger. Again, this is optional, but very helpful. There are four states the debugger can be in, and each one is signaled by a
different blink pattern of the system LED:
not connected (LED is off),
waiting for power-cycling the target (LED flashes every second for 0.1 sec),
target is connected (LED is on),
error state, i.e., not possible to connect to target or internal error (LED blinks furiously every 0.1 sec).
Third, as you can see, the Vcc rail of the breadboard is connected to pin D9 of the Arduino Uno so that it will be able to power-cycle
the target chip. Note that powering the target system through a GPIO of the debugger works only as long as the target system
needs 20 mA or less. Otherwise, you need to power the target externally and then power-cycle the system manually, when that
becomes necessary.
Furthermore, pin D8 of the Arduino Uno is connected to the RESET pin of the ATtiny (pin 1). Note the presence of the pull-up
resistor of 10kΩ on the ATtiny RESET pin. The remaining connections between Arduino Uno and ATtiny are MOSI (Arduino Uno
D11), MISO (Arduino Uno D12) and SCK (Arduino Uno D13), which you need for ISP programming. In addition, there is a LED
connected to pin 3 of the ATtiny chip (which is PB4 or pin D4 in Arduino terminology). The pinout of the ATtiny85 is given in the next
figure (with the usual "counter-clockwise" numbering of Arduino pins).
[Link] 7/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
Here is a table of all the connections so that you can check that you have made all the connections.
ATtiny pin# Arduino Uno pin Component
1 (Reset) D8 10k resistor to Vcc
2 (D3)
3 (D4) 220 Ω resistor to LED (+)
4 (GND) GND LED (-), decoupling cap 100 nF, blocking cap of 10µF (-),
5 (D0, MOSI) D11
6 (D1, MISO) D12
7 (D2, SCK) D13
8 (Vcc) D9 10k resistor, decoupling cap 100 nF
RESET blocking cap of 10 µF (+)
D7 system LED (+)
D6 200 Ω to system LED (-)
If instead of an ATtiny85, you want to debug an Uno board, everything said above applies here as well. A Fritzing sketch showing
you the connections is below. Remember to cut the `RESET EN` solder bridge on the target board as described above.
[Link] 8/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
Note that sourcing the target board from an ATmega pin might stretches the limits a bit. In fact the voltage goes down to 4V. You can
also connect the supply pin of the target board to the 5V on the debbugger board and do the power-cycling manually when the
systen LED signals that.
Debugging the Sketch
After having installed the software and prepared the hardware, we are now ready to start a debugging session.
Debugging under PlatformIO
Debugging with PlatformIO is very straight-forward. After making the project you want to debug the current project, you simply have
to select the Debug icon on the left.
This will reconfigure the GUI to a debugging configuration, showing a number of different information panes. When you now click on
the green triangle labelled PIO Debug, a debug session will be started. This will compile the project and start the debugger. The
target will be switched into debugWIRE mode, either by power-cycling automatically or by requesting (by the system LED blink
pattern and a message in the Debug Console) you to power-cycle. If the MCU was already in debugWIRE mode, perhaps
from a previous debug session, no power-cycling will be necessary.
[Link] 9/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
The program will be uploaded and started. The initial stop is in the main routine (signaled by the yellow triangle to the left of the
line numbers). You can now use the execution control panel in the upper right corner to start (and stop) execution. Or you can
single-step, either not stepping into function calls (step-over) or stepping into the function (step-in). The upward arrow initiates a
step-out operation, i.e, execution is continued until the current function is finished. The green circle initiates a restart, and the red
square terminates the debug session.
[Link] 10/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
You can now single-step into the loop function, or you may also reload the [Link] file into the editor window. There you may
want to set breakpoints, i.e., points where execution shall be stopped. This can be done by clicking in the empty column to the left of
the line numbers. That will place a read dot, at which execution will be stopped.
There are now a lot of things you could do to inspect the inner workings of your programs and you may even change values of your
variables by typing the respective GDB command into the input line of the displayed Debug Console. A good introduction to
debugging with PlatformIO has been written up Valerii Koval: Debugging with PlatformIO, Part I and Part II.
[Link] 11/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
If you want to bring back the MCU into normal state, you can type monitor dwoff into the input line of the Debug
Console during a debugging session. Alternatively, when the debugging session is not active any longer, you can first click on
the PlatformIO symbol on the left (the ant), and then select under Program Tasks the Debug/Custom category, where
you will find the entry DebugWIRE Disable. Clicking on that will disable the debugWIRE mode.
[Link] 12/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
Debugging with avr-gdb
Let us now start a debug session using only avr-gdb. So compile the example [Link] with debugging enabled and require the
binary files to be exported, which gives you the file [Link] in the sketch directory. Then connect your Uno to the host and
start avr-gdb. All the lines starting with either the > or the (gdb) prompt contain user input and everything after # is a comment.
<serial port> is the serial port you use to communicate with the Uno.
Shell
> avr-gdb -b 115200 [Link]
GNU gdb (GDB) 10.1
Copyright (C) 2020 Free Software Foundation, Inc.
...
Reading symbols from [Link]...
(gdb) target remote <serial port> # connect to the serial port of debugger
Remote debugging using <serial port> # connection made
0x00000000 in __vectors () # we always start at location 0x0000
(gdb) monitor dwconnect # show propertied of the debugWIRE
connection
Connected to ATtiny85
debugWIRE is now enabled, bps: 125736
(gdb) load # load binary file
Loading section .text, size 0x714 lma 0x0
Loading section .data, size 0x4 lma 0x714
Start address 0x00000000, load size 1816
Transfer rate: 618 bytes/sec, 113 bytes/write.
(gdb) list loop # list part of loop and shift focus
6 byte thisByte = 0;
7 void setup() {
8 pinMode(LED, OUTPUT);
9 }
10
11 void loop() {
12 int i=random(100);
[Link] 13/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
13 digitalWrite(LED, HIGH);
14 delay(1000);
15 digitalWrite(LED, LOW);
(gdb) break loop # set breakpoint at start of loop
function
Breakpoint 1 at 0x494: file ..., line 12.
(gdb) br 15 # set breakpoint at line 15
Breakpoint 2 at 0x4bc: file ..., line 15.
(gdb) c # start execution at PC=0
Continuing.
Breakpoint 1, loop () at /.../[Link]
12 int i=random(100);
(gdb) next # single-step over function
13 digitalWrite(LED, HIGH);
(gdb) n # again
14 delay(1000);
(gdb) print i # print value of 'i'
$1 = 7
(gdb) print thisByte # print value of 'thisByte'
$2 = 0 '\000'
(gdb) set var thisByte = 20 # set variable thisByte
(gdb) p thisByte # print value of 'thisByte' again
$3 = 20 '\024'
(gdb) step # single-step into function
delay (ms=1000) at /.../wiring.c:108
108 uint32_t start = micros();
(gdb) finish # execute until function returns
Run till exit from #0 delay (ms=1000)
at /.../wiring.c:108
Breakpoint 2, loop () at /.../[Link]
15 digitalWrite(LED, LOW);
(gdb) info br # give information about breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000494 in loop()
at /.../[Link]
breakpoint already hit 1 time
2 breakpoint keep y 0x000004bc in loop()
at /.../[Link]
breakpoint already hit 1 time
(gdb) delete 1 # delete breakpoint 1
(gdb) detach # detach from remote target
Detaching from program: /.../[Link], Remote target
Ending remote debugging.
[Inferior 1 (Remote target) detached]
(gdb) quit # quit debugger
>
Note that the MCU is still in debugWIRE mode and the RESET pin cannot be used to reset the chip. If you want to bring the MCU
back to the normal state, you need to execute avr-gdb again.
Shell
> avr-gdb
GNU gdb (GDB) 10.1
...
(gdb) set serial baud 115200 # set baud rate
(gdb) target remote <serial port> # connect to serial port of debugger
Remote debugging using <serial port>
0x00000000 in __vectors ()
(gdb) monitor dwoff # terminate debugWIRE mode
Connected to ATtiny85
debugWIRE is now disabled
(gdb) quit
>
[Link] 14/15
07/02/2022, 09:27 Debugging an Arduino project with GDB on Classic ATtiny and Small ATmega MCUs - CodeProject
Of course, you could have done that before leaving the debug session above.
You have seen a number of different GDB commands already in the above example, but there are tons more. A good reference card
can be found online at the GDB documentation website, which contains also an extensive manual. I also recommend the tips on
using GDB for embedded debugging written up by Jay Carlson.
Summary
So far, embedded debugging of classic ATtiny and small ATmega MCUs was possible only with proprietary soft- and hardware or it
had a lot of limitations. With the recently developed open-source firmware dw-link, it is possible to turn an Arduino Uno (or similar
board) into a hardware debugger, which translates between debugWIRE and GDB RSP. In other words, now one can use avr-gdb in
order to debug MCUs that support debugWIRE. In this article, it is outlined how to install dw-link on an Arduino board, to set up a
debugging environment, to connect the hardware debugger to the target and the host, and to perform a debugging session.
Hopefully, this will make debugging Arduino sketches for these MCUs much more enjoyable.
History
19th January, 2022: Provided updated source code (1.3.2) and corrected description of blinking patterns in order to fix a
problem caused by a timeout when reading a reply from the target
2nd February, 2022: Added Fritzing sketch for connecting an Uno as the target, updated description of how to modify Arduino
configuration files, and updated source code archive
License
This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)
About the Author
Bernhard NebelArduino addict
Retired
Germany
[Link] 15/15