19.2 Egyszerű UART driver: inicializálás, küldés/vétel megszakítással

19.2.1 Motiváció

A beágyazott rendszerek többségében a soros kommunikáció az egyik legalapvetőbb perifériakezelési feladat. Egy szenzoradatokat fogadó, parancsokat értelmező vagy egyszerűen csak konzolüzeneteket naplózó firmware gyakran UART-on keresztül kommunikál a külvilággal. Ha a vételt pollinggal valósítjuk meg, a CPU aktívan pörög, és nem tud aludni vagy más feladatot végezni. Ezzel szemben az interrupt-vezérelt vétel lehetővé teszi, hogy a CPU mélyalvásban (WFI) vagy más kritikus feladat végrehajtásában legyen, amíg a periféria jelzi, hogy új bájt érkezett. Ez a minta nem csak UART-ra, hanem szinte minden bemeneti perifériára (SPI, I2C, ADC) adaptálható.

Ebben az alfejezetben egy minimális, de teljes UART drivert építünk fel a QEMU mps2-an385 machine CMSDK APB UART perifériájára. A fókusz a vételi irányon van: a bejövő karaktereket megszakításban fogadjuk, és egy körkörös pufferbe (ring buffer) helyezzük. A küldést egyszerű pollingos putc-vel oldjuk meg, hogy a példa ne zsúfolódjon el. Az itt bemutatott regiszterkezelés és ISR-szerződés közvetlenül használható más ARM Cortex-M alapú driver fejlesztésénél is.

19.2.2 Definíció: UART regisztrerszerződés

A UART (Universal Asynchronous Receiver/Transmitter) egy olyan memóriába leképzett periféria, amely párhuzamos adatokat soros formában továbbít, illetve soros adatokat párhuzamosít. A CMSDK APB UART egy egyszerű, ARM által definiált periféria, amelyet a QEMU mps2-an385 modellje is implementál. A regisztertérképe öt 32 bites regiszterből áll, amelyek a periféria báziscíméhez képest fix offszeteken érhetők el. Az alábbi táblázat a feltételezett címezést mutatja; a pontos báziscím és offszetek a QEMU machine-definíciójából ellenőrizendők.

Regiszter Offszet Írás Olvasás
DATA 0x000 Küldendő bájt Vett bájt
STATE 0x004 — Állapotbitek: TXFULL, RXFULL, TXOVERRUN, RXOVERRUN
CTRL 0x008 Vezérlés: TXEN, RXEN, TXIE, RXIE Vezérlés visszaolvasása
INTSTATUS 0x00C Write-1-to-clear: a megfelelő bitre írt 1 törli a jelzőt Aktív megszakítások: TXINT, RXINT
BAUDDIV 0x010 Baud osztó értéke Baud osztó visszaolvasása

A CTRL regiszter bitjei a következők: - bit 0: TXEN — adó engedélyezése - bit 1: RXEN — vevő engedélyezése - bit 2: TXIE — adási megszakítás engedélyezése - bit 3: RXIE — vételi megszakítás engedélyezése

A STATE regiszter bitjei: - bit 0: TXFULL — adó puffer megtelt - bit 1: RXFULL — vevő puffer megtelt (van olvasható adat) - bit 2: TXOVERRUN — adási túlcsordulás - bit 3: RXOVERRUN — vételi túlcsordulás

Az INTSTATUS regiszter bitjei: - bit 0: TXINT — adási megszakítás aktív - bit 1: RXINT — vételi megszakítás aktív

