Tento post je součástí série článků o vývoji pro STM8 s OpenSource nástroji. Celý popis toolchainu najdete v postu Vývoj STM8 s OpenSource nástroji


GDB maskot >>

GDB (GNU Debugger) je standardní nástroj na hledání chyb v software. Pokud budeme chtít program krokovat a za běhu se dívat do proměnných budeme potřebovat upravený STM8-GDB z GNU binutils

Před použitím GDB je třeba mít nainstalovaný a spuštěný Open On Chip Debugger. Tento nástroj zprostředkovává komunikaci mezi GDB a čipem přes debugovací rozhraní — ST-LINK. GDB je tedy nástroj pro ladění (sem zadáváte příkazy, krokujete, prohlížíte proměnné), zatímco OpenOCD zajišťuje fyzickou komunikaci s mikrokontrolérem.

Nenechte se odradit

Ano, je to textové rozhraní, žádná klikátka ani pouťové efekty. Pouze textové příkazy. Toto rozhraní však může být velmi efektivní a díky prográmku cgdb i krásně barevné a přehledné.

Instalace

Legacy verze

Po internetu najdete různé návody na build GDB verze 8.1 ze starších zdrojů. Konkrétně je to projekt stm8-binutils-gdb. Funguje to dobře, ale pokud byste to chtěli použít doporučuji raději naše repo stm8-binutils-gdb-legacy, protože zdrojáky z něj jdou buildnout i dnes i se současným Python 12+.

Obecně bych ale doporučil novější verzi stm8-gdb založenou na GDB v15.0

Novější a hezčí GDB v15.0

V současné době je k dispozici výrazně novější verze stm8-gdb založená na GDB 15.0, která přináší vylepšení oproti staré verzi 8.1: Práce s ním je přehlednější díky barvičkám a celkově je to prostě novější verze…

Zdrojáky si můžete stáhnout z mého forku na GitLabu:

1
2
3
# GitLab (doporučeno)
git clone --depth 1  https://gitlab.com/spseol/mit-no/stm8-binutils-gdb.git
cd stm8-binutils-gdb

(Původní fork, který zařadil patche pro GDBv8.1 do aktuálního GDB, raději nepoužívejte protože obsahuje několik chybiček.)

Build je velmi jednoduchý:

1
2
3
4
5
6
7
./configure \
  --prefix=/usr/local/stow/stm8-binutils-gdb \
  --target=stm8-none-elf32 \
  --program-prefix=stm8-

make -j$(nproc)
sudo make install

A opět Stow.

1
2
cd /usr/local/stow
sudo stow stm8-binutils-gdb

Spuštění GDB

GDB můžete spustit ve dvou módech:

S TUI (Text User Interface) — přehlednější textové rozhraní s okny pro kód:

1
stm8-gdb --tui build-STM8S208/out-STM8S208.elf

Bez TUI — jednodušší příkazový řádek:

1
stm8-gdb build-STM8S208/out-STM8S208.elf

Samozřejmě nemusíte to vždy znovu vypisovat. Je to napsané v Makefile, takže stačí zavolat:

1
make debug

Je třeba poznamenat, že ještě předtím, než zavoláte make debug musíte v jiném terminálu zavolat:

1
make openocd

Připojení a spuštění programu

Takže máme dva terminály: v jednom běží OpenOCD a ve druhém běží GDB. Než spustíte GDB, ujistěte se, že OpenOCD již běží.

Po spuštění GDB jednoduše zadejte:

1
(gdb) run

Příkaz run automaticky:

  1. Připojí se k OpenOCD (localhost:3333)
  2. Nahraje program do čipu
  3. Provede reset
  4. Spustí program

… má v sobě tedy ukryto asi toto:

1
2
3
(gdb) target remote localhost:3333
(gdb) load
(gdb) continue &

Základní příkazy GDB

V programu funguje tabulátor. To znamená, že při stisku klávesy TAB se GDB pokusí uhodnout, co chcete napsat a doplní slova tak, aby byla smysluplná. Aby ale TAB fungoval musí být focus na frejmu s příkazy a ne na frejmu se zdrojovým kódem.

