Hardveres nyomkövetés és Semihosting

Az alábbiakban röviden ismertetem a hardveres nyomkövetés és a semihosting mechanizmussal bővített nyomkövetés menetét.

A hardveres nyomkövetés

A NUC140 mikrovezérlő a Cortex-M0 típuscsalád többi tagjához hasonlóan Serial Wire Debugger (soros vonali nyomkövető) áramkörrel rendelkezik, amely 4 töréspontot és 2 megfigyelési pontot képes használni. A Serial Wire Debugger ugyanolyan képességekkel rendelkezik, mint a JTAG nyomkövetők, csupán kevesebb vezetéket használ. A hardveres nyomkövető áramkör a programok letöltése és kiolvasása mellett arra szolgál, hogy a programfejlesztés során ellenőrizni tudjuk a program működését, valamint a regiszterek és a memória állapotát.

A NuTiny-SDK-NUC140 fejlesztői modul esetében a céláramkör SWD (Serial Wire Debugger) portjához a leválasztható kártyára épített NuLink-Me nyomkövető készülék csatlakozik, amelyet az összes jelentős fejlesztői szoftver (Keil, IAR, CooCox CoIDE) támogatja.

 A NuTiny-SDK-NUC140 fejlesztői kártya részei: a céláramkör és a NuLink-Me hardveres nyomkövető

Megjegyzés:
A leválasztható kártyán elhelyezett NuLink-Me más Nuvoton NUC1xx céláramkörhöz is felhasználható (pl. saját építésű áramkörök). Szükség esetén a NUC140VE3CN céláramkörhöz is csatlakoztathatunk más hardveres nyomkövető készüléket (pl. Segger J-Link, Keil Ulink2, Coocox CoLinkEx).

1. Ha a nyomkövetőt a Configuration menüben már beállítottuk az első projekt létrehozásánál leírtak, illetve az alábbi ábra szerint, akkor a hardveres nyomkövetéshez minden készen áll, nincs további teendő.   
 
 

2. A projekt lefordítása után kattintsunk a kicsi zöld bogárra (Start Debug), indítsuk el a nyomkövetést. (Az alábbi ábrákon az előző oldalon ismertetett 02_Blinky projektet futtatjuk debug módban.)




3. Alapértelmezetten a program elindul és a main() függvény belépésénél áll meg, ahol az integrált fejlesztői környezeteknél megszokott módon a programot töréspontig futtathatjuk, vagy utasításonként léptethetjük. A View menüben állíthatjuk be, hogy mely ablakok legyenek megnyitva (disassembly, memory, registers, semihosting stb.).




Mi a semihosting?

A Semihosting egy olyan mechanizmus, ami lehetővé teszi, hogy az ARM céláramkörön futó alkalmazói program ki- és beviteli kérelmei a  hardveres nyomkövetővel kapcsolatban álló hoszt számítógépen érvényesüljenek. Ez a mechanizmus lehetővé teszi többek között azt is, hogy  a céláramkörön futó programban a C könyvtári függvények (mint például a printf() és scanf() függvények) a hoszt számítógép billentyűzetét és képernyőjét használják.  Ez különösen hasznos olyan esetekben, amikor a korlátozott kiépítésű hardver nem rendelkezik a kommunikációhoz szükséges perifériákkal. A semihosting mechanizmus viszont lehetővé teszi, hogy a hoszt perifériáit használja.

A hosting befogadást, vendéglátást jelent. A semihosting, ahogy a név is jelzi, csak "félmegoldás", azaz olyasmi, mint a félpanziós ellátás.  A céláramkör használhatja a hoszt számítógép perifériáit, de ez a használat meglehetősen korlátozott és csak a hardveres nyomkövetés idejére korlátozódik.

A semihosting mechanizmus a Szoftveres Interrupt (SWI) definiált műveletei formájában van megvalósítva: az alkalmazás kiad egy SWI megszakítást, a nyomkövető eszköz pedig lekezeli a megszakítást. A nyomkövető eszköz biztosítja a kommunikációt a hoszt számítógéppel.



A semihosting  működésének vázlata
(a kép forrása: infocenter.arm.com)

A legtöbb esetben az SWI megszakításokat közvetett módon, könyvtári függvények keltik. Arra is van azonban lehetőség, hogy a az alkalmazás közvetlenül keltsen SWI megszakítást. 