Fontos szerződés: a vételi megszakítás (RXINT) akkor válik aktívvá, ha a RXFULL bit beáll, és a RXIE engedélyezve van. A DATA regiszter olvasása törli a STATE.RXFULL bitet — ez megakadályozza, hogy új RX-megszakítás keletkezzen —, de nem törli az INTSTATUS.RXINT jelzőt, amely a hardverben attól a pillanattól önálló, “latched” (rögzített) állapotként él. A már megállapított RXINT-et külön, write-1-to-clear móddal kell törölni: az INTSTATUS megfelelő bitjébe 1-et kell írni. Az INTSTATUS puszta olvasása a jelzőt nem törli. Ha a DATA kiolvasása után elmarad az INTSTATUS-ba írt törlés, az NVIC a megszakítást továbbra is pendingnek látja, és a vezérlés az ISR visszatérése után — vagy akár azonnal, mielőtt visszatérne — újra belép ugyanabba az ISR-be: megszakítási vihart okozva, amely a CPU-t teljesen lefoglalja. Hasonlóképpen az adási megszakítás (TXINT) akkor aktív, ha a TXFULL bit törölve van (azaz a puffer fogad új bájtot) és a TXIE engedélyezve van. Fontos, hogy ennek a fordítottja NEM igaz: a DATA regiszter írása a puffert feltölti, és ettől a TXFULL jellemzően beáll, nem törlődik — a TXFULL törlését a hardver tényleges adatküldése végzi, amikor a puffer a soros vonalra kiürül, függetlenül attól, hogy a szoftver mikor írt utoljára a DATA-ba. A TXINT jelzőt ugyanúgy az INTSTATUS megfelelő bitjébe írt 1-gyel kell törölni, mint az RXINT-et.

A körkörös puffer (ring buffer) egy olyan adatszerkezet, amely fix méretű tömbben tárolja az elemeket, és két indexszel (head, tail) jelöli a következő írási és olvasási pozíciót. Az egyik indexet a termelő (itt az ISR), a másikat a fogyasztó (itt a fő ciklus) módosítja. A puffer akkor üres, ha head == tail, és akkor tele, ha (head + 1) % méret == tail. A méretet érdemes 2 hatványára választani, hogy a modulo helyett bitmaszkolást használhassunk.

19.2.3 Baud rate konfiguráció

A BAUDDIV regiszter a periféria bemeneti órajelét osztja le a kívánt baud rátára. A pontos összefüggés a CMSDK APB UART specifikációjában található; tipikusan a következő alakú:

BAUDDIV=fUARTCLKbaudrate \text{BAUDDIV} = \frac{f_{\text{UARTCLK}}}{\text{baudrate}}

ahol fUARTCLKf_{\text{UARTCLK}} a UART periféria számára biztosított rendszeróra frekvenciája. A QEMU mps2-an385 esetében ez az érték a machine konfigurációjától függ, és a pontos frekvencia a QEMU forráskódjából vagy az MPS2 AN385 dokumentációból ellenőrizendő. A QEMU CMSDK UART modellje a baud rátát baudrate = UARTCLK / BAUDDIV alakban számítja — nincs -1 eltolás a képletben. A példában 25 MHz-es UARTCLK-t feltételezünk, ami 115200 baud esetén BAUDDIV = 25000000 / 115200 ≈ 217 értéket ad. Mivel a QEMU nem valós idejű emulációt végez, a baud rate pontossága a konzol bemenetére nincs hatással, de a regisztert a periféria inicializálásához be kell állítani.

19.2.4 Kidolgozott példa: UART driver

Munkakönyvtár: ~/arm-labor/cm/a076/ — az alfejezet minden laborfájlja ide kerül, és minden parancs innen futtatandó (a könyvtárrendet a Labor-alapkörnyezet alfejezet írja le).

mkdir -p ~/arm-labor/cm/a076
cd ~/arm-labor/cm/a076

Az alábbi példa egy teljes, fordítható UART driver vázát mutatja. A kód C nyelvű, mert a regiszterelérések és a ring buffer kezelése így olvashatóbb; az assembly szerepe a vektortábla és a startup kód, amelyet a korábbi fejezetekből ismerünk. A futtatáshoz szükséges teljes fájllista: lab-mps2.ld, lab-a076-startup.s (a kanonikus startup, az alábbi bővítéssel), lab-uart.h, lab-uart.c, lab-main.c és az ellenőrző lab-check-vectors.py.

