Skip to content
 

OpenOCD – kurz prežitia

OpenOCD je softvér, ktorý slúži na ovládanie MCU vybaveného JTAG rozhraním prostredníctvom ladiaceho JTAG adaptéra. Jeho veľkou výhodou je univerzálnosť a flexibilita – podporuje široké spektrum adaptérov a cieľových MCU. Preto stojí za pozornosť.

Miesto OpenOCD v projekte

openocd

OpenOCD slúži ako prostredník medzi vývojovým softvérom a ladeným MCU, pripojeným prostredníctvom JTAG adaptéra. Ovláda sa prostredníctvom GDB či už priamo alebo z vhodného IDE, prípadne GDB frontendu (tu hrá úlohu ako tzv. gdbserver). Súčasne umožňuje priame ovládanie cez telnet protokol. Okrem toho obsahuje ešte komunikačný kanál pre softvérové ovládanie (nie je zobrazený).

Svoju aktivitu zobrazuje na konzole (terminálové okno, z ktorého bol OpenOCD spustený), prípadne ju zapisuje do logu.

Koncepcia OpenOCD

OpenOCD je založený na skriptoch, písaných v jazyku Jim-Tcl, čo odľahčená verzia verzia jazyka Tcl. Všetky konfiguračné súbory sú implementované ako skripty v tomto jazyku, všetky nastavenia sú implementované ako príkazy v tomto jazyku a všetky príkazy sú implementované ako príkazy v tomto jazyku – sú to buď zabudované príkazy, volania funkcií alebo spustenie príkazov z iného konfiguračného súboru.

Táto koncepcia môže byť na prvý pohľad mätúca, keďže konfiguračné skripty nemajú obvyklú štruktúru, kde sa určitému parametru priradí konkrétna hodnota. Rozoberme si jednoduchý konfiguračný súbor (resp. skript), ktorý popisuje pripojenie procesora STM32F103C8 prostredníctvom JTAG adaptéra J-Link:

source [find interface/jlink.cfg]
transport select swd

set CHIPNAME F103C8
source [find target/stm32f1x.cfg]
  • príkaz na riadku 1 načíta a vykoná konfiguračné príkazy zo súboru jlink.cfg, nachádzajúceho sa v podadresári interface (relatívne ku koreňovému adresáru s konfiguračnými skriptami); syntax vyzerá podivnem ale ide o Tcl príkaz s príkazovou substitúciou: príkaz v hranatých zátvorkách sa vykoná a jeho výstup sa použije ako parameter príkazu source. Príkaz source vykoná príkazy z uvedeného súboru tak, ako keby tam boli priamo napísané. Je to taký ekvivalent C-čkového #include
  • príkaz na riadku 2 je zabudovaný príkaz; je to volanie funkcie transport s parametrami „select“ a „swd“. Parametre sú reťazce – keďže v Tcl je všetko reťazec, nie je potrebné použivať úvodzovky.
  • príkaz na riadku 4 je príkaz Tcl – nastavenie hodnoty premennej CHIPNAME na hodnotu „F103C8“
  • príkaz na riadku 5 načíta definíciu rodiny procesorov STM32F1xx a preskúma pripojený procesor – napríklad vyčíta a nastaví mapu pamäte; procesor pomenuje podľa premennej CHIPNAME, ktorú sme definovali na riadku 4

Keďže uvedená konfigurácia je skript, nič nám nebráni zmeniť napríklad riadok 5 nasledovne:

set chipcfg [find target/stm32f1x.cfg]
echo "Konfiguracia MCU: $chipcfg"
source $chipcfg

Výsledok je ten istý, navyše sa vo výstupe po spustení OpenOCD objaví informačný riadok s celou cestou ku konfiguračnému skriptu:

Konfiguracia MCU: /usr/local/share/openocd/scripts/target/stm32f1x.cfg

Koncepcia „všetko je skript“ sa podpísala aj na parametroch príkazového riadku. OpenOCD pozná v podstate dva (dôležité) parametre:

  • -f resp. --file načíta a vykoná uvedený skript; je to ekvivalent príkazu source [find ...]
  • -c resp. --command vykoná uvedený príkaz