Pokud zadáte jen Enter — tedy odentrujete prázdný příkaz, zopakuje se ten předchozí. Takže nemusíte pořád psát např. next, ale stačí ho napsat jednou a pak jen Enter.

Text User Interface

Několik příkazů a klávesových skratek pro TUI:

  • Ctrl+X A — zapne/vypne TUI mód (můžete přepínat kdykoliv)
  • Ctrl+X 2 — rozdělí okno (split layout)
  • Ctrl+L — překreslí obrazovku (užitečné když se rozbije)
  • layout src — zobrazí zdrojový kód
  • layout asm — zobrazí assembler
  • layout split — zobrazí obojí
  • layout regs — přidá zobrazení registrů
  • focus cmd — přepne focus na příkazový řádek
  • focus src — přepne focus na okno se zdrojovým kódem
  • Ctrl+X O — přepíná fokus mezi src, cmd, asm, atd.

Vizuální markery v TUI:

  • > — aktuální pozice programu (current execution point)

Breakpoint markery (kombinace hit/not-hit a enabled/disabled):

  • B+ — breakpoint byl zasažen (hit) a je aktivní (enabled)
  • B- — breakpoint byl zasažen (hit) a je vypnutý (disabled)
  • b+ — breakpoint nebyl ještě zasažen (not hit) a je aktivní (enabled)
  • b- — breakpoint nebyl ještě zasažen a je vypnutý (disabled)

Hardware breakpointy:

  • H+ / H- — hardware breakpoint hit, enabled/disabled
  • h+ / h- — hardware breakpoint not hit, enabled/disabled
list main, list main.c:20, list main.c:setup
nalistuje ve zdrojovém kódu příslušné místo, řádek, funkci

Breakpointy

b main, break main
nastaví breakpoint na vstup do funkce main
b 48, break 48
nastaví breakpoint na řádek 48
b milis.c:48, break milis.c:48
nastaví breakpoint na řádek 48 v souboru milis.c
info b, info breakpoints
vypíše informace o breakpointech
d 2, delete 2
vymaže breakpoint 2
disable 2, enable 2
dočasně vypne/zapne breakpoint 2

Ovládání běhu programu

r, run
spustí program (s auto-connect)
c, cont, continue
pokračuje v běhu programu, dokud nenarazí na breakpoint
s, step
vykoná jeden příkaz/řádek zdrojového kódu, vstoupí do funkcí
n, next
vykoná jeden příkaz/řádek zdrojového kódu, funkci vykoná jako jeden příkaz — nebude vstupovat do funkce
fin, finish
dokončí funkci, ve které se program právě nachází (pokud v ní není další breakpoint)
interrupt, Ctrl+C
přeruší program, program se zastaví tam, kde zrovna teď je

Proměnné a paměť

p time, print time
vypíše obsah proměnné time
p/x time, print/x time
vypíše obsah proměnné v hexadecimálním formátu
p *ptr@10
vypíše 10 prvků pole na které ukazuje pointer
p array[0]@10
vypíše 10 prvků pole od indexu 0
p/s string
vypíše řetězec (string)
x/10x array
examine memory — zobrazí 10 hodnot v hexadecimálním formátu od adresy array
x/s string
examine memory jako string (užitečné pro char*)

Formáty výpisu (použijte /formát za příkazem):

  • p/d variable — decimal (desítkově)
  • p/x variable — hexadecimal (šestnáctkově)
  • p/o variable — octal (osmičkově)
  • p/t variable — binary (binárně)
  • p/c variable — character (jako znak)
  • p/s variable — string (jako řetězec)

Display s formáty:

display time
vypíše obsah proměnné time pokaždé, když se program zastaví
display/d time
totéž jako display, ale vynutí desítkový formát
display/x time
zobrazí hodnotu v hexadecimálním formátu při každém zastavení
undisplay 2
už nebude vypisovat display číslo 2, když se program zastaví
info locals
vypíše všechny lokální proměnné
info variables
vypíše všechny globální proměnné