A linker script és a startup a II. rész kanonikus párja (Linker script alapjai, Startup kód), a vektortábla ellenőrzője pedig a Vektortábla alfejezet lab-check-vectors.py-ja. A linker script változatlan; a startup másolatának vektortábláját egyetlen, gépileg alkalmazható lépéssel bővítjük az UART0 RX (IRQ0) bejegyzésével:

cp ~/arm-labor/cm/a065/lab-mps2.ld .
cp ~/arm-labor/cm/a066/lab-startup.s lab-a076-startup.s
cp ~/arm-labor/cm/a064/lab-check-vectors.py .
cat > lab-a076-irq.inc <<'EOF'
    /* 16-tól: külső IRQ-k; UART0 RX = IRQ0 = 16. index */
    .word UART0_Handler           /* 0x40: IRQ0 (UART0 RX)       */
EOF
sed -i '/\.word SysTick_Handler/r lab-a076-irq.inc' lab-a076-startup.s   # a SysTick sor után
# gyenge alias: kezelő nélkül is linkelhető
echo '    lab_default_handler UART0_Handler' >> lab-a076-startup.s
# ellenőrzés: a bővített tábla vége
sed -n '/\.word SysTick_Handler/,/^$/p' lab-a076-startup.s

A sed mintája csak a vektortábla .word SysTick_Handler sorára illeszkedik, a fájl végén lévő lab_default_handler SysTick_Handler aliasra nem. A fel nem használt IRQ-helyek a kanonikus Default_Handler-re mutatnak. Az echo sor a kanonikus lab_default_handler makróval a fájl végére egy gyenge, Default_Handler-re mutató aliast fűz a használt kezelőhöz is. A C-fájl erős definíciója ezt felülírja, de ha egy build a kezelőt nem tartalmazza, a KEEP-pel megtartott vektortábla hivatkozása akkor sem marad feloldatlan. Ezek valódi táblaszavak, nem megjegyzések, mert csak így kerül a használt kezelő a megfelelő indexre (16+IRQn16 + \text{IRQn}).

19.2.4.1 Regiszterdefiníciók és ring buffer

// lab-uart.h
#ifndef LAB_UART_H
#define LAB_UART_H

#include <stdint.h>

#define UART0_BASE 0x40004000UL
#define UART0_IRQn 0  // AN385: UART0 RX = IRQ0, TX = IRQ1 (lásd a072/a079)

typedef struct {
    volatile uint32_t DATA;
    volatile uint32_t STATE;
    volatile uint32_t CTRL;
    volatile uint32_t INTSTATUS;
    volatile uint32_t BAUDDIV;
} CMSDK_UART_TypeDef;

#define UART0 ((CMSDK_UART_TypeDef *) UART0_BASE)

#define UART_CTRL_TXEN  (1U << 0)
#define UART_CTRL_RXEN  (1U << 1)
#define UART_CTRL_TXIE  (1U << 2)
#define UART_CTRL_RXIE  (1U << 3)

#define UART_STATE_TXFULL (1U << 0)
#define UART_STATE_RXFULL (1U << 1)

#define UART_INTSTATUS_TXINT (1U << 0)
#define UART_INTSTATUS_RXINT (1U << 1)

void uart_init(uint32_t uartclk, uint32_t baudrate);
void uart_putc(char c);
int  uart_getc(void);
int  uart_available(void);

#endif
// lab-uart.c
#include "lab-uart.h"

#define RX_BUF_SIZE 64  // 2 hatványa
static volatile char     rx_buf[RX_BUF_SIZE];
static volatile uint32_t rx_head = 0; // csak az ISR írja
static volatile uint32_t rx_tail = 0; // csak a fő ciklus írja

