Was ist PyAvrOCD? Wie benutzt man es? Was sind die Stärken? Welche Alternativen gibt es? Warum hat es so lange bis zur Version 1.0.0 gedauert? Und wie spricht man „PyAvrOCD“ aus? Diese und weitere Fragen werden in diesem Blogbeitrag behandelt.
TL;DR: PyAvrOCD hilft beim Debuggen von Programmen, die auf klassischen AVR-Chips laufen.
GDB-Server für Embedded-Debugging
Neben proprietären Software-Entwicklungswerkzeugen für Embedded-Systeme gibt es eine große Anzahl an Open-Source-Entwicklungstools. Der symbolische Debugger GDB spielt dabei eine wichtige Rolle, oft implizit als Debug-Engine innerhalb einer IDE. Um GDB für Embedded-Debugging einzusetzen, benötigt man ein Interface zwischen GDB und dem Hardware-Debugger, der mit dem Zielsystem verbunden ist: einen GDB-Server. Dieser übersetzt zwischen dem Protokoll des Hardware-Debuggers (häufig CMSIS-DAP) und dem Remote Serial Protocol (RSP) von GDB.
PyAvrOCD ist ein solcher GDB-Server, zugeschnitten auf AVR-MCUs (derzeit nur klassische ATtinys und ATmegas). Er verbindet sich mit EDBG-basierten Microchip-Debuggern sowie mit dem DIY-Debugger dw-link.
Wofür steht PyAvrOCD und wie spricht man es aus?
Die Silbe Py weist darauf hin, dass das System in Python geschrieben ist, was es plattformunabhängig macht (das USB-Interface-Modul ist allerdings plattformabhängig). Avr steht für die AVR-MCUs. Offiziell ist AVR kein Akronym. Der Internet-Weisheit zufolge (inklusive Wikipedia) steht AVR jedoch für Alf und Vegard’s RISC-Prozessor. OCD ist ein Akronym für On-Chip Debugging, der Fähigkeit, direkt auf dem Produktionschip zu debuggen, statt In-Circuit-Emulation zu verwenden.
Und wie spricht man es aus? Da AVR und OCD ausbuchstabierte Akronyme sind, wäre die kanonische amerikanische Aussprache: Pie-Ay-Vee-Ar-Oh-See-Dee. Man darf es aber auch als „Piaf rockt“ aussprechen, das heißt, diese Software ist eine Hommage an die Chansonsängerin Édith Piaf.
Wie benutzt man PyAvrOCD?
Arduino-Nutzer können PyAvrOCD verwenden, indem sie einen debug-fähigen Arduino-Core installieren. Dieser kümmert sich um das Herunterladen und Installieren von PyAvrOCD und AVR-GDB, dem symbolischen Debugger für AVR MCUs. Danach wird der zuvor ausgegraute Debug-Knopf in der Arduino-IDE 2 aktiviert, und man kann mit dem Debuggen beginnen, wobei ein vorheriger Blick in die Quick-Start-Anleitungen lohnend ist.

