Produkty Novinky Články Návody Kontakty

Lipicano, CMSIS-DAP a pyOCD

Vyzkoušeli jsme, jak funguje Lipicano Board s debuggerem pyOCD z projektu ARM mbed. PyOCD je open source knihovna pro Python 2.7, která umožňuje ladit programy a programovat mikrokontrontoléry ARM Cortex-M přes rozhraní CMSIS-DAP. Knihovnu lze používat na operačních systémech Linux, OSX a Windows. My jsme zkoušeli práci s pyOCD na Windows 8.1.

K čemu je CMSIS-DAP

Knihovna pyOCD je balík programů, které přes USB port komunikují protokolem CMSIS-DAP s ladicím agentem (mikrokontrolérem) a ten přes JTAG nebo Serial Wire Debug (SWD) port s Coresight Debug Access Portem (DAP) procesoru s laděným programem. Specifikaci protokolu CMSIS-DAP a vzorovou implementaci pro procesor Cortex-M lze po registraci získat na webu ARM.
Na Lipicanu je mikrokontrolér NXP LPC11U67, který slouží jako ladicí agent, má firmware, který implementuje rozhraní CMSIS-DAP a je propojen s JTAG/SWD portem procesorového modulu. Na stranu k PC komunikuje přes USB port a prezentuje se jako HID zařízení. Firmware Lipicana v současnosti komunikuje s laděným mikrokontrolérem jen přes SWD port, obsluha JTAG rozhraní není implementována. Modré jumpery na obrázku spojují ladící mikrokontrolér se SWD portem (SWDIO-TMS, SWDCLK-TCK) mikrokontroléru na modulu.

Python, pyWinUSB a pyOCD

Abychom mohli spouštět programy z knihovny pyOCD, nainstalovali jsme na Windows Python 2.7. Následně jsme nainstalovat knihovnu pyWinUSB, která v Pythonu zpřístupní Windows API pro komunikaci s USB/HID periferiemi. Je to jedna z USB knihoven, se kterými umí pyOCD pracovat. PyWinUSB jsme nainstalovali programem pip, který je součástí instalace Pythonu.
pip install pywinusb
Nakonec jsme si nainstalovali knihovnu pyOCD. Knihovna je stále ve vývoji, průběžně přibývají její nové aktualizace a vlastnosti tříd se postupně rozšiřují a mění. Knihovna pyOCD je zaměřená především na ARM mbed moduly, jejichž firmware umí identifikovat. Slouží k tomu třída MbedBoard(), která je potomkem třídy Board(). Lipicano Board ale má obecný CMSIS-DAP firmware a umožňuje použít moduly s různými mikrokontroléry. Proto jsme napsali vlastní třídu LipicanoBoard(), která je také potomkem třídy Board(). Při psaní skriptů jsme pracovali s pyOCD ve verzi 0.5.1. Tuto konkrétní verzi pyOCD nainstalujete příkazem:
pip install pyocd==0.5.1

Lipicano pyTools

Sadu skriptů Lipicano pyTools si stáhnete jako zip soubor nebo si je naklonujete pomocí Gitu příkazem:
git clone https://github.com/ucsimply/lipicano_pyTools.git
Ve staženém balíku jsou dva podadresáře. V podadresáři tests jsou skripty a další soubory, které probereme v následujícím textu. V podadresáři gdb_server je upravený pyOCD GDB server, který namísto třídy MbedBoard() používá třídu LipicanoBoard() a umožňuje ladit programy v modulech zasunutých v socketu Lipicano Board.

Jaká USB/HID zařízení máme

