Egy C-ben megírt adatfeldolgozó függvényt Linux alatt szeretnél gyorsítani. Egy másik feladatban egy szenzor jelére kell kiszámítható időn belül reagálnod, operációs rendszer nélkül. Mindkettőhöz választhatsz ARM-alapú processzort, mégsem ugyanazt az assembly kódot, fordítóprogram-célt vagy indítási eljárást fogod használni.
Az „ARM” egy architektúracsalád megnevezése, nem egyetlen gépi nyelvé. A megfelelő cél kiválasztásához külön kell azonosítani:
Az előző alfejezet ISA és mikroarchitektúra közötti különbségére építve most ezeket helyezzük el. Az utasítások részletes működése helyett a cél annak felismerése, hogy melyik program milyen gépen és milyen környezetben értelmezhető.
Az architektúraprofil (architecture profile) az ARM architektúracsaládon belül egy alkalmazási területhez igazított követelmény- és szolgáltatáskészlet. Nem pusztán teljesítménykategória: meghatározza például a memóriakezelés és a kivételkezelés alapmodelljét.
| Profil | Jellemző feladat | Jellemző memóriakezelés | Jellemző szoftverkörnyezet |
|---|---|---|---|
| A-profile, alkalmazási profil | Általános alkalmazások, szerverek, összetett felhasználói rendszerek | Virtuális memória, MMU | Linux és más összetett operációs rendszerek |
| R-profile, valós idejű profil | Határidős vezérlés, tárolóvezérlők, ipari és biztonságkritikus rendszerek | Architektúraverziótól függő; gyakran MPU, egyes újabb változatokban MMU is | Valós idejű operációs rendszer vagy célzott firmware |
| M-profile, mikrokontroller-profil | Beágyazott vezérlés, szenzorok, kis erőforrású perifériakezelés | A könyv Cortex-M3 célján nincs MMU; MPU alkalmazható, ha megvalósított | Operációs rendszer nélküli firmware vagy RTOS |
A memóriakezelő egység (Memory Management Unit, MMU) címfordítást és hozzáférés-szabályozást tesz lehetővé. Linux alatt a folyamatok jellemzően saját virtuális címtérben működnek: a C-mutató értéke nem közvetlenül a fizikai memória címét jelenti.
A memóriavédelmi egység (Memory Protection Unit, MPU) ezzel szemben memóriatartományok hozzáférési tulajdonságait szabályozza, de nem biztosít MMU-szerű virtuális–fizikai címfordítást. Az MPU meglétét mindig a konkrét processzor megvalósításán kell ellenőrizni.
Az R-profile nevében a real-time nem azt jelenti, hogy „nagyon gyors”. A valós idejű rendszerben a helyes eredménynek meghatározott határidőn belül kell rendelkezésre állnia. Ehhez nem elegendő a megfelelő processzorprofil: a memória, az ütemezés, a megszakítások és a szoftver legrosszabb eseti viselkedése is számít.
A táblázat tehát tipikus felhasználást, nem kizárólagosságot mutat. A-profile rendszeren is lehet valós idejű feladatot végezni, és M-profile processzoron is futhat operációs rendszer. A könyv az R-profile részletes programozását nem tárgyalja.
Az elnevezések különböző kérdésekre válaszolnak:
| Megnevezés | Mit azonosít? | Könyvbeli szerep |
|---|---|---|
| Armv8-A | Architektúraverzió és A-profile | Az I. rész architekturális alapprofilja |
| AArch64 | 64 bites végrehajtási állapot (execution state) | Az I. rész programjainak végrehajtási állapota |
| A64 | Az AArch64 állapot utasításkészlete | Az I. rész assembly nyelve |
| Armv7-M | Architektúraverzió és M-profile | A II. rész architekturális alapja |
| Cortex-M3 | Konkrét processzormag-típus | A II. rész referenciaprocesszora |
| Thumb-2 | 16 és 32 bites Thumb-utasításkódolásokat tartalmazó technológia | A II. rész Armv7-M utasításainak kódolási környezete |
A Cortex-M3 tehát nem „az ARM harmadik verziója”: a név egy processzormag-típust jelöl, amely az Armv7-M architektúrát valósítja meg. Az Armv7-M és az Armv8-A verziószámából önmagában nem következik teljesítményarány; eltérő profilokról beszélünk.
A64 ISA, AArch64 környezetben az általános célú
regiszterek 64 bites nézete Xn, 32 bites nézete
Wn. Ettől függetlenül minden A64 utasítás kódolása 32
bites. A regiszterszélesség és az utasításhossz tehát külön fogalom.
Thumb-2/Armv7-M környezetben a Cortex-M3 általános
célú regiszterei 32 bitesek, jelölésük Rn. Az
utasításkódolások lehetnek 16 vagy 32 bitesek. Egy 32 bites
Thumb-utasítás ettől nem válik A64 utasítássá, és egy 16 bites kódolás
sem jelent szükségképpen 16 bites adatfeldolgozást.
A Cortex-M3 nem hajt végre A64 vagy A32 utasításokat. Az A-profile családban előforduló AArch32 állapot és annak A32/Thumb utasításai nem képezik a könyv I. részének célját.
Fontos további korlát: az AArch64 nem minden környezetben az A-profile szinonimája; az R-profile családnak is vannak AArch64 változatai. Ebben a könyvben az AArch64 cél mindig a kijelölt A-profile/Linux környezetet jelenti.
A felhasználói tér (userspace) az operációs rendszer által korlátozott alkalmazásfuttatási környezet. Az I. részben a program betöltését, a folyamat kezdeti környezetét és a rendszererőforrások kezelését Linux biztosítja.
Ez pedagógiai előny: először a regiszterekkel, aritmetikával, memóriacímzéssel és függvényhívásokkal foglalkozhatunk. Nem kell rögtön a rendszerindítást vagy a soros port vezérlését is megírni. A kimenethez C-könyvtári függvényt vagy később rendszerhívást használhatunk.
Ez azonban nem közvetlen hardverhozzáférés. Egy Linux-alkalmazás nem írhat tetszőlegesen perifériaregisztereket, és nem módosíthatja szabadon a processzor privilegizált állapotát.
A labor elsődleges futtatója a qemu-aarch64,
felhasználói módú emulációban (user-mode
emulation). Ez AArch64 Linux-programokat futtat, és közvetíti a
rendszerhívásaikat a gazdarendszer felé. Nem indít külön AArch64
Linux-kernelt, és nem emulál egy teljes Raspberry Pi alaplapot.
Az Ubuntu x86_64 hoston futó keresztfordító (cross-compiler) ettől még AArch64 gépi kódot állít elő. A fordító futtatási gépe és a létrehozott program célgépe két külön dolog.
A II. részben a közvetlen hardveren futó, operációs rendszer nélküli környezetet (bare-metal) tanulmányozzuk. Itt nincs Linux, amely előkészítené a C-program futását vagy kiszolgálná a rendszerhívásait.
A firmware feladata lesz többek között:
Ezeket nem feltételezzük ismertnek: a minimal runtime fejezete építi fel őket.
A cél a Cortex-M3 referenciaprofil, QEMU-ban az
mps2-an385 gépmodell. A futtató eszköz itt
qemu-system-arm, rendszerszintű
emulációban (system emulation): a processzor mellett a
kiválasztott gép memóriáját és modellezett perifériáit is
biztosítja.
Az M-profile önmagában nem követeli meg a bare-metal működést; ez a könyv tudatos választása. Egy valós idejű operációs rendszer használata előtt így megérthető az a mechanizmus, amelyre az később épül.
Mindkét részben tovább használjuk a bináris reprezentációt, a regiszterek fogalmát, a memóriahozzáféréseket, az elágazásokat és a gépi kód vizsgálatát. Nem vihetők át változtatás nélkül a regiszternevek, az utasításkódolások, a hívási konvenciók vagy a programindítás szabályai.
Az I. rész a skaláris műveletektől a C–assembly együttműködésen át a NEON/SIMD feldolgozásig jut. A záróprojekt I egy C-programba illeszthető, teljesítménykritikus AArch64-modul: helyességét tesztekkel, hasznát mérési tervvel, működését audittal kell igazolni.
A II. részben az indulástól a megszakításokon át a perifériavezérlésig haladunk. A záróprojekt II megszakításvezérelt firmware-modul lesz, reprodukálható QEMU-teszttel és ellenőrizhető állapotkezeléssel.
A Cortex-M3 célon nincs NEON. A közösen továbbvihető tudás ezért elsősorban a gondolkodásmód és az ellenőrzési módszer, nem a konkrét utasítássor.
Munkakönyvtár: ~/arm-labor/a64/a004/ —
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/a64/a004
cd ~/arm-labor/a64/a004Szerepe a fejezetben: ez a példa célzott kiegészítés: egyetlen jelenséget mutat meg külön builddel, ezért első olvasáskor kihagyható. A fejezet végigvitt projektjének ide tartozó lépése — amely nem hagyható ki — a szakasz végén, a „Végigvitt projekt” részben található.
Feladat: készítsünk olyan Linux-programot, amely C-ből meghív egy A64 assembly függvényt, majd ellenőrizze a célját és eredményét.
Profil: I. rész, A64 ISA, Armv8-A, AArch64 Linux,
little-endian, LP64 adatmodell. Utóbbiban a mutató 64 bites, az
unsigned int 32 bites; ezt nem általában az „ARM” névből
következtetjük.
Az eszközök telepítését a labor-alapkörnyezet részletezi. Itt már elérhetőnek tekintjük az AArch64 keresztfordítót, a célrendszer C-könyvtári fejlesztői állományait és a QEMU user-mode futtatót.
A lab-profile.c fájl:
/* I. rész: AArch64 Linux, LP64, little-endian laborprofil. */
#include <stdio.h>
#if !defined(__aarch64__) || !defined(__linux__)
#error "Ehhez a laborhoz AArch64 Linux celu fordito kell."
#endif
_Static_assert(sizeof(void *) == 8, "64 bites mutato szukseges");
_Static_assert(sizeof(unsigned int) == 4, "32 bites unsigned szukseges");
extern unsigned int lab_add(unsigned int a, unsigned int b);
int main(void)
{
unsigned int result = lab_add(19u, 23u);
printf("mutato=%zu; unsigned=%zu; osszeg=%u\n",
sizeof(void *), sizeof(unsigned int), result);
return result == 42u ? 0 : 1;
}A lab-profile.S fájl:
// I. rész: A64 ISA, AArch64 Linux; GNU assembler-direktívák.
.text
.p2align 2
.global lab_add
.type lab_add, %function
lab_add:
add w0, w0, w1
ret
.size lab_add, .-lab_add
.section .note.GNU-stack,"",%progbitsAz A64 ADD itt regiszteroperandusokat használ, nincs
kódolandó immediate konstansa. Az A64 W0 és W1
tartalmazza a két 32 bites, előjel nélküli argumentumot; az eredmény az
A64 W0 regiszterben tér vissza. Ez az AArch64 hívási
konvenció egyik egyszerű esete, amelyet később az AAPCS64 regiszterszerződése részletez. Az A64
RET a hívóhoz tér vissza.
A ponttal kezdődő sorok GNU assemblerdirektívák, nem
processzorutasítások. Az itt bemutatott jelentésük az AArch64 ELF-célhoz
tartozik; például a .p2align 2
bájtos igazítást kér.
Build és futtatás — target: qemu-user, AArch64 Linux:
aarch64-linux-gnu-gcc -std=c11 -O0 -Wall -Wextra \
-march=armv8-a -mlittle-endian \
-c lab-profile.c -o lab-profile-c.o
aarch64-linux-gnu-gcc -march=armv8-a -mlittle-endian \
-c lab-profile.S -o lab-profile-asm.o
aarch64-linux-gnu-gcc -march=armv8-a -mlittle-endian -static \
lab-profile-c.o lab-profile-asm.o -o lab-profile-a64
qemu-aarch64 ./lab-profile-a64
printf 'kilepes=%d\n' "$?"Az első lépés a C-forrást fordítja, a második az assembly forrást szereli össze. A harmadik linkeli a programot a szükséges C-futtatókörnyezettel. A statikus linkelés ennél a példánál elkerüli a külön AArch64 dinamikus betöltő megadását; a program ettől továbbra is Linux-szolgáltatásokat igényel.
Várt, nem ténylegesen mért eredmény:
| Ellenőrzés | Várt érték |
|---|---|
| A program kiírása | mutato=8; unsigned=4; osszeg=42 |
| A shell kiírása | kilepes=0 |
Visszatérési érték az A64 RET előtt |
A64 W0 = 42, előjel nélküli
értelmezésben |
A bináris célját külön is ellenőrizzük.
Binárisvizsgálat — target: ugyanaz az AArch64 Linux-program:
aarch64-linux-gnu-readelf -h lab-profile-a64
aarch64-linux-gnu-objdump -d --disassemble=lab_add lab-profile-a64Az ELF-fejlécben ELF64, little-endian adatábrázolás és
AArch64 géptípus várható. A függvény visszafejtett kódjában
az A64 ADD és RET utasításnak kell
megjelennie.
Ez az ellenőrzés az ISA-t és a bináris formátumot azonosítja, nem egy konkrét processzormagot. A sikeres futásból sem a fizikai gép típusára, sem hardveres ciklusszámra nem következtethetünk.
lab-hidden célplatformjaUgyanezt az azonosítást a fejezet végigvitt projektjén is elvégezzük:
# Hostoldali vizsgálat; a fájl célja AArch64 Linux, qemu-user.
cp ~/arm-labor/a64/a001/lab-hidden .
aarch64-linux-gnu-readelf -h lab-hidden | grep -E 'Class|Data|Type|Machine'Levezetett, várt eredmény:
| ELF-mező | Várt érték | Mit azonosít |
|---|---|---|
Class |
ELF64 |
64 bites fájlformátum |
Data |
2's complement, little endian |
little-endian adatábrázolás |
Type |
EXEC |
statikusan linkelt, nem PIE futtatható fájl |
Machine |
AArch64 |
A-profile architektúra AArch64 végrehajtási állapota, A64 ISA |
A lab-hidden tehát az I. rész célplatformjára készült:
A-profile, AArch64, Linux-userspace. Egy Cortex-M firmware a II. részben ugyanitt ELF32
osztályt és ARM géptípust mutat majd. Ahogy a fenti
példánál, a fejléc itt sem árulja el a konkrét processzormagot.
| Tünet | Hipotézis | Adatgyűjtés és egyetlen célzott változtatás |
|---|---|---|
| A C-fordításnál a beépített ellenőrzés hibát jelez | A host gcc fordítóját használtuk |
Ellenőrizzük a parancsot; csak a fordítót cseréljük
aarch64-linux-gnu-gcc-re |
| Statikus linkeléskor hiányzó könyvtár jelenik meg | Hiányos a célrendszer C-fejlesztői környezete | Vizsgáljuk meg a linkerüzenetet; a hiányzó célcsomagot rendezzük |
| A program fut, de hibás összeget ad | Az assembly operandusai hibásak | objdump segítségével nézzük meg a tényleges utasítást;
csak a hibás operandust javítsuk |
| A programot Cortex-M firmware-ként próbáljuk használni | Összekevertük az ISA-t és a futási környezetet | Az ELF-fejléc alapján azonosítsuk a célt; ne átnevezéssel próbáljuk átalakítani |
Az utolsó hiba nem javítható azzal, hogy másik QEMU-programot választunk. A Cortex-M3 célhoz más gépi kód, más indulási környezet és firmware-hez tartozó memóriakiosztás kell. Az ELF konténerformátum mindkét világban használható, de ettől a benne levő programok nem kompatibilisek.
Magyarázó feladat (alapszint — cél: megértés-ellenőrzés). Egy követelmény szerint a készülék összetett Linux-alkalmazásokat futtat; egy másik szerint szűk határidős vezérlési feladatot lát el; egy harmadik szerint kis firmware-rel szenzort kezel. Mely profilok tipikus kiindulópontok? Miért nem bizonyítja önmagában az R-profile választása a határidő betartását?
Implementációs feladat (alapszint — cél:
rutinépítés). A laborban add össze a 0xffffffffu
és 1u előjel nélküli értékeket. Módosítsd az elvárt
eredményt is. Mi legyen a kiírás és a kilépési állapot? Használd
ugyanazt a build–futtatás lépéssort.
Hibakeresési feladat (középszint — cél:
rutinépítés). Az eredeti 19u, 23u
bemenetek mellett módosítsd az A64 utasítást add w0, w0, w0
alakra. Fordítsd újra a programot. A kimenetből állíts fel hipotézist,
majd objdump segítségével ellenőrizd. Egyetlen operandus
módosításával javítsd a hibát.
1. megoldás — Segítség. Gondold át, melyik profil melyik tulajdonságot (gazdag OS-felszín, garantált válaszidő, minimális erőforrás) teszi lehetővé — és hogy a “garantált” szó miért nem a profilból, hanem a teljes rendszer tervezéséből következik.
1. megoldás — Teljes megoldás. Linux-alkalmazásokhoz az A-profile, határidős vezérléshez gyakran az R-profile, kis szenzoros firmware-hez az M-profile kézenfekvő. Ezek kiindulópontok, nem automatikus döntések. A határidőt a teljes rendszer legrosszabb eseti végrehajtási és várakozási ideje határozza meg; a profil nem ad önmagában végponttól végpontig tartó időgaranciát.
2. megoldás — Segítség. Számítsd ki
0xffffffffu + 1u-t 32 biten, moduló
— mi lesz az eredmény bitmintája, és ez milyen C
unsigned int értéknek felel meg?
2. megoldás — Teljes megoldás. A két módosított C-sor:
/* I. rész: ugyanaz az AArch64 Linux laborprofil. */
unsigned int result = lab_add(0xffffffffu, 1u);
/* A printf változatlanul marad. */
return result == 0u ? 0 : 1;A levezetés: a 32 bites előjel nélküli legnagyobb érték
.
Egyet hozzáadva
keletkezik, amelynek alsó 32 bitje nulla. Az A64 W0
eredménye ezért 0, összhangban a 32 bites C
unsigned int moduláris aritmetikájával. A várt kiírás vége
osszeg=0, a kilépési állapot 0.
3. megoldás — Segítség. Számítsd ki előre, mit adna
19+19 az elvárt 19+23 helyett — ha a kimenet
ezt a hibás összeget mutatja, az operandus-cserére gyanakodj, és ezt
objdump-pal igazold.
3. megoldás — Teljes megoldás. A várt hibás eredmény
38, a kilépési állapot 1. Az A64 utasítás
kétszer az első argumentumot adja össze: 19 + 19. Az
objdump kimenetében a harmadik operandus A64
W0, nem A64 W1. Ennek javítása és újrafordítás
után ismét 42, illetve 0 várható. Ez
funkcionális ellenőrzés, nem teljesítménymérés.
Az ARM családon belül a profil, a verzió, a végrehajtási állapot és a konkrét processzormag külön fogalom. Az A-profile tipikusan összetett operációs rendszereket, az R-profile határidős rendszereket, az M-profile mikrokontrolleres feladatokat szolgál. A könyv először AArch64 Linux-alkalmazásokkal tanítja a gépi programozást, majd Cortex-M3 firmware-rel teszi láthatóvá az indulást, a megszakításokat és a perifériákat. A közös alapelvek átvihetők, a gépi kód és a futtatási szerződés nem. A laborban az ELF-fejléc a célt, a visszafejtett utasítások és a kimenet a működést ellenőrzik; egyik sem igazol hardveres teljesítményt.
A linkek és a dokumentumverziók közös állapota az Olvasói útmutató forrásállapot-táblájában van; itt csak az alfejezet saját megállapításai szerepelnek.
readelf és
objdump. Tartalmilag ellenőrizve: a
jelenlegi kiadás binutils 2.47.master ágú dokumentáció nem egy
konkrét kiadott QEMU-verzióhoz kötött; a ténylegesen telepített (pl.
Ubuntu 24.04 csomagolt) verzió dokumentációja ettől eltérhet.Ellenőrzési hiányok, amelyek a fenti ellenőrzés után is
fennmaradnak: a példát itt nem fordítottuk és nem futtattuk. A
telepített Ubuntu 24.04-csomagverziókat, a statikus célkönyvtárak
elérhetőségét és a II. részhez szükséges mps2-an385
gépmodell helyi támogatását a laborban kell ellenőrizni.