void uart_init(uint32_t uartclk, uint32_t baudrate) {
    UART0->CTRL = 0; // kikapcsolás
    UART0->BAUDDIV = uartclk / baudrate; // baudrate = UARTCLK / BAUDDIV
    UART0->CTRL = UART_CTRL_TXEN | UART_CTRL_RXEN | UART_CTRL_RXIE;
}

void uart_putc(char c) {
    while (UART0->STATE & UART_STATE_TXFULL) {
        // vár, amíg az adó puffer kiürül
    }
    UART0->DATA = (uint32_t)(uint8_t)c;
}

int uart_getc(void) {
    if (rx_head == rx_tail) {
        return -1;
    }
    uint8_t c = (uint8_t)rx_buf[rx_tail];
    rx_tail = (rx_tail + 1) & (RX_BUF_SIZE - 1);
    return (int)c;
}

int uart_available(void) {
    return (rx_head != rx_tail);
}

void UART0_Handler(void) {
    uint32_t status = UART0->INTSTATUS;
    if (status & UART_INTSTATUS_RXINT) {
        // write-1-to-clear: torli az INTSTATUS.RXINT-et
        UART0->INTSTATUS = UART_INTSTATUS_RXINT;
        char c = (char)(UART0->DATA & 0xFF);     // olvasás törli a STATE.RXFULL-t
        uint32_t next = (rx_head + 1) & (RX_BUF_SIZE - 1);
        if (next != rx_tail) {
            rx_buf[rx_head] = c;
            rx_head = next;
        }
        // else: túlcsordulás, a bájt elveszik
    }
    if (status & UART_INTSTATUS_TXINT) {
        UART0->INTSTATUS = UART_INTSTATUS_TXINT; // write-1-to-clear
        // Adási megszakítás kezelése; itt nem használjuk
    }
}

Az alábbi ábra a körpuffer négy jellemző állapotát mutatja — üres, részben telt, körbefordult, tele. A szemléltetéshez 8 szeletet használunk a tényleges 64 helyett, hogy az ábra olvasható maradjon; a head/tail logika (és a telítettség feltétele) azonban azonos a fenti rx_buf-éval.

Az rx_buf körpuffer állapotai: üres, részben telt, körbefordult, tele - Szemléltetés N=8 szelettel; a valódi rx_buf mérete RX_BUF_SIZE = 64.

19.2.4.2 Fő program és NVIC engedélyezés

// lab-main.c
#include "lab-uart.h"

static void nvic_enable_irq(int irqn) {
    volatile uint32_t *nvic_iser = (volatile uint32_t *)0xE000E100;
    nvic_iser[irqn / 32] = (1U << (irqn % 32));
}

int main(void) {
    uart_init(25000000, 115200); // feltételezett UARTCLK
    nvic_enable_irq(UART0_IRQn);

    const char *msg = "UART ready\r\n";
    for (const char *p = msg; *p; ++p) {
        uart_putc(*p);
    }

    while (1) {
        int c = uart_getc();
        if (c >= 0) {
            uart_putc((char)c); // echo
        }
    }
}

19.2.4.3 Build és futtatás QEMU-ban

arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -ffreestanding -nostdlib -O1 -T lab-mps2.ld \
  -o lab-uart.elf lab-a076-startup.s lab-uart.c lab-main.c
python3 lab-check-vectors.py lab-uart.elf 1=Reset_Handler 16=UART0_Handler
qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -nographic -kernel lab-uart.elf

A lab-a076-startup.s vektortáblájában a UART0_Handler az IRQ0 helyén (16. index, 0x40) áll; ezt a lab-check-vectors.py utolsó sora gépileg igazolja. A -nographic kapcsoló a QEMU soros konzolját a standard bemenetre/kimenetre köti. A várt kimenet: a konzolon megjelenik az UART ready üzenet, majd a beírt karakterek visszaíródnak (echo). Mivel a QEMU emuláció nem valós idejű, a beírt karakterek megjelenési sorrendje és késleltetése eltérhet a fizikai hardveren tapasztalttól; ez nem hiba, hanem az emuláció jellemzője.