První skript, který jsme si napsali, je velice jednoduchý. Jmenuje se test_hids.py, používá pouze knihovnu pyWinUSB a vypíše seznam USB/HID zařízení, která máme k PC připojená. V okně s příkazovým řádkem v podadresáři tests se skript spustí příkazem:
python test_hids.py
Skript vypsal VID a PID připojených USB/HID zařízení a také jméno výrobce, název produktu a sériové číslo. Mezi řadou zařízení, které nejspíš představují USB klávesnici a myš, je vidět Lipicano Board s VID: 0xA600 a PID: 0xE2C0. To je dobrá zpráva, Windows registrují Lipicano Board jako USB/HID zařízení a my můžeme pokračovat dál.
VID: 0x0566, PID: 0x3002, Vendor: Unknown manufacturer, Product: @input.inf,%hid ...
VID: 0x0566, PID: 0x3002, Vendor: Unknown manufacturer, Product: @input.inf,%hid ...
VID: 0x0566, PID: 0x3002, Vendor: Unknown manufacturer, Product: @input.inf,%hid ...
VID: 0xA600, PID: 0xE2C0, Vendor: uCSimply, Product: CMSIS-DAP, SerNo: A001
VID: 0x0001, PID: 0x0001, Vendor: Unknown manufacturer, Product: @input.inf,%hid ...

Jaký typ mikrokontroléru je na modulu

Druhý skript test_dap.py vznikal postupně. Tento skript nepracuje přímo s pyWinUSB, ale používá knihovnu pyOCD, která prostřednictvím CMSIS-DAP ladicího agenta (firmware v LPC11U67 na Lipicanu) komunikuje s Coresight Debug Access Portem v mikrokontroléru na modulu v socketu Lipicana. Skript se spustí v podadresáři tests příkazem:
python test_dap.py
Nejdřív jsme zkusili zjistit základní informace o mikrokontroléru v zasunutém modulu. To je celkem jednoduchá věc, pomocí VID (0xA600) a PID (0xE2C0) získáme připojení na Lipicano Board, vytvoříme instanci třídy Board(), spustíme její metodu board.init(), která mimo jiné naplní obsah objektu target a voláním funkce print_target_info() některé proměnné objektu target vypíšeme:
def print_target_info(target):
    print("IDCODE  : 0x%08X" % (target.idcode))
    print("Arch    : %s" % (ARCH_NAME[target.arch]))
    print("Core    : %s" % (CORE_TYPE_NAME[target.core_type]))
    print("FPU     : %s" % (MAP_BOOL_Y_N[target.has_fpu]))
    print("Hw-BPt  : %s" % (len(target.hw_breakpoints)))
    print("Hw-WPt  : %s" % (len(target.watchpoints)))
    print("State   : %s" % (TARGET_STATE[target.getState()]))

if __name__ == '__main__':
    lipicano = get_lipicano()
    board = Board(target = "cortex_m", 
                  flash = "cortex_m", 
                  interface = lipicano, 
                  transport = "cmsis_dap", 
                  frequency = FREQ)
    board.init()
    target = board.target
    print_target_info(target)
Pro modul s mikrokontrolérem LPC1115 jsme dostali výpis:
IDCODE  : 0x0BB11477
Arch    : ARMv6M
Core    : Cortex-M0
FPU     : No
Hw-BPt  : 4
Hw-WPt  : 2
State   : Halted
V socketu Lipicana jsme vyměnili modul s LPC1115 za modul s LPC11U37 a dostali jsme výpis:
IDCODE  : 0x0BB11477
Arch    : ARMv6M
Core    : Cortex-M0
FPU     : No
Hw-BPt  : 4
Hw-WPt  : 2
State   : Halted
Hezký výsledek, ale pro oba mikrokontroléry je stejný. Pro mikrokontroléry LPC1317 a LPC1347, které mají ARM Cortex-M3 procesor, jsme dostali taky identické hodnoty:
IDCODE  : 0x2BA01477
Arch    : ARMv7M
Core    : Cortex-M3
FPU     : No
Hw-BPt  : 6
Hw-WPt  : 4
State   : Halted
Čekali jsme víc, chtěli jsme získat přesnou identifikaci typu mikrokontroléru, který je na modulu. IDCODE ale identifikuje typ procesoru, tímhle způsobem typ mikrokontroléru nezískáme.
Pátrání zjednoduší to, že hledáme, jak zjistit typ mikrokotroléru pro několik exemplářů z řady NXP LPC1xxx. Při nahlédnutí do manuálu k LPC111x vše vypadá jasné a prosté. Na adrese 0x400483F4 je registr Device ID, který obsahuje ID typu mikrokontroléru. No a přečíst pomocí pyOCD nějaká data z paměti není žádný problém. Počáteční radost ale zkazila poznámka v manuálu: This register returns the part ID for parts of the LPC1100, LPC1100C, and LPC1100L series only. Use ISP/IAP to obtain the part ID for the LPC1100XL series. A seznam ID končí mikrokontrolérem LPC1114, o LPC1115 ani zmínka. Po prohlédnutí manuálů k LPC11Uxx a LPC1300 a pátrání po diskuzích na LPCware.com je jasné, že ISP/IAP je jedinou spolehlivou cestou, jak získat ID mikrokontroléru.
Fajn, ale jak to máme udělat pomocí pyOCD a CMSIS-DAP? Musíme napsat jednoduchý program, který zavolá IAP funkci se správnými parametry. Tento program přeložíme, binárku zapíšeme pomocí pyOCD do SRAM paměti mikrokontroléru, spustíme a ze SRAM přečteme získaná data. A jak řekli, tak udělali.
In-Application Programming (IAP) rozhraní mikrokontrolérů LPC1xxx poskytuje především funkce pro programování interní Flash paměti. Nás však zajímá funkce Read Part ID, která vrací ID kód mikrokontroléru. Navíc jsme zkusili získat další data voláním funkcí Read Boot code version, která vrací verzi Bootloaderu a Read UID, která vrací unikátní sériové číslo použitého exempláře mikrokontroléru.
Adresa vstupního bodu IAP je 0x1FFF1FF0 a parametry se předávají přes registry R0 a R1 odkazem na struktury v paměti.