Python-Nutzer können PyAvrOCD einfach mit pip oder pipx installieren:
pip install pyavrocd
Alle anderen können Binärdateien von der Releases-Seite des GitHub-Repositories herunterladen und in ein geeignetes Verzeichnis kopieren. Sobald PyAvrOCD in einem Verzeichnis liegt, das im PATH enthalten ist, kann man den GDB-Server wie folgt starten:
pyavrocd --manage all --device atmega328p
PyAvrOCD sucht dann nach einem angeschlossenen Hardware-Debugger und wartet anschließend auf den Verbindungsaufbau durch GDB. Bei Nutzung des Kommandozeilen-Interfaces könnte das so aussehen:
> avr-gdb blink.ino.elf
GNU gdb (GDB) 17.1
Copyright (C) 2026 Free Software Foundation, Inc.
...
(gdb) target remote :2000
Remote debugging using :2000
0x00000000 in __vectors ()
(gdb) monitor debugwire
debugWIRE mode is disabled
(gdb) monitor debugwire enable
*** Please power-cycle the target system ***
Ignoring packet error, continuing...
debugWIRE mode is enabled
(gdb) load
Loading section .text, size 0x596 lma 0x0
Start address 0x00000000, load size 1430
Transfer rate: 1 KB/sec, 1430 bytes/write.
(gdb) break loop
Breakpoint 1 at 0x470: file /Users/.../blink.ino, line 13.
Note: automatically using hardware breakpoints for read-only addresses.
(gdb) continue
...
Was sind die Stärken von PyAvrOCD?
Das Hauptziel beim Design von PyAvrOCD war ein möglichst einfaches und benutzerfreundliches Benutzerinterface. Dafür wurden folgende Ziele verfolgt:
- plattformübergreifend,
- einfach zu installieren,
- gut dokumentiert,
- konfigurierbare Fuse-Verwaltung,
- gut verständliche Präsentation von I/O-Registerinhalten,
- sicherer Zugriff auf I/O-Registe
- interrupt-sicheres und performantes Single-Stepping,
- minimaler Flash-Verschleiß,
- Robustheit (für alle unterstützten MCUs und Hardware-Debugger)
Die ersten drei Punkte sind selbsterklärend. Schauen wir uns die übrigen genauer an.
Konfigurierbare Fuse-Verwaltung
Beim Debuggen einer AVR-MCU sind oft Vorbereitungen nötig, etwa das Löschen von Lock-Bits, das Setzen der Debug-Fuse und das Deaktivieren der Bootloader-Fuse. Die meisten anderen AVR-GDB-Server überlassen das dem Nutzer. PyAvrOCD hingegen kann diese Aufgaben übernehmen – fein granular steuerbar über die Kommandozeilenoption --manage, mit der festgelegt wird, welche Fuses von PyAvrOCD verwaltet werden sollen.
Verständliche Präsentation von I/O-Registerinhalten
Eine zentrale Aufgabe beim Embedded-Debugging ist die Anzeige und ggf. das Ändern von I/O-Registern. PyAvrOCD selbst bietet dafür keine direkte Oberfläche, stellt jedoch SVD-Spezifikationen bereit. Diese werden aus den von Microchip bereitgestellten ATDF-Dateien mithilfe des Rust-Tools atdf2svd erzeugt. Die SVD-Dateien können dann von IDEs genutzt werden, um Meta-Informationen und Inhalte der I/O-Register anzuzeigen. In der Arduino IDE 2 werden die SVD-Dateien automatisch verwendet, um im CORTEX PERIPHERAL-Fenster (der Name ist irreführend) alle I/O-Register darzustellen. In anderen IDEs, zum Beispiel VS Code mit PlatformIO, muss der Pfad zur SVD-Datei explizit angegeben werden.
Sicherer Zugriff auf Peripherieregister
Das Lesen bestimmter I/O-Register kann zu Seiteneffekten führen. Beispielsweise löscht das Lesen des USART- oder des SPI-Datenregisters den gelesenen Wert. Das ist im Programm auch intendiert, stört aber wenn es durch den Debugger erfolgt. PyAvrOCD führt daher – basierend auf der ATDF-Spezifikation einer MCU – eine Liste von Registern, deren Lese- oder Schreibzugriff durch den Debugger blockiert werden, um unbeabsichtigte Seiteneffekte zu vermeiden.
Interrupt-sicheres und performantes Single-Stepping
Interrupts können beim Single-Stepping den Kontrollfluss stören und in die Interrupt-Vektortabelle führen. Im Gegensatz zu den meisten anderen GDB-Servern implementiert PyAvrOCD als Standardverhalten ein interrupt-transparentes Single-Stepping, bei dem Interrupts entweder im Hintergrund behandelt oder vollständig unterdrückt werden.
Zur Performance-Optimierung verwendet PyAvrOCD Range-Stepping, um Schleifen innerhalb einer Zeile effizient ausführen zu lassen, beispielsweise das Makro _delay_ms. Zusätzlich implementiert PyAvrOCD eine Synchronisation zwischen asynchronen Stop-Signalen und dem RSP-Protokoll, um zuverlässiges Anhalten während intensiver Single-Step-Operationen zu ermöglichen, was bei vielen anderen GDB-Servern nicht möglich ist.
Minimaler Flash-Verschleiß
Der Flash-Speicher in AVR-MCUs hat eine begrenzte Anzahl von Lösch-/Schreibzyklen. Bei klassischen Chips sind 10.000 Zyklen garantiert. Es ist zwar kaum vorstellbar, dass man diese Grenze durch die Neuprogrammierung des Flash-Speichers erreicht, aber Software-Breakpoints werden ebenfalls durch die Neuprogrammierung einzelner Flash-Seiten implementiert. Dabei ist es nicht nur das Setzen und Löschen von Breakpoints, die ihren Tribut fordern. Manchmal führt jeder Breakpoint-Treffer zu zwei Neuprogrammierungszyklen. Dies ist bei allen GDB-Servern außer PyAvrOCD der Fall, wenn Breakpoints bei Zwei-Wort-Instruktionen gesetzt werden.
PyAvrOCD minimiert den Flash-Verschleiß, indem es so weit wie möglich Hardware-Haltepunkte verwendet und bei Breakpoint-Treffern nie Flash-Neuprogrammierung auslöst. Bei Breakpoints an Stellen mit Zwei-Wort-Instruktionen simuliert PyAvrOCD die ursprüngliche Instruktion statt zwei Neuprogrammierungen durchzuführen.
Robustheit
Robustheit wurde durch umfangreiche Tests erreicht. Zusätzlich zu über 500+ Unit-Tests, die etwa 90 Prozent des Quellcodes abdecken, existieren Integrationstests sowie eine große Anzahl von End-to-End-Tests, die Nutzerinteraktionen über GDB simulieren.
Diese End-to-End-Tests wurden auf fast allen klassischen AVR-Mikrocontrollern mit einer Vielzahl unterschiedlicher Hardware-Debugger durchgeführt. Dabei stellte sich heraus, dass es zahlreiche chip-spezifische Eigenheiten gibt, die berücksichtigt werden müssen:
- ATtiny2313/ATtiny2313A: Beide Chips haben die gleiche Signatur, aber ihre On-Chip-Debugger-Register liegen an unterschiedlichen Adressen.
- ATtiny441, 841 und 1634: Der Flashing-Prozess unterscheidet sich von dem anderer Chips.
- ATmega48 und ATmega88: Diese Chips sind mit vielen Open-Source-Tools nicht debuggbar und können durch debugWIRE „gebrickt“ werden. PyAvrOCD implementiert eine Methode, die sie von ihren Cousins mit A-Suffix, die die gleiche Chipsignatur haben, unterscheidbar macht.
- ATmega88A, ATmega168A, ATmega328, ATmega328P: Diese Chips geben sich im debugWIRE-Modus als andere Varianten aus. Ähnliches gibt es auch bei den JTAG MCUs.
- ATmega16(A) und ATmega64(A): Diese Chips besitzen gesetzte, ungenutzte Bits im Program Counter, die auf dem Stack innerhalb von Rücksprungadressen sichtbar werden.
- ATmega329(P) und ATmega3250(P): Diese Chips zeigen gesetzte ungenutzte PC-Bits direkt beim Abfragen des PC-Werts, was GDB völlig verwirrt.
Es ist möglich, dass es weitere solcher Eigenheiten gibt, da nicht jede Chip-Variante der JTAG-Mega-MCUs getestet wurde. Für das größte Problem, nämlich die gesetzten ungenutzten Programmzählerbits, habe ich jedoch eine allgemeine Lösung entwickelt.
Ungenutzte Bits des Programmzählers werden von PyAvrOCD maskiert, wenn GDB den Programmzählerwert abfragt. Gesetzte Bits in Rückspungadressen werden von GDB mithilfe eines von mir bereitgestellten Patches maskiert. Bis der Patch in die Distribution aufgenommen wird, stelle ich eine gepatchte GDB-Version zur Verfügung, die mit den PyAvrOCD-Binärdateien ausgeliefert wird und von der Seite „Releases“ meines persönlichen avr-gdb-Repositorys heruntergeladen werden kann.
Welche Alternativen zu PyAvrOCD gibt es?
PyAvrOCD ist nicht der erste GDB-Server für AVRs. Insgesamt existieren inzwischen fünfzehn Implementierungen. Die folgende Tabelle (nach Entwicklungsbeginn sortiert) gibt einen Überblick.
| Name | Beginn der Ent- wick- lung | Unter- stützte Platt- formen | Debug- Inter- faces | Unter- stützte MCUs | Unter- stützte Hardware- Debugger | Kommentar |
|---|---|---|---|---|---|---|
| atbackend (Microchip Studio 7) | 1997 | Windows | Alle | Alle MCUs | Alle | Microchips propriatärer GDB-Server. |
| AVaRICE | 2001 | Linux, mittler- weile auch Windows und Mac (C++, Posix) | JTAG, debug- WIRE, PDI | Klassische ATtinys, ATmegas, and Xmegas | Alle außer die 5er Serie | Erster Open-Source AVR GDB-Server. |
| dwire-debug | 2015 | Plattform- übergrei fend (C) | debug- WIRE | ATtiny13, 84, 85, 841, 45, ATmega 168, 328 | USB-UART Konverter | Minimale Implementation, nur ein Hardware-Breakpoint; basiert auf Reverse-Engineering des debugWIRE Protokolls. |
| debugwire-gdb-bridge | 2018 | Plattform- übergrei fend (Pascal) | debug- WIRE | ATtiny 13, 2313, 24, 44, 84, 25, 45, 85, 441, 841 ATmega 48, 88, 168, 328 | USB-UART Konverter | Basiert auf dwire-debug, ist aber sehr viel vollständiger. |
| dwire-gdb | 2019 | Linux (C) | debug- WIRE | Attiny 84, 85 | USB-UART Konverter | Nur ein Hardware-Breakpoint, keine Flash-Programmierung |
| dwtk | 2019 | Plattform- übergrei fend (Go) | debug- WIRE | Alle debugWIRE MCUs | USB-UART Konverter | Vollständiger GDB-Server für alle debugWIRE MCUs. |
| pyAVRdbg | 2020 | Plattform- übergrei fend (Python) | UPDI | Alle modernen MCUs | Alle EDBG-basierten Debugger | Keine Flash-Programmierung, basiert auf pymcuprog. |
| dw-link | 2021 | Plattform- übergrei fend (Arduino Firmware) | debug- WIRE | Alle debugWIRE MCUs | Arduino UNO R3 mit GDB-Server Firmware | RSP Interface über die serielle Schnittstelle. |
| Bloom | 2021 | Linux (C++, Posix) | Alle | Alle MCUs | Alle EDBG-basierten Debugger | Bester GDB-Server für Präsentation der I/O-Register und Abdeckung aller MCUs. Überdurchschnittlicher Flash-Verschleiß. |
| pyavrdebug | 2022 | Plattform- übergrei fend (Python) | UPDI | Einige moderne MCUs | Alle EDBG-basierten Debugger | Lediglich 2 Hardware-Breakpoints und keine Flash-Programmierung, basiert auf pymcuprog. |
| gdb-debug-wire-integrated-server | 2022 | Plattform- übergrei fend (Arduino Firmware) | debug- WIRE | ATtiny85, ATmega328P | Implementiert auf ATmegaXU2 | RSP Interface über die serielle Schnittstelle. Zusätzlich RTT Schnittstelle. Kann in den ATmega16U2 auf einem UNO-Board geflasht werden. |
| updi-gdbserver | 2023 | Plattform- übergrei fend (Scheme) | UPDI | Einige moderne MCUs | USB-UART Konverter | Lediglich 2 Hardware-Breakpoints, basiert auf Reverse-Engineering des UPDI-Protokolls. |
| avr-absurd | 2024 | Plattform- übergrei fend (Python) | UPDI | Einige moderne MCUs | USB-UART Konverter | Lediglich 2 Hardware-Breakpoints, keine Flash-Programmierung, basiert auf Reverse-Engineering des UPDI-Protokolls. |
| PK5-UPDI-GDB-Server | 2024 | Plattform- übergrei fend (Python) | UPDI | Alle modernen MCUs | PICkit4 & 5 | Nur 2 Hardware-Breakpoints, basiert auf Reverse-Engineering des USB-Protokolls mit den PICkit-Debuggern. |
Warum also brauchen wir eine fünfzehnte Implementierung eines GDB-Servers? Wie aus der Tabelle hervorgeht, laufen manche GDB-Server nur auf einer Plattform, unterstützen nur eine Debug-Schnittstelle oder sind nur mit wenigen MCUs kompatibel. Einige sind zudem lediglich Proof-of-Concept-Implementierungen, die zwar technisch interessant, aber für den täglichen Einsatz ungeeignet sind. Darüber hinaus gibt es bei der Dokumentation und beim Installationskomfort häufig Luft nach oben.
Während einige der weiter oben genannten Designziele für PyAvrOCD auch in anderen GDB-Servern realisiert wurden, ist die Kombination all dieser Eigenschaften in einem einzigen System bisher nur für PyAvrOCD umgesetzt. Darüber hinaus wurde PyAvrOCD in die Arduino IDE 2 integriert, was eines der Hauptziele der Entwicklung war. Der einzige weitere potenzielle Kandidat für eine solche Integration wäre AVaRICE gewesen. Da ich jedoch deutlich vertrauter mit Python als mit C++ bin, entschied ich mich, den GDB-Server von Grund auf neu zu entwickeln, auf Basis von pymcuprog.
Warum hat es so lange gedauert?
Es dauerte fast ein halbes Jahr, um von dw-gdbserver, einem Python-GDB-Server für debugWIRE-MCUs, ausgehend PyAvrOCD zu entwickeln. Ich hatte gehofft, schneller zu sein. Also: Was hat so lange gedauert?
Vergleicht man die Verzeichnisstruktur von dw-gdbserver mit PyAvrOCD, wird deutlich, dass eine erhebliche Umstrukturierung stattgefunden hat. Ein Blick in die CHANGELOG-Einträge zeigt umfangreiche Refactorings. Zusätzlich wurden zahlreiche neue Features implementiert, darunter die Möglichkeit, Monitor-Kommandos als Kommandozeilenoptionen zu verwenden. Auch die Anpassung von dw-link, um mit PyAvrOCD kompatibel zu sein, nahm Zeit in Anspruch.
Große Zeitanteile entfielen außerdem auf das Schreiben und Ausführen der Tests. Ich halte dies jedoch für äußerst wertvoll, da es die Anzahl verbleibender Fehler im GDB-Server minimiert. Damit verbunden war auch Zeitaufwand für das Aufspüren von Fehlern oder Schwächen in anderen Software-Tools, etwa in der avr-gcc-Toolchain, der FASTLED-Bibliothek, simavr und GDB. Auch pymcuprog scheint nicht fehlerfrei zu sein. Schließlich nahm auch das Schreiben der Dokumentation Zeit in Anspruch.
Zusammenfassung
Alles in allem bin ich mit dem Ergebnis sehr zufrieden, auch wenn es etwas länger gedauert hat. Die investierte Zeit war gut angelegt, finde ich. Oder um es mit Édith Piaf zu sagen: Non, je ne regrette rien. Der nächste Schritt ist jetzt, UPDI zu integrieren.
Schreibe einen Kommentar