19.2.5 Tipikus hibák és ellenpróbák

Tünet Hipotézis Adatgyűjtés Javítás
Az első karakter után több nem érkezik A DATA-t nem olvassuk ki, ezért a STATE.RXFULL 1 marad, és a periféria a további bájtokat nem fogadja (overrun) GDB-vel az ISR után nézzük meg a STATE értékét: ha RXFULL 1, ez a hiba Az ISR-ben mindenképpen olvassuk ki a DATA regisztert, még akkor is, ha a bájtot eldobjuk
Az ISR azonnal, folytonosan újra belép (megszakítási vihar) A DATA olvasása csak a STATE.RXFULL-t törli; az INTSTATUS.RXINT jelző „latched”, write-1-to-clear nélkül megmarad GDB-vel az ISR-ből visszatérés után nézzük meg az INTSTATUS értékét: ha RXINT még 1, ez a hiba Írjunk explicit 1-et az INTSTATUS megfelelő bitjébe az ISR-ben, a DATA kiolvasása előtt, ahogy a driver teszi
Gyorsan egymás után érkező bájtoknál a vétel elakad, egy bájt beragad A nyugtázás a DATA olvasása után áll: a két művelet között érkező új bájt RXINT-jét a késői W1C letörli, a bájt RXFULL = 1 mellett, megszakítás nélkül marad a regiszterben STATE és INTSTATUS az elakadáskor: RXFULL = 1, RXINT = 0 A sorrend cseréje: előbb W1C, utána DATA-olvasás
A beírt karakterek egyáltalán nem jelennek meg A vételi megszakítás nincs engedélyezve (RXIE hiányzik), vagy az NVIC-ben nem engedélyeztük az IRQ-t Ellenőrizzük a CTRL és az NVIC ISER regiszterét GDB-vel Állítsuk be a RXIE bitet, és hívjuk meg az NVIC_EnableIRQ-ot
A ring buffer tartalma „elavult”, a fő ciklus nem látja a friss adatot A rx_head vagy rx_tail változó nem volatile, a fordító optimalizálja Kapcsoljuk ki az optimalizálást (-O0), és nézzük meg, hogy megjelenik-e a hiba Tegyük volatile-lá a ring buffer indexeit és a puffert
A beírt karakterek minden második esetben jelennek meg Az ISR-ben a DATA regisztert kétszer olvassuk (pl. a STATE ellenőrzése után is), vagy a rx_head növelése hibás Szúrjunk be egy számlálót az ISR-be, és figyeljük a rx_head értékét Egyszer olvassuk a DATA-t, és a rx_head növelését egyetlen atomi műveletsorként végezzük
A QEMU nem indítja el a firmware-t, vagy hibás címen áll meg A linker script memóriatérképe nem egyezik a QEMU machine memory mapjével Ellenőrizzük a readelf -S lab-uart.elf kimenetét és a QEMU -d kapcsolójával a betöltési címeket Igazítsuk a linker scriptet az MPS2 AN385 flash/RAM tartományához

A sorrend a perifériától függő eseményprotokoll része, nem általános szabály. A CMSDK UART-nál az RXINT a bájt érkezésekor beáll, és csak W1C törli; a DATA olvasása az RXFULL-t törli, és ezzel engedi a következő bájt fogadását. Ha a driver előbb nyugtáz, majd olvas, egy közben érkező bájt újra beállítja az RXINT-et, és új megszakítást kér. Fordított sorrendnél a DATA olvasása után, de a W1C előtt érkező bájt jelzését a W1C letörli: a bájt bent ragad, és mivel RXFULL = 1, további bájt sem érkezik. A helyettesítő eszközlánccal (tools/cm-run.py, sűrűn érkező ABCDEFGH bemenettel) ez kimutatható. A driver sorrendjével mind a nyolc bájt visszaíródott. A felcserélt sorrenddel az A után a vétel elakadt; ritkán érkező bájtoknál a két változat nem különbözött. A timer más eset: ott nincs kiolvasandó adat, a törlésnek csak a visszatérés előtt kell megtörténnie (Megszakítás-vezérelt I/O).