V assembleru jsme napsali jednoduchý program getids.s, který postupně zavolá všechny tři uvedené IAP funkce a získaná data uloží na smluvené místo v paměti SRAM. Na kompilaci programu a převedení výsledné binárky do hexadecimálního tvaru jsme si napsali dávku compile.bat, kterou jsme spustili z příkazového řádku v podadresáři tests\asm_snippets.
Hexa kód jsme zapsali do pole GETIDS_CODE. O provedení kódu, přečtení výsledků z paměti SRAM a jejich výpis se stará funkce print_target_ids(). A jestli to funguje? Tady jsou výpisy pro všechny moduly:
IDCODE  : 0x0BB11477
Arch    : ARMv6M
Core    : Cortex-M0
FPU     : No
Hw-BPt  : 4
Hw-WPt  : 2
State   : Halted
PartID  : 0x00050080
BootVer : 7.2
PartSN  : 0xF5001946-541E6049-AE4A8C03-05043039
Name    : LPC1115

IDCODE  : 0x0BB11477
Arch    : ARMv6M
Core    : Cortex-M0
FPU     : No
Hw-BPt  : 4
Hw-WPt  : 2
State   : Halted
PartID  : 0x00017C40
BootVer : 7.2
PartSN  : 0xF5001941-53722405-AE3B9406-0C02B003
Name    : LPC11U37

IDCODE  : 0x2BA01477
Arch    : ARMv7M
Core    : Cortex-M3
FPU     : No
Hw-BPt  : 6
Hw-WPt  : 4
State   : Halted
PartID  : 0x1A020525
BootVer : 5.2
PartSN  : 0xF5000003-5036632D-53590C3A-09093931
Name    : LPC1317

IDCODE  : 0x2BA01477
Arch    : ARMv7M
Core    : Cortex-M3
FPU     : No
Hw-BPt  : 6
Hw-WPt  : 4
State   : Halted
PartID  : 0x08020543
BootVer : 5.2
PartSN  : 0xF5000006-53C86F6D-53340000-1616232B
Name    : LPC1347

IDCODE  : 0x2BA01477
Arch    : ARMv7M
Core    : Cortex-M4
FPU     : Yes
Hw-BPt  : 6
Hw-WPt  : 4
State   : Halted
PartID  : 0x481D3F47
BootVer : 8.4
PartSN  : 0xF5000006-50F85A03-53300000-09090D19
Name    : LPC4088
Výsledky pokusů jsme použili při úpravách GDB serveru. Ten používá třídu LipicanoBoard() a pozná, který z modulů je zasunutý v Lipicanu.