Az ARM Cortex-M0 mikrovezérlő gyártók közül jelenleg csak a Nuvoton és az NXP támogatja a semihosting mechanizmust.

Egyszerű példa a semihosting használatára

A 03_Semihosting projekt egy egyszerű mintapélda a semihosting használatára. Ehhez az előzőekben bemutatott 02_Blinky projektet egészítettük ki a semihosting használatához szükséges sorokkal. A semihosting mechanizmust használó projektünk létrehozásakor és konfigurálásakor az alábbi műveletekkel bővült az előző oldalakon ismertetett projekt létrehozási tevékenység sor:

1. A Repository ablak Peripherals fülére kattintva jelöljük ki (pipa) a COMMON szekcióból a semihosting komponenst! Ez valószínűleg magával vonja (ha nem, akkor kézzel jelöljük be!) a Retarget printf és a C Library komponenseket is. (Fentiek mellett ne felejtsük el bejelölni a GPIO komponenst is!)




2. Egészítsük ki a programot nyomkövetést segítő kiírásokkal (SH_Sendstring(), vagy printf() függvényekkel), valamint a főprogramba csatoljuk be a semihosting.h és stdio.h fejléc állományokat! Az utóbbira csak akkor van szükség, ha a printf() vagy scanf() könyvtári függvényeket használjuk.

Az alábbi listán látható a main.c állomány listája. A jobb áttekinthetőség érdekében az újonnan betoldott programsorokat három csillaggal (***) megjelöltük.

1. lista: A 03_Semihosting projekt main.c állományának listája
// 03_Semihosting:
// Semihosting example. This is the old 02_Blinky program
// extended by semihosted output messaging.
//
#include "NUC1xx.h"
#include "Driver\DrvGPIO.h"
#include "Driver\DrvSYS.h"
#include "semihosting.h" //*** SH_xxx function declarations
#include "stdio.h" //*** Needed for printf (optional)

int main (void)
{
UNLOCKREG(); // unlock register for programming
DrvSYS_Open(48000000); // set System Clock to run at 48MHz
LOCKREG(); // lock register from programming
SH_SendString("Start up the system\n"); //*** Debug message through semihosting
DrvGPIO_Open(E_GPA, 10, E_IO_OUTPUT); // GPA10 pin set to output mode
DrvGPIO_SetBit(E_GPA, 10); // GPA10 pin output Hi to turn off LED

while (1) // forever loop to keep flashing LED
{
DrvGPIO_ClrBit(E_GPA, 10); // output Low to turn on LED
printf("LED is on..."); //*** Debug message
DrvSYS_Delay(250000); // 250 ms delay
DrvGPIO_SetBit(E_GPA, 10); // output Hi to turn off LED
printf(" ...LED is off\r\n"); //*** Debug message
DrvSYS_Delay(250000); // 250 ms delay
}
}

A főprogramba beiktatott új programsorok szerepe:

3. A projektbe automatikusan felvett printf.c állományba is csatoljuk be a semihosting.h fejléc állományt, emellett definiáljuk felül a PrintChar() függvényt így:
void PrintChar(char c)
{
SH_SendChar(c); //semihosting is used!!!
}
Végeredményben a printf.c állomány eleje úgy nézzen ki, ahogy az az alábbi ábrán látható!


4. Kattintsunk a Configuration gombra (vagy a View menüben nyissuk meg a Configuration ablakot), s a Link lapon a Library feliratnál a legördülő listából a Semihosting opciót válasszuk ki!

 
5. A Configuration ablakban maradva válasszuk ki a Debugger lapot, s itt tegyünk pipát a Semihosting enable felirat elé! Nem szükséges, de a kommunikáció gyorsítása érdekében megnövelhetjük az átvitel alapértelmezett órajelét is (NuLink-Me esetén 2 MHz a maximális frekvencia).


6. Fordítsuk le (vagy újra) a programot, majd a kis zöld bogárkára kattintva indítsuk el a programletöltést és a hardveres nyomkövetést! A nyomkövetés elindítása után a CoIDE jobb alsó sarkában megjelenik egy Semihosting nevű ablak, amelyben a program futása során - kissé darabosan ugyan - de sorra megjelennek a kiírások. Akkor történik kiírás, amikor a néhány karakteres buffer megtelik...