19.2.6 Feladatok

  1. Magyarázó feladat: Ismertesse a CMSDK UART STATE és INTSTATUS regisztereinek bitjeit! Magyarázza el, hogyan törlődik az RX és a TX megszakítás, és miért nem elegendő az INTSTATUS regiszter olvasása a megszakítás törléséhez!

  2. Implementációs feladat: Egészítse ki a ring buffert egy volatile uint32_t rx_overflow számlálóval, amely minden elveszett bájt esetén növekszik! Készítsen egy uart_get_overflow(void) függvényt, amely visszaadja a túlcsordulások számát! Ügyeljen arra, hogy a számláló írása az ISR-ben, olvasása a fő ciklusban történik, és a 32 bites írás/olvasás atomi a Cortex-M3-on.

  3. Hibakeresési/mérési feladat: A programban a beírt karakterek csak minden harmadik esetben jelennek meg. A gyanú szerint az ISR-ben a rx_head növelése és a rx_buf írása között megszakítás érkezik, vagy a rx_tail olvasása nem atomi. Analizálja a kódot, készítsen ellenpróbát, és javítsa a hibát! Mérésként hasonlítsa össze a javított és a hibás változat viselkedését QEMU-ban, és dokumentálja a különbséget!

19.2.7 Megoldások

  1. Megoldás: A STATE regiszter bit 0 a TXFULL, bit 1 az RXFULL. Az INTSTATUS bit 0 a TXINT, bit 1 az RXINT. Az RX megszakítás akkor aktív, ha RXFULL && RXIE. A DATA regiszter olvasása törli a STATE.RXFULL-t, ami megakadályozza, hogy új RX-megszakítás keletkezzen — de az INTSTATUS.RXINT jelző ettől önállóan, “latched” módon megmarad, mert az INTSTATUS puszta olvasása csak visszajelzi az állapotot, nem módosítja azt. A már bekövetkezett RXINT-et (és TXINT-et) ezért külön, write-1-to-clear móddal kell törölni: 1-et kell írni az INTSTATUS megfelelő bitjébe. A TX megszakítás akkor aktív, ha a STATE.TXFULL törölve van (a puffer fogad új bájtot) és TXIE engedélyezett; a TXFULL-t a hardver tényleges adatküldése törli, nem a DATA-ba írás — a DATA írása éppen ellenkezőleg, feltölti a puffert, és ettől a TXFULL jellemzően beáll. A TXINT jelzőt emellett ugyanúgy explicit kell törölni az INTSTATUS-ba írt 1-gyel. Ha ez elmarad, az NVIC a megszakítást pendingnek látja, és az ISR azonnal újra belép — megszakítási vihart okozva.

  2. Megoldás:

// lab-uart.c kiegészítés
static volatile uint32_t rx_overflow = 0;

void UART0_Handler(void) {
    uint32_t status = UART0->INTSTATUS;
    if (status & UART_INTSTATUS_RXINT) {
        UART0->INTSTATUS = UART_INTSTATUS_RXINT; // write-1-to-clear
        char c = (char)(UART0->DATA & 0xFF);
        uint32_t next = (rx_head + 1) & (RX_BUF_SIZE - 1);
        if (next != rx_tail) {
            rx_buf[rx_head] = c;
            rx_head = next;
        } else {
            rx_overflow++;
        }
    }
    // ...
}

uint32_t uart_get_overflow(void) {
    return rx_overflow;
}