Parametre -f a -c sa môžu ľubovoľne kombinovať a môžu byť uvedené niekoľkokrát. Ekvivalent skôr uvedeného skriptu bude príkaz:

openocd -f "interface/jlink.cfg" -c "transport select swd" -c "set CHIPNAME F103C8" -f "target/stm32f1x.cfg"

Spustenie OpenOCD

OpenOCD spustíme príkazom openocd s kombináciou parametrov -f (–file) a -c (–command), pomocou ktorých OpenOCD nakonfigurujeme. Príklad, ktorý už poznáme:

openocd -f "interface/jlink.cfg" -c "transport select swd" -c "set CHIPNAME F103C8" -f "target/stm32f1x.cfg"

Pokiaľ nezadáme žiaden parameter, načíta sa preddefinovaný konfiguračný súbor openocd.cfg z pracovného adresára.

OpenOCD na po spustení nachádza v konfiguračnej fáze. V tejto fáze sa načítajú a vykonajú všetky špecifikované a konfiguračné súbory, Úlohou tejto fázy je nakonfigurovať JTAG adaptér, nastaviť parametre komunikačného protokolu (typ, rýchlosť), nakonfigurovať reset signály, získať informácie o ladenom procesore: zoznam registrov, architektúru, mapu pamäte.

Konfiguračná fáza je ukončená inicializáciou. Inicializácia sa vykoná automaticky po spracovaní všetkých zadaných súborov a príkazov, alebo explicitne príkazom init. Explicitná inicializácia je potrebná v prípade, že chceme OpenOCD používať v dávkovom režime.

Po úspešnej inicializácii prechádza OpenOCD do vykonávacej fázy. V tejto fáze typicky čaká na spojenie cez niektorý z komunikačných kanálov (GDB, telnet) a vykonáva zadávané príkazy (interaktívny režim), prípadne vykonáva ďalšie príkazy, nachádzajúce sa v konfiguračných skriptoch alebo parametroch príkazového riadku (dávkový režim).

Interaktívny režim

V interaktívnom režime po úspešnej inicializácii OpenOCD očakáva príkazy. Terminálové okno, v ktorom sme spustili OpenOCD slúži výlučne ako stavová konzola, v ktorej sa vypisujú všetky stavové správy, prípadne výstupy príkazov. Toto okno sa nedá využiť na ovládanie OpenOCD. Pre komunikáciu s OpenOCD je potrebné pripojiť sa prostredníctvom niektorého z komunikačných kanálov:

  • telnet kanál je hlavným komunikačným protokolom pre priamu komunikáciu s OpenOCD. Pripojiť sa môžeme ľubovoľným programom podporujúcim protokol telnet – napríklad príkazom telnet localhost 4444
  • GDB kanál slúží na pripojenie debugera GDB, na tomto kanáli poskytuje služby ako tzv. gdbserver. OpenOCD podporuje tzv. extended protokol. GDB nakonfigurujeme (manuálne alebo v súbore .gdbinit) pre pripojenie k OpenOCD príkazom target extended-remote localhost:3333
  • takzvaný TCL kanál slúži na komunikáciu s inými aplikáciami pomocou JSON-RPC protokolu; tento kanál prakticky využívať nebudeme.

Pri použití telnet kanála môžeme príkazy zadávať tak, ako sú popísané v dokumentácii prípadne v tomto článku. Ich výstup sa priamo zobrazí v terminálovom okne a súčasne na stavovej konzole.

Prostredníctvom GDB kanála komunikuje debuger GDB na pozadí pomocou protokolu gdbserver. Pomocou príkazu monitor (skrátene mon) môžeme posielať priamo príkazy OpenOCD, napríklad príkazom mon flash banks vypíšeme zoznam oblastí pamäte-

Dávkový režim

V dávkovom režime sa vykonajú príkazy špecifikované v skripte alebo v príkazovom riadku a po ich vykonaní sa OpenOCD automaticky ukončí. V dávkovom režime treba vykonať explicitnú inicializáciu príkazom init. Na záver treba ukončiť OpenOCD príkazom shutdown.

Inými slovami, v dávkovom režime sa vykonajú príkazy obkolesené príkazmi init a shutdown. Napríklad skript pre dávkové naprogramovanie MCU môže vyzerať nasledovne:

init
program program.elf verify
shutdown

A použijeme ho (za predpokladu že všetka potrebná inicializácia sa nachádza v súbore openocd.cfg) nasledovne:

openocd -f "openocd.cfg" -f "batchpgm.cfg"

Základné príkazy OpenOCD

Príkazy môžeme rozdeliť do dvoch základných skupín:

  • konfiguračné príkazy – slúžia na nastavenie parametrov ladiaceho adaptéra, cieľového MCU a parametrov komunikácie
  • výkonné príkazy – slúžia hlavne na prácu s cieľovým MCU

Naše rozdelenie na líši od delenia v oficiálnej dokumentácii, kde sa delia podľa fázy, v ktorej sa môžu použiť. Naše rozdelenie sleduje účel použitia.

Konfiguračné príkazy

Prevažná väčšina konfiguračných príkazov slúži na nakonfigurovanie parametrov ladiaceho adaptéra alebo konfiguráciu cieľového procesora. Väčšina z nich je mimo rámec tohoto úvodu, keďže v praxi budeme väčšinou využívať pripravené konfiguračné skripty. Avšak stále ostáva niekoľko príkazov, ktoré občas musíme použiť.

Konfigurácia komunikácie a nastavenie GDB

  • telnet_port port – TCP port, používaný pre pripojenie cez protokol telnet; ide o hlavný komunikačný kanál, pomocou ktorého dokážeme OpenOCD ovládať. Preddefinovaná hodnota je 4444
  • gdb_port port – TCP port, používaný pre pripojenie GDB (príkazom target remote); preddefinovaná hodnota je 3333
  • gdb_memory_map enable/disable – povoľuje GDB vyčítať informácie o rozdelení pamäte (implicitne povolené)
  • gdb_flash_program enable/disable – povoľuje programovanie FLASH pamäte prostredníctvom GDB (implicitne povolené)

Konfigurácia ladiaceho adaptéra a protokolu

  • transport list – vypíše zoznam implementovaných protokolov. Je to zoznam protokolov implementovaných v OpenOCD, každý ladiaci adaptér podporuje iba niektoré z nich.
  • transport select nazov_transportu – nastavenie transportného protokolu; názov transportu je jeden z protokolov vypísaných príkazom transport list
  • adapter_khz frekvencia – hodinová frekvencia JTAG adaptéra v kHz
  • jtag_rclk frekvencia – pokúsi sa povoliť adaptívne časovanie a ak zlyhá, nastaví špecifikovanú frekvenciu adaptéra. Adaptívne časovanie potrebuje signál RTCK (resp RCLK), ktorý poskytuje ladený procesor JTAG adaptéru a slúži na synchronizáciu JTAG príkazov s jadrom ladeného procesora.

Konfigurácia RESET signálov

Toto nastavenie je závislé od konštrukcie hardvéru – ak používame priemyselne vyrábanú vývojovú dosku, ku ktorej máme konfiguračný súbor k dispozícii, tam táto konfigurácia bude prednastavená. V prípade, že zapojenie nepoznáme, môžeme použiť preddefinované nastavenie: reset_config none separate – v tomto prípade sa reset signály nepoužijú a procesor sa bude resetovat pomocou príkazov JTAG

  • reset_config signály vlastnosti – konfigurácia reset signálov. Týmto príkazom špecifikujeme, ktoré z reset signálov SRST a TRST sú zapojené a ako sa navzájom ovplyvňujú. Základné možnosti pre signály sú none, trst_only, srst_only, trst_and_srst. Parameter vlastnosti popisuje chovanie a typ signálov – viac informácií nájdete v dokumentácii. Pokiaľ máme pochybnosti o zapojení reset signálov, môžeme použiť reset_config none ako bezpečnú voľbu.

Ukončenie konfiguračnej fázy

  • init – tento príkaz spustí inicializáciu nakonfigurovaného adaptéra a cieľového procesora. Tento príkaz sa vykoná automaticky po načítaní všetkých konfiguračných súborov; automatickej inicializácii sa dá zabrániť použitím príkazu noinit.
  • noinit – pokiaľ sa tento príkaz použije kedykoľvek počas konfiguračnej fázy, po načítaní všetkých konfiguračných súborov sa nespustí automaticky inicializácia. Využijeme ho v prípade, keď potrebujeme odladiť konfiguráciu adaptéra alebo cieľového MCU.

