This project is in alpha stage. Only a few ATTiny devices have been tested, but most UPDI-based devices are expected to work. Please open issues if you encounter problems!
~updi-gdbserver~ bridges [[https://sourceware.org/gdb/][avr-gdb]] and you favorite [[https://en.wikipedia.org/wiki/AVR_microcontrollers#UPDI][UPDI]]-enabled AVR microcontroller. It can be used to debug (stop, inspect, modify) and program your target.
: avr-gdb : +------------+ : | | : | TCP | : +-----+------+ : | gdb remote UART : | protocol dongle AVR chip : +-----+----------+ +-------+ +-------------+ : | TCP | | | | | : | (listen) | | TX |---1kOhm--+--+ UPDI pin | : | tty +------+ | | | | : | | | RX |----------+ | | : +----------------+ +-------+ +-------------+ : updi-gdbserver | | : GND GND
** Getting started
*** Building
Unfortunately, this tool is written in an exoteric language and has unusual build steps. Try this:
apt install chicken-bin # or pacman -S chicken or something thereof chicken-install -s srfi-18 # install dependencies git clone https://github.com/kristianlm/updi-gdbserver cd updi-gdbserver csc -O1 gdbserver.scm # or chicken-csc ./gdbserver -b
Set baudrate of UPDI serial port (required) -p Set TCP listening port for GDB-server (defaults to 4444) -r Set TCP listening port for Chicken Scheme repl (defaults to off) -v Verbose logs to stderr -g Disable initial (SIB) greeting +end_src
*** Hardware setup
Follow the ordinary [[https://github.com/microchip-pic-avr-tools/pymcuprog#serial-port-updi-pyupdi][pymcuprog]] guidance for how to get the serial port set up to talk UPDI.
Since the UPDI interface resembles debugWIRE (see below), you can also give [[https://github.com/dcwbrown/dwire-debug][dwire-debug]] a read for more details and for some UART adapter recommendations. It can also help to try with ~avrdude~ first, since its ~serialupdi~ programmer uses the same communication mechanisms:
avrdude -c serialupdi -P /dev/ttyUSB1 -b 9600 -p t414
+end_src
For the devices that have a separate UPDI-pin, this should be all that's required to use UPDI. Some devices share the UPDI pin and need a special high-voltage sequence to enable UPDI on the target. This project does not support that. Luckily, UPDI has been enabled by default on all devices tested so far. So if you're willing to sacrifice a pin dedicated to UPDI, this simple UART-based debugging experience may be for you.
*** Building target example
If you're lucky enough to have a version of ~avr-gcc~ above 12.0 or 13.0 (and recent accompanying ~avr-libc~), the newer UPDI-enabled AVR devices are supported out of the box:
avr-gcc -DF_CPU=1000000 -Os -g3 -mmcu=attiny414 \ example-led-blink.c -o example-led-blink.elf
+end_src
Older ~avr-gcc~ versions can still target the newer UPDI-enabled devices, but you'll need to provide appropriate headers and libraries. https://start.atmel.com/ may be able to provide you with a project template.
The ~-g~ option ensures that debugging information, like symbol names, is available to ~avr-gdb~. The ~-g3~ option also includes preprocessor macros, like ~PORTA~, which is handy to have available in your ~avr-gdb~ session.
Programming your target can be done with the ~load~ command from ~avr-gdb~. Here, ~avr-gdb~ will extract the program section of the elf-file and send that to ~updi-gdbserver~ in a series of ~vFlashWrite~ commands followed by a final ~vFlashDone~. ~updi-gdbserver~ holds the flash pages in memory and writes the modified pages to the target device after receiving ~vFlashDone~. When recompiling your elf-files, running ~load~ is sufficient to both update the (modified pages of) target flash, and update the current ~avr-gdb~ session with any changes.
*** Running ~gdbserver~
Complete the build steps above, then try this, for example:
./gdbserver -b 115200 /dev/ttyUSB1 Starting gdbserver on port 4444. Press ctrl-c to quit. Connect with, for example: avr-gdb -ex "target extended-remote 127.0.0.1:4444" example-led-blink.elf
+end_src
*** Running ~avr-gdb~
With ~gdbserver~ running and the stars aligned, you might be able to do something this:
avr-gdb --eval-command "target remote 127.0.0.1:4444" example-led-blink.elf GNU gdb (GDB) 12.1 …
program target flash:
(gdb) load Loading section .text, size 0xd4 lma 0x0 Start address 0x00000000, load size 212 Transfer rate: 237 bytes/sec, 42 bytes/write.
(gdb) info b No breakpoints or watchpoints.
(gdb) c Continuing.
^C Program received signal SIGINT, Interrupt. 0x000000c8 in _delay_ms (ms=100) at /home/user/x/avr//avr/include/util/delay.h:187 187 builtin_avr_delay_cycles(__ticks_dc);
(gdb) hbreak example-led-blink.c:7 Hardware assisted breakpoint 1 at 0x4c: file example-led-blink.c, line 7.
(gdb) info br Num Type Disp Enb Address What 1 hw breakpoint keep y 0x0000004c in main at example-led-blink.c:7
(gdb) c Continuing .
Breakpoint 1, main () at example-led-blink.c:7 7 PORTA.OUT |= (1 << PIN6_bp); _delay_ms(100);
(gdb) p/t PORTA.IN $14 = 11110001
Note that software breakpoints are often the default for various IDEs etc. These may still work but haven't been tested extensively, and also wear down the flash. Being explicit about ~hbreak~ instead of ~break~ is therefore currently recommended.
When the target program is running, ~updi-gdbserver~ has to poll the target (currently at 100 Hz) which causes high UART activity.
** Comparison to debugWIRE
debugWIRE is an older protocol for debugging AVR devices. It's on the very popular ~attiny85~, for example. It has a lot in common with UPDI, particularly in that it's a 1-wire, half-duplex, UART-based interface that can be used to debug AVR chips with just a UART adapter.
One major advantage of UPDI versus debug-wire is that the UPDI UART baud rate is independent of target CPU speed. The target UPDI module will detect the host UPDI baudrate using the ~0x55~ mark, and reply with the same baudrate. This, for example, makes it possible to change the target CPU clockspeed during a debugging session.
** REPL
⚠ This is highly experimental and subject to change.
There is also a Scheme REPL available where you can experiment. I find it useful to poke at peripheral registers interactively from a REPL environment before I start writing any code meant to run on the target. Below is an example of using the DAC peripheral while the target CPU is stopped.
me@workstation> rlwrap nc localhost 1234 ;; nrepl on (./gdbserver)
This should produce a scope trace like this:
[[./images/scope-dac.png]]
The DAC output is shown in purple, and the rather slow UPDI UART communication is shown in blue. The delays between the ~dac~ calls are caused by UART communication. This could probably be improved by investigating at guard time and UPDI clock speed.
** TODOs
Too many to mention here, the source contains a lot of them. But a few important ones:
Support multiple hardware breakpoints
Detect target device automatically (based on the 24-bit signature)
registers: track bitfields and pretty-print
Find out what register ~#x0f90~ is for (stores opcode?) Can we run arbitrary instructions on the target CPU from there?
Support (gdb) info io_registers
Integrate with chicken-debugwire? If the API can be generalized.
EEPROM support?
Monitor for wdt reset and inform gdb (SIGILL?)
fix: PORTB is gone!
Many, many more.