A rx_overflow 32 bites, aligned változó; a Cortex-M3 egyetlen LDR/STR utasítással éri el, ezért az írás és olvasás atomi. Nem kell kritikus szakasz.

  1. Megoldás: A hiba oka nagy valószínűséggel az, hogy az ISR-ben a rx_head növelése és a rx_buf[rx_head] írása között egy másik megszakítás is lefut, amely szintén módosítja a rx_head-et, vagy a fő ciklus rx_tail olvasása nem atomi. Mivel a Cortex-M3-on a 32 bites aligned olvasás/írás atomi, a rx_head és rx_tail külön kontextusban történő módosítása önmagában nem okoz versenyhelyzetet. A hiba forrása inkább az lehet, hogy az ISR-ben a DATA regisztert nem egyszer olvassuk, vagy a ring buffer mérete nem 2 hatványa, és a % operátor lassú, illetve a rx_head növelése nem maszkolással történik. Az ellenpróba: adjunk ki egy GPIO-t vagy egy számlálót az ISR elején és végén, és figyeljük, hogy az ISR futása alatt történik-e másik megszakítás. A javítás: a rx_head növelését és a puffer írását egyetlen, megszakításmentes szakaszban végezzük, vagy biztosítsuk, hogy a ring buffer műveletek atomiak legyenek. Mivel az ISR-ben vagyunk, a megszakítások engedélyezése nem szükséges, de ha más, magasabb prioritású megszakítás is módosítja a puffert, akkor kritikus szakasz kell. A mért különbség: a javított változatban a karakterek minden esetben megjelennek, a hibásban csak minden harmadik.

19.2.8 Rövid összefoglaló

Az UART driver inicializálása a CTRL, BAUDDIV és INTSTATUS regiszterek konfigurációjából áll. A vételi megszakítás az ISR-ben két lépésben törlődik: a DATA regiszter olvasása a STATE.RXFULL bitet törli, a INTSTATUS megfelelő bitjébe írt 1 (write-1-to-clear) pedig a tényleges megszakításjelzőt — mindkettő szükséges, különben megszakítási vihar keletkezik. A karaktert ezután egy körkörös pufferbe írjuk. A ring buffer head és tail indexét különböző kontextusban módosítjuk, így elkerülhető a versenyhelyzet. A küldés polling-alapú egyszerűsítéssel történik. A QEMU mps2-an385 machine-en a periféria címei és IRQ számai ellenőrizendők.

Továbbhasznált definíciók: - UART: univerzális aszinkron adó-vevő, memóriába leképzett periféria. - ring buffer: fix méretű körkörös puffer head és tail indexszel. - ISR: hardveresen meghívott megszakításkezelő rutin, amely az eseményt törli és feldolgozza.

19.2.9 Források és ellenőrzési hiányok

Az alábbiak dokumentációs kiindulópontok (forrásállapot: nem megnyitott, lásd az Olvasói útmutató jelöléseit):

A laborkönyvtár összeállítását a helyettesítő eszközlánccal (Clang + ld.lld) ellenőriztük: egy üres könyvtárban, kizárólag a Vektortábla, a Linker script alapjai, a Startup kód és ennek az alfejezetnek a fájljaiból, a fenti cp/cat/sed lépésekkel a labor fordul és linkelődik, és a lab-check-vectors.py a Reset- és a használt IRQ-bejegyzést is a várt szimbólumra mutatónak találja. A tools/cm-run.py UART0-modelljében (--uart-stdin) a program kiírta az UART ready sort, és a bemenetet visszaírta. Ellenőrzési hiányok: A QEMU mps2-an385 machine-en a UART0 báziscíme, az IRQ számok, a BAUDDIV pontos képlete és az UARTCLK frekvenciája ebben a folyamatban nem lett visszaellenőrizve. Ezek a QEMU forráskódjából (hw/char/cmsdk-apb-uart.c, hw/arm/mps2.c) vagy az MPS2 AN385 dokumentációból ellenőrizendők. A cikkben szereplő 0x40004000 és 0 IRQ szám feltételezett értékek; a hallgatónak a labor során a tényleges QEMU-verzióhoz kell igazítania.