Výkonné príkazy

Do tejto skupiny patria príkazy ktorými skúmame stav ladeného MCU, spúšťame a zastavujeme program, či zapisujeme obsah FLASH pamäte.

Práca s registrami a RAM

  • reg [meno [hodnota]] – príkaz s bez parametrov vypíše obsah všetkých registrov; pri zadaní mena alebo čísla registra vypíše obsah príslušného registra. Obsah registrov sa obvykle udržiava vo vyrovnávacej pamäti – ak chceme zobraziť obsah priamo z procesora, musíme pripojiť kľúčové slovo force. Pokiaľ zadáme hodnotu, táto hodnota sa do registra zapíše.
  • mdw, mdh, mdb adresa [počet] – prečíta a vypíše obsah pamäte ako 32-bitové slovo (mdw), 16-bitové polslovo (mdh) alebo 8-bitový byte (mdb); je možné zadať počet, ak chceme vypísať viac pamäťových miest. V prípade, že náš procesor používa jednotku správy pamäte (MMU), adresa je interpretovaná ako virtuálna adresa podľa nastavenia MMU. Pokiaľ chceme pristupovať priamo k fyzickej pamäti, musíme zadať pred adresou kľúčové slovo phys. Tieto príkazy sa dajú použiť na čítanie obsahu RAM, špeciálnych a periférnych registrov a pamätí FLASH, EEPROM alebo ROM.
  • mww, mwh, mwb adresa hodnota – zapíše obsah pamäte ako 32-bitové slovo (mdw), 16-bitové polslovo (mdh) alebo 8-bitový byte (mdb); v prípade použitia MMU platia rovnaké pravidlá ako pri operáciách na čítanie pamäti. Tieto príkazy sa dajú použiť na zápis obsahu RAM, špeciálnych a periférnych registrov; nedajú sa použiť na zápis do FLASH či EEPROM.
  • dump_image súbor adresa – uloží obsah pamäte do binárneho súboru. Tento príkaz sa dá použiť na uloženie obsahu RAM a pamätí FLASH, EEPROM alebo ROM.
  • load_image súbor offset [[typ] adresa dĺžka] – zapíše obsah súboru (alebo jeho relevantných častí) do pamäte na adresy definované v súbore – tieto adresy je možné posunúť o hodnotu offset; typ určuje formát súboru (ak autodetekcia nefunguje): bin, ihex, elf, s19; nahrávané dáta je možné voliteľne ohraničiť adresou (pamäť pod touto adresou sa nezapisuje) a dĺžkou. Tento príkaz sa dá použiť na nahranie obsahu RAM; nedá sa použiť na zápis do FLASH či EEPROM.

Práca s FLASH pamäťou

  • flash banks – vypíše zoznam nakonfigurovaných oblastí FLASH pamäte. Formát výpisu sa pre rôzne ovládače môže líšiť. Napríklad ovládače s automatickou detekciou FLASH parametrov môžu zobraziť nulovú adresu alebo dĺžku FLASH pamäte. Údaje sa môžu aktualizovať príkazom flash probe.
  • flash probe oblasť – otestovanie parametrov oblasti FLASH pamäte. Zistené údaje vypíše, prípadne upraví tabuľku nakonfigurovaných pamäťových oblastí
  • flash info oblasť – vypíše podrobné informácie o oblasti FLASH pamäte, po úroveň blokov
  • flash erase_check oblasť – vypíše stav vymazania oblasti FLASH pamäte po jednotlivých blokoch
  • flash erase_sector oblasť od do – vymaže sektory (špecifikované rozsahom oddo; do môže mať hodnotu last) v špecifikovanej oblasti
  • flash erase_address [pad] adresa dĺžka – vymaže oblasť pamäte v špecifikovanom rozsahu; adresy musia byť zarovnané na hranice sektorov. Ak je zadané kľúčové slovo pad, rozsah je zarovnaný automaticky.
  • flash fillw,fillh,fillb adresa hodnota počet – zapíše príslušný 32-bitových slov (fillw), 16-bitových polslov (fillh) alebo 8-bitových byte (fillb); celý zápis musí byť v rámci jedného sektora. Flash pamäť sa pred zápisom nemaže.
  • flash read_bank oblasť súbor offset veľkosť – zapíše oblasť flash pamäte do súboru; offset je počiatočná adresa v rámci oblasti.
  • flash write_bank oblasť súbor offset – nahrá obsah súboru do oblasti od adresy offset
  • flash verify_bank oblasť súbor offset – porovná obsah súboru s obsahom oblasti od adresy offset
  • flash write_image súbor [offset] [typ] – zapíše obsah súboru (alebo jeho relevantných častí) do FLASH pamäte na adresy definované v súbore – tieto adresy je možné posunúť o hodnotu offset; typ určuje formát súboru (ak autodetekcia nefunguje správne): bin, ihex, elf, s19, mem, builder; FLASH pamäť môžeme pred zápisom vymazať a/alebo odomknúť uvedením kľúčových slov erase a/alebo unlock pred názvom súboru.