Monitor příkazy (ovládání OpenOCD)

monitor reset halt
resetuje čip a zastaví ho
monitor reset run
resetuje čip a spustí program
monitor halt
zastaví běžící program

Zjištění pozice

where, backtrace, bt
zobrazí call stack s aktuální pozicí (soubor, funkce, řádek)
frame, info frame
zobrazí detailní informace o aktuálním frame (včetně soubor:řádek)
info line
zobrazí přesnou informaci o aktuálním řádku
list
po zastavení programu (Ctrl+C) ukáže kód kolem aktuální pozice
set listsize 20
nastaví počet řádků, které ukáže příkaz list

Pokročilé

load
ručně nahraje program do čipu (obvykle není potřeba, dělá to run)
target remote localhost:3333
ručně se připojí k OpenOCD (obvykle není potřeba, dělá to run)
info sources
vypíše seznam zdrojových souborů
frame 2, f 2
přepne se na frame číslo 2 v call stacku

Watchpointy (zastaví program při změně/čtení proměnné):

Omezení STM8

OpenOCD pro STM8 podporuje pouze watchpointy délky 1 byte. Pro větší datové typy (např. uint16_t, uint32_t) budete muset sledovat jednotlivé byty nebo použít podmínkové breakpointy. Pokud se pokusíte nastavit watchpoint na větší typ, dostanete chybu: Error: Only watchpoints of length 1 are supported

watch variable
zastaví program při zápisu do proměnné (pouze 1 byte!)
rwatch variable
zastaví program při čtení z proměnné (pouze 1 byte!)
awatch variable
zastaví program při čtení i zápisu do proměnné (pouze 1 byte!)

Pokročilé breakpointy:

tbreak main
temporary breakpoint — smaže se automaticky po prvním zastavení
break main.c:42 if x == 5
podmínkový breakpoint — zastaví se jen když je podmínka splněna
condition 1 x > 10
přidá nebo změní podmínku u existujícího breakpointu číslo 1
ignore 1 5
ignoruje breakpoint číslo 1 příštích 5x (pak se aktivuje)

Zde něco málo více k příkazům print a display.

GDB %%

cgdb

cgdb je curses-based rozhraní pro GDB. Práce s ním je o něco málo příjemnější a barevnější než když používáte TUI vestavěné přímo v GDB. Dokumentaci naleznete na domovské stránce projektu.

Instalace

1
apt install cgdb

Spuštění

Opět je vše zapsáno v Makefile, takže pokud běží openocd stačí zavolat:

1
make cgdb

Případně můžete spustit ručně:

1
cgdb -d stm8-gdb -- out.elf

Ovládání

cgdb je jen nadstavba nad GDB — v dolním okně běží plnohodnotný GDB, takže platí vše, co je popsáno výše: breakpointy, watchpointy, print, display, monitor příkazy i veškerá další syntaxe jsou naprosto stejné.

cgdb má dvě okna: horní zobrazuje zdrojový kód, dolní je klasický GDB terminál. Přepínání mezi nimi funguje takto:

  • Esc — přepne fokus do okna se zdrojovým kódem (vim-like mód)
  • i — vrátí fokus zpět do GDB příkazového řádku

V okně se zdrojovým kódem platí vim klávesy:

  • Space — přidá/odebere breakpoint na aktuálním řádku
  • t — přidá/odebere dočasný (temporary) breakpoint na aktuálním řádku
  • j / k, 🡇 /🡅 — pohyb nahoru/dolů
  • Ctrl+F / Ctrl+B — stránkování dolů/nahoru
  • gg / G — skok na začátek/konec souboru
  • /pattern — hledání, n / N přeskakuje výsledky
  • o — otevře dialog pro výběr souboru

  • Ctrl+W — přepíná horizontální a vertikální layout oken

  • =/-, +/_ — zvětšuje/zmenšuje velikost okna se zdrojovým kódem

Funkční klávesy (fungují z obou oken):

  • F5run
  • F6continue
  • F7finish
  • F8next
  • F10next
  • F11step

Související posty