Posledným príkazom je kombinovaný príkaz (vlastne je to skript), ktorý zjednodušuje nahrávanie programu do pamäti-

  • program súbor [offset] [voľby] – inicializuje MCU a zapíše obsah súboru (alebo jeho relevantných častí) do FLASH pamäte na adresy definované v súbore – tieto adresy je možné posunúť o hodnotu offset; po ukončení môže vykonať dodatočné akcie, určené parametrom [voľby]:
    • verify – kľúčové slovo, obsah FLASH sa po nahraní verifikuje (vykoná sa flash verify)
    • reset – procesor sa po nahraní obsahu flash resetuje (vykoná sa reset run)
    • exit – OpenOCD sa automaticky ukončí (vykoná sa shutdown)

Riadenie behu MCU

V tejto skupine sú príkazy slúžiace ovládanie behu procesora, vrátane správy breakpointov a watchpointov. Tieto príkazy, prípadne ich chovanie, sa môžu mierne líšiť v závislosti od cieľovej platformy, prípadne niektoré z ich parametrov môžu byť ignorované. Napríklad nie je možné nastaviť hardvérový breakpoint mimo oblasti flash pamäte (napr. pri ladení v RAM).

  • poll – vyčíta stav procesora – či beží alebo je zastavený, prípadne v akom je režime. Príkaz sa môže použiť aj na zapnutie alebo vypnutie periodického vyčítavania stavu (príkazmi poll on alebo poll off)
  • reset – skupina príkazov na resetovanie procesora; dodatočný parameter určuje akciu po resete. Pokiaľ parameter nie je zadaný, procesor sa po resete spustí (ekvivalent reset run)
    • reset halt – procesor po resete ostane zastavený
    • reset init – procesor po resete ostane zastavený a vykoná sa inicializačný skript
    • reset run – procesor sa po resete spustí
  • halt [ms] – zastaví procesor čaká na jeho zastavenie; čakaciu dobu v milisekundách je možné špecifikovať voliteľným parametrom, preddefinovaná hodnota je 5 sekúnd.
  • wait_halt [ms] – čaká na zastavenie procesore; čakaciu dobu v milisekundách je možné špecifikovať voliteľným parametrom, preddefinovaná hodnota je 5 sekúnd.
  • resume [adresa] – spustenie programu; ak je zadaná adresa, tak sa spustí od tejto adresy
  • step [adresa] – vykonanie jednej inštrukcie programu; ak je zadaná adresa, tak sa vykoná inštrukcia na tejto adrese
  • bp [adresa dĺžka [hw]] – pokiaľ sa príkaz zadá bez parametrov, vypíšu sa všetky breakpointy; ak chceme definovať nový breakpoint, musíme zadať adresu a dĺžku – dĺžka závisí od cieľovej architektúry, napríklad pre procesory ARM je dĺžka 2 pre breakpoint v Thumb móde a 4 pre breakpoint v ARM móde. Typ breakpointu – hardvérový či softvérový – je závislý od cieľovej platformy. U niektorých platforiem je možné vynútiť hardvérový breakpoint pomocou posledného parametra (hw), u niektorých sa určuje automaticky. Pri zadefinovaní softvérového breakpointu sa zmení obsah RAM – to je potrebné si uvedomiť, ak budeme skúmať obsah pamäti.
  • rbp adresa – vymazanie breakpointu na uvedenej adrese
  • wp [adresa dĺžka [r|w|a [hodnota [maska]]]] – nastaví watchpoint. tzn. stráženie prístupu k pamäti. Prvé dva parametre sú rovnaké ako u breakpointu. Ďalší parameter určuje typ prístupu – čítanie (r), zápis (w) alebo oboje (a – access). Preddefinované je oboje. Ak je zadaná hodnota a maska, watchpoint sa aktivuje len ak pamäťové miesto obsahuje zadanú hodnotu (pričom maska určuje bity, ktoré sa budú ignorovať). Ak sa príkaz použije bez parametrov, vypíšu sa všetky definované watchpoity
  • rwp [adresa] – vymazanie watchpointu na uvedenej adrese

Príkazy špecifické pre architektúru a cieľový procesor

OpenOCD podporuje špecifické príkazy pre jednotlivé architektúry. Ich dostupnosť môže byť závislá nielen od architektúry ladeného procesora, ale aj od typu použitého ladiaceho adaptéra. Napríklad adaptéry využívajúce HLA (STLink V2, TI-ICD) implementujú virtuálnu architektúru a prekrývajú fyzickú architektúru.

Príkazy tejto skupiny sú nad rámec tohoto prehľadu. Ako príklad si môžeme uviesť dva zaujímavé príkazy pre architektúru ARM:

  • arm disassembly adresa [počet [thumb]] – vypíše disassemblovaný kód od špecifikovanej adresy. Pokiaľ ladený procesor podporuja ARM aj Thumb inštrukčnú sadu, použitím prepínača thumb sa kód interpretuje ako Thumb inštrukčná sada.
  • arm semihosting on/off – povolí alebo zakáže tzv. semihosting. Semihosting je mechanizmus, ktorým dokáže program v MCU prostredníctvom JTAG rozhrania komunikovať s ladiacimi nástrojmi. Tento príkaz povolí OpenOCD prijímať túto komunikáciu.

Všetky príkazy, ktoré sme doteraz uvádzali, pracovali s predvoleným cieľovým procesorom. Obvykle procesor s ktorým pracujeme obsahuje len jedno jadro. Avšak existujú MCU s viacerými jadrami, napríklad LPC4350, ktoré obsahuje dve jadrá – jedno Cortex-M4 a jedno Cortex-M0. V tomto prípade potrebujeme prepínať medzi jadrami. Nasledujúce príkazy nám s tým pomôžu.

  • targets – vypíše zoznam cieľových procesorov
  • targets target – nastaví nový predvolený cieľový procesor

Názvy cieľových procesorov nie sú nijako ujednotené, u LPC4350 nájdeme lpc4350.m4 a lpc4350.m0, u STM32F1 sa stretneme s názvom stm32f1x.cpu.

Pomocné príkazy

  • gdb_sync – umožní synchronizáciu GDB s OpenOCD. Po jeho zadaní sa najbližší príkaz step ignoruje a GDB si dokáže načítať aktuálny stav procesora. Tento príkaz je možné zadať iba cez GDB kanál („monitor gdb_sync“)
  • help – vypíše obsiahly help
  • shutdown – ukončí OpenOCD; s výhodou využijeme v automatizovaných skriptoch

Kam na OpenOCD

Pokiaľ používame Linux, tak je pravdepodobné, že niektorá s verzií OpenOCD je k dispozícii. Pokiaľ táto verzia neexistuje, môže byť problém nájsť aktuálnu verziu v binárnej podobe. Našťastie kompilácia OpenOCD pod Linuxom nie je až tak ťažká.

V prípade Windows existuje niekoľko zdrojov, odkiaľ sa dá OpenOCD získať, vrátane najčerstvejších vývojových verzií. Napríklad:

A pokiaľ sa rozhodneme skompilovať vlastnú verziu, postup je uvedený v článku Kompilácia OpenOCD

Ďalšie čítanie

Najlepším zdrojom informácií je oficiiálna dokumentácia, ktorá je k dispozícii na oficiálnych strankach OpenOCD.

Print Friendly, PDF & Email
577 zobrazení