Obsah
1. Aritmetické operace s celočíselnými typy i s hodnotami s plovoucí řádovou čárkou
2. Specifické vlastnosti aritmeticko-logické jednotky
3. Hodnoty typu single a double
4. Formát plovoucí řádové binární tečky a norma IEEE 754
5. Operace s hodnotami typu single a double
6. Základní operace s FP hodnotami – načtení konstanty do FP registru
7. Praktický příklad – načtení konstant do registrů D1 a S1
8. Nepodporované konstanty aneb (logické) omezení možností RISCových instrukcí
9. Vynulování registrů Dx a Sx
10. Nepřímé načtení konstant s libovolnou hodnotou do registrů Dx a Sx
11. Přenos operandů mezi registry
12. Ukázka přenosu mezi celočíselným a FP registrem
13. Konverze mezi různými formáty
14. Převod FP hodnot na celá čísla (zaokrouhlení)
15. Základní aritmetické operace s hodnotami s plovoucí řádovou čárkou
16. Porovnání operandů s hodnotami s plovoucí řádovou čárkou
18. Repositář s demonstračními příklady
19. Předchozí články o architektuře AArch64
1. Aritmetické operace s celočíselnými typy i s hodnotami s plovoucí řádovou čárkou
Nová instrukční sada mikroprocesorů s architekturou AArch64 obsahuje instrukce, které je možné rozdělit do několika oblastí podle toho, jaká jednotka implementovaná uvnitř mikroprocesoru tyto instrukce skutečně spouští. Podívejme se na následující tabulku:
Skupina | Další dělení |
---|---|
Load-Store | Load-Store pro jeden registr |
Load-Store pro dvojici registrů (již jsme si ukázali) | |
Prefetch | |
Skoky | Nepodmíněné skoky |
Nepodmíněný skok na adresu v registru (popsáno) | |
Podmíněné skoky (popsáno) | |
ALU operace | Základní aritmetické instrukce |
Násobení a dělení | |
Logické instrukce | |
Znaménkové rozšíření operandu či rozšíření o nuly | |
Bitové operace | |
Extrakce dat | |
Bitové posuny | |
Aritmetické posuny | |
Podmíněné zpracování dat (popsáno) | |
Podmíněné porovnání | |
FP operace | Přenos operandů mezi registry |
Konverze mezi různými formáty | |
Převod na celá čísla (zaokrouhlení) | |
Základní aritmetické operace | |
Výpočet minima a maxima | |
MAC (Multiply Accumulate) | |
Porovnání operandů | |
Podmíněný výběr operandu | |
SIMD operace | Aritmetické operace se skaláry |
Aritmetické operace s vektory | |
Permutace vektorů | |
Konverze dat | |
Instrukce z crypto extension (patří do SIMD) | |
Systémové instrukce | Zpracování výjimek |
Přístup k systémovým registrům | |
Implementace bariér | |
Instrukce pro jádro systému |
Ve čtvrté části miniseriálu o specifických vlastnostech mikroprocesorů s architekturou AArch64 se budeme zabývat převážně instrukcemi určenými pro provádění aritmetických operací. Tyto instrukce se jak z historických tak i technických důvodů rozdělují na instrukce určené pro celočíselné datové typy (bajt, 16bitové slovo, 32bitové slovo, 64bitové slovo) a na instrukce, které provádějí operace s hodnotami s plovoucí řádovou čárkou (tedy ponejvíce s hodnotami single a double, i když se v této oblasti nově objevil například formát blfoat). Další dělení je možné podle toho, zda instrukce prování operaci s jedinou dvojicí operandů, nebo s dvojicí vektorů (ovšem sada registrů zůstává v tomto případě pořád stejná – konkrétně se jedná o registry V0 až V31,).
2. Specifické vlastnosti aritmeticko-logické jednotky
Kromě toho, že aritmeticko-logická jednotka byla (v porovnání s původní architekturou ARM neboli dnes A32 a T32) rozšířena pro zpracování 64bitových operandů u prakticky všech instrukcí, došlo k jejímu doplnění o vylepšenou násobičku a děličku. Násobička může kromě běžných operací pro násobení provádět i operace typu MAC (Multiply Accumulate), které typicky najdeme u DSP. U AArchu64 je zde ovšem jedna podstatná změna – namísto akumulátoru se může použít odlišný vstupní a odlišný výstupní registr, což konkrétně znamená, že DSP operace:
acc += op2 × op3
dokáže AArch64 provést:
op1 = op2 × op3 + op4
Podívejme se nyní, které instrukce provádí násobička a dělička:
# | Instrukce | Stručný popis |
---|---|---|
1 | MUL | 32bitové či 64bitové násobení |
2 | MADD | výsledek = op2 × op3 + op4 |
3 | MSUB | výsledek = op4 – op2 × op3 |
4 | MNEG | výsledek = – op2 × op3 |
5 | SMULL | násobení hodnot se znaménkem (32×32 → 64) |
6 | SMADDL | MADD hodnot se znaménkem pro (32×32 → 64) |
7 | SMSUBL | MSUB hodnot se znaménkem pro (32×32 → 64) |
8 | SMNEGL | MNEG hodnot se znaménkem pro (32×32 → 64) |
9 | SMULH | násobení 64×64, z výsledku se vezme jen horních 64 bitů ze 128 |
10 | UMULL | násobení hodnot bez znaménka (32×32 → 64) |
11 | UMADDL | MADD hodnot bez znaménka pro (32×32 → 64) |
12 | UMSUBL | MSUB hodnot bez znaménka pro (32×32 → 64) |
13 | UMNEGL | MNEG hodnot bez znaménka pro (32×32 → 64) |
14 | UMULH | násobení 64×64, z výsledku se vezme jen horních 64 bitů ze 128 |
15 | SDIV | 32bitové či 64bitové dělení hodnot se znaménkem |
16 | UDIV | 32bitové či 64bitové dělení hodnot bez znaménka |
Operace | Vstupní operandy | Výsledek |
---|---|---|
MUL | 32×32 bitů | 32 bitů |
MUL | 64×64 bitů | 64 bitů |
MUL | 32×32 bitů | 64 bitů (rozšíření) |
MUL | 64×64 bitů | horních 64 bitů (rozšíření) |
MAC | 32±32×32 bitů | 32 bitů |
MAC | 64±64×64 bitů | 64 bitů |
MAC | 64±32×32 bitů | 64 bitů (rozšíření) |
V navazujících kapitolách se zaměříme na registry a instrukce určené pro zpracování numerických hodnot s plovoucí řádovou čárkou.
3. Hodnoty typu single a double
Uložení racionálních čísel ve formátu plovoucí řádové tečky (někdy se taktéž setkáme s označením „FP formát“) se od celočíselného formátu nebo formátu s pevnou řádovou tečkou (v ČR spíše řádovou čárkou) odlišuje především v tom, že si každá numerická hodnota sama v sobě nese aktuální polohu řádové tečky (zatímco v případě, že je tečka/čárka pevně nastavena, je tato informace součástí programu a nikoli hodnoty). Z tohoto důvodu je kromě bitů, které musí být rezervovány pro uložení významných číslic numerické hodnoty, nutné pro každou numerickou hodnotu rezervovat i další bity, v nichž je určena mocnina o nějakém základu (typicky 2, 8, 10 či 16), kterou musí být významné číslice vynásobeny resp. vyděleny. První část čísla uloženého v FP formátu se nazývá mantisa, druhá část exponent (navíc se ještě přidává informace o znaménku). Obecný formát uložení a způsob získání původního čísla je následující:
xFP=be×m
přičemž význam jednotlivých symbolů je následující:
- xFX značí reprezentovanou numerickou hodnotu z podmnožiny reálných čísel
- b je báze, někdy také nazývaná radix
- e je hodnota exponentu (může být i záporná)
- m je mantisa, která může být i záporná
Konkrétní formát numerických hodnot reprezentovaných v systému plovoucí řádové tečky závisí především na volbě báze (radixu) a také na počtu bitů rezervovaných pro uložení mantisy a exponentu. V minulosti existovalo značné množství různých formátů plovoucí řádové tečky (vzpomíná si někdo například na Turbo Pascal s jeho šestibajtovým datovým typem real?), v relativně nedávné minulosti se však ustálilo použití formátů specifikovaných v normě IEEE 754 (ta sama je ovšem postupně rozšiřována). Ovšem, jak uvidíme dále, se ukazuje, že původní formáty definované v IEEE 754 nedostačují všem požadavkům, a to na obou stranách spektra (někdo požaduje vyšší přesnost/rozsah, jiný zase rychlost výpočtů a malé paměťové nároky). Proto došlo k rozšíření této normy o nové formáty a nezávisle na tom i na vývoji formátu bfloat16. Nicméně nás dnes budou v souvislosti s procesory AArch64 zajímat především formáty s jednoduchou a dvojitou přesností, neboli single (float) a double.
4. Formát plovoucí řádové binární tečky a norma IEEE 754
V oblasti FP formátů se dnes nejčastěji setkáme s výše zmíněnou normou IEEE 754, popř. jejími rozšířenými variantami. Norma IEEE 754 je velmi užitečná v tom, že specifikuje nejenom vlastní formát uložení numerických hodnot v systému plovoucí řádové tečky, ale (a to je celkem neznámá skutečnost) i pravidla implementace operací s těmito hodnotami, včetně konverzí. Konkrétně je v této normě popsáno:
- Základní (basic) a rozšířený (extended) formát uložení numerických hodnot.
- Způsob provádění základních matematických operací:
- součet
- rozdíl
- součin
- podíl
- zbytek po dělení
- druhá odmocnina
- porovnání
- Režimy zaokrouhlování.
- Způsob práce s takzvanými denormalizovanými hodnotami.
- Pravidla konverze mezi celočíselnými formáty (integer bez a se znaménkem) a formáty s plovoucí řádovou čárkou.
- Způsob konverze mezi různými formáty s plovoucí řádovou čárkou (single → double atd.).
- Způsob konverze základního formátu s plovoucí řádovou čárkou na řetězec číslic (včetně nekonečen a nečíselných hodnot).
- Práce s hodnotami NaN (not a number) a výjimkami, které mohou při výpočtech za určitých předpokladů vzniknout (NaN totiž ve skutečnosti jsou čísla :-).
Obrázek 1: První čip, který používal formát definovaný v IEEE 754 – Intel 8087.
Zdroj: Wikipedia, Autor: Dirk Oppelt
V normě (přesněji řečeno v její rozšířené variantě IEEE 754–2008, resp. její poslední úpravě IEEE 754–2019) nalezneme mj. i tyto FP formáty:
Označení | Šířka (b) | Báze | Exponent (b) | Mantisa (b) |
---|---|---|---|---|
IEEE 754 half | 16 | 2 | 5 | 10+1 |
IEEE 754 single | 32 | 2 | 8 | 23+1 |
IEEE 754 double | 64 | 2 | 11 | 52+1 |
IEEE 754 double extended | 80 | 2 | 15 | 64 |
IEEE 754 quadruple | 128 | 2 | 15 | 112+1 |
IEEE 754 octuple | 256 | 2 | 19 | 236+1 |
Obrázek 2: Mikroprocesory Pentium i všechny další čipy řady 80×86 již implicitně obsahují plnohodnotný FPU. Zlé jazyky tvrdí, že u první řady Pentií byl FPU tak rychlý jen proto, že výsledky pouze odhadoval :-)
Nás však budou v dalším textu zajímat především formáty single a double.
Typ single (nebo float, popř. float32) vypadá takto:
bit | 31 | 30 29 … 24 23 | 22 21 … 3 2 1 0 |
---|---|---|---|
význam | s | exponent (8 bitů) | mantisa (23 bitů) |
Exponent je přitom posunutý o hodnotu bias, která je nastavena na 127, protože je použit výše uvedený vztah:
bias=2eb-1-1
a po dosazení eb=8 (bitů) dostaneme:
bias=28–1-1=27-1=128–1=127
Vzorec pro vyjádření reálné hodnoty vypadá následovně:
Xsingle=(-1)s × 2exp-127 × m
Rozsah hodnot, které je možné reprezentovat ve formátu jednoduché přesnosti v normalizovaném tvaru je –3,4×1038 až 3,4×1038. Nejnižší reprezentovatelná (normalizovaná) hodnota je rovna 1,17549×10-38, denormalizovaná pak 1,40129×10-45. Jak jsme k těmto hodnotám došli? Zkuste se podívat na následující vztahy:
hexadecimální hodnota | výpočet FP | dekadický výsledek | normalizováno |
---|---|---|---|
0×00000001 | 2-126×2-23 | 1,40129×10-45 | ne |
0×00800000 | 2-126 | 1,17549×10-38 | ano |
0×7F7FFFFF | (2–2-23)×2127 | 3,4×1038 | ano |
Formát s dvojitou přesností (double), který je definovaný taktéž normou IEEE 754, se v mnoha ohledech podobá formátu s jednoduchou přesností (single), pouze se zdvojnásobil celkový počet bitů, ve kterých je hodnota uložena, tj. místo 32 bitů se používá plných 64 bitů:
bit | 63 | 62 … 52 | 51 … 0 |
---|---|---|---|
význam | s | exponent (11 bitů) | mantisa (52 bitů) |
Exponent je v tomto případě posunutý o hodnotu bias=2047 a vzorec pro výpočet reálné hodnoty vypadá takto:
Xdouble=(-1)s × 2exp-2047 × m
Přičemž hodnotu mantisy je možné pro normalizované hodnoty získat pomocí vztahu:
m=1+m51-1+m50-2+m49-3+…+m0-52
(mx představuje x-tý bit mantisy)
Rozsah hodnot ukládaných ve dvojité přesnosti je –1,7×10308..1,7×10308, nejmenší možná nenulová hodnota je rovna 2,2×10-308.
V novější normě IEEE 754–2008 je specifikován nepovinný formát nazvaný binary128, který se ovšem běžně označuje quadruple precision či jen quad precision. Tento formát je založen na slovech širokých 128 bitů (16 bajtů), která jsou rozdělena takto:
bit | 127 | 126 … 112 | 111 … 0 |
---|---|---|---|
význam | s | exponent (15 bitů) | mantisa (112 bitů) |
Exponent je v tomto případě posunutý o hodnotu bias=16383. Dekadická přesnost u tohoto formátu dosahuje 34 cifer!
Jen krátce se zmiňme o poslední variantě FP formátu, který se nazývá binary256 či méně formálně octuple precision. Tento formát využívá slova o šířce plných 256 bitů (32 bajtů) s následujícím rozdělením:
bit | 255 | 254 … 236 | 235 … 0 |
---|---|---|---|
význam | s | exponent (19 bitů) | mantisa (235 bitů) |
Exponent je v tomto případě posunutý o hodnotu bias=262143. Dekadická přesnost u tohoto formátu dosahuje 71 cifer, nejmenší (nenormalizovaná) reprezentovatelná hodnota rozdílná od nuly je přibližně 10−78984, maximální hodnota pak 1.611 ×1078913 (těžko říct, zda je takový rozsah vůbec reálně využitelný).
5. Operace s hodnotami typu single a double
Matematický koprocesor je sice u architektury AArch64 volitelný (u desktopových procesorů ho najdete vždy), ale oproti 32bitovým ARMům došlo k určitému zjednodušení – už neexistuje rozdělení ABI na soft floating point a hard floating point, protože pro předávání hodnot typu single/float a double jsou vždy použity FP registry popsané v navazující kapitole. Z technologického hlediska sice není soft floating point špatné řešení, ale prakticky způsobovalo (a dodnes způsobuje) množství problémů při distribuci knihoven i aplikací.
Samostatná sada pracovních registrů je používána při operacích s typy single/float a double (tedy s operandy reprezentovanými v systému plovoucí řádové čárky), u SIMD operací a taktéž kryptografickým modulem:
Jméno | Význam |
---|---|
v0..v31 | 128bitové registry |
d0..d31 | spodních 64 bitů registrů v0..v31, použito pro hodnoty typu double |
s0..s31 | spodních 32 bitů registrů v0..v31, použito pro hodnoty typu single/float |
Pro SIMD operace, tj. operace pracující s vektory, se výše uvedené registry Vn rozdělují následujícím způsobem:
Tvar (shape) | Celkem | Pojmenování v assembleru |
---|---|---|
8b×8 | 64b | Vn.8B |
8b×16 | 128b | Vn.16B |
16b×4 | 64b | Vn.4H |
16b×8 | 128b | Vn.8H |
32b×2 | 64b | Vn.2S |
32b×4 | 128b | Vn.4S |
64b×1 | 64b | Vn.1D |
64b×2 | 128b | Vn.2D |
Aritmetické operace:
# | Instrukce | Význam | Prováděný výpočet |
---|---|---|---|
1 | VADD Fd, Fn, Fm | součet | Fd := Fn + Fm |
2 | VSUB Fd, Fn, Fm | rozdíl | Fd := Fn – Fm |
3 | VNEG Fd, Fm | změna znaménka | Fd := – Fm |
4 | VABS Fd, Fm | absolutní hodnota | Fd := abs(Fm) |
5 | VSQRT Fd, Fm | druhá odmocnina | Fd := sqrt(Fm) |
6 | VDIV Fd, Fn, Fm | dělení | Fd := Fn / Fm |
7 | VMUL Fd, Fn, Fm | násobení | Fd := Fn * Fm |
8 | VMLA Fd, Fn, Fm | násobení + akumulace | Fd := Fd + (Fn * Fm) |
9 | VMLS Fd, Fn, Fm | odečtení součinu | Fd := Fd – (Fn * Fm) |
10 | VNMUL Fd, Fn, Fm | násobení + změna znaménka | Fn := – (Fn * Fm) |
11 | VNMLA Fd, Fn, Fm | kombinace VNMUL a VMLA | Fd := – Fd – (Fn * Fm) |
12 | VNMLS Fd, Fn, Fm | kombinace VNMUL a VMLS | Fd := – Fd + (Fn * Fm) |
Porovnání:
# | Instrukce | Význam | Prováděný výpočet |
---|---|---|---|
1 | VCMP Fd, Fm | Porovnání obsahu dvou registrů | Fd – Fm |
2 | VCMP Fd, #0.0 | Porovnání jednoho registru s nulou | Fd – 0.0 |
Přesuny dat:
# | Instrukce | Význam |
---|---|---|
1 | VCVT{C}.F64.F32 Dd, Sm | Konverze single na double |
2 | VCVT{C}.F32.F64 Sd, Dm | Konverze double na single |
3 | VCVT{C}.F32/F64.U32 Fd, Sm | Konverze unsigned integer na float |
4 | VCVT{C}.F32/F64.S32 Fd, Sm | Konverze signed integer na float |
5 | VCVT{R}{C}.U32.F32/F64 Sd, Fm | Konverze float na unsigned integer |
6 | VCVT{R}{C}.S32.F32/F64 Sd, Fm | Konverze float na signed integer |
7 | VCVT.F32/F64.typ Fd, Fd, #bitů | Konverze fixed-point na float (volitelná pozice tečky) |
8 | VCVT.typ.F32/F64 Fd, Fd, #bitů | Konverze float na fixed-point (volitelná pozice tečky) |
9 | VCVTT.F16.F32 Sd,Sm | Konverze single na half (do horních 16 bitů registru) |
10 | VCVTB.F16.F32 Sd,Sm | Konverze single na half (do spodních 16 bitů registru) |
11 | VCVTT.F32.F16 Sd,Sm | Konverze half na single |
12 | VCVTB.F32.F16 Sd,Sm | Konverze half na single |
13 | VMOV.F32/F64 Fd, Fm | Fd := Fm (prostá kopie) |
14 | VMOV Sn, Rd | Sn := Rd (Rd = registr ARM procesoru) |
15 | VMOV Rd, Sn | Rd := Sn (Rd = registr ARM procesoru) |
16 | VMOV Sn, Sm, Rd, Rn | Sn := Rd, Sm := Rn (kopie dvou registrů) |
17 | VMOV Rd, Rn, Sn, Sm | Rd := Sn, Rn := Sm (kopie dvou registrů) |
18 | VMOV Dm, Rd, Rn | Dm[31:0] := Rd, Dm[63:32] := Rn (pro double jsou zapotřebí dva ARM registry) |
19 | VMOV Rd, Rn, Dm | Rd := Dm[31:0], Rn := Dm[63:32] (pro double jsou zapotřebí dva ARM registry) |
20 | VMOV Dn[0], Rd | Dn[31:0] := Rd (pouze spodní polovina double) |
21 | VMOV Rd, Dn[0] | Rd := Dn[31:0] (pouze spodní polovina double) |
22 | VMOV Dn[1], Rd | Dn[63:32] := Rd (pouze horní polovina double) |
23 | VMOV Rd, Dn[1] | Rd := Dn[63:32] (pouze horní polovina double) |
24 | VMRS APSR_nzcv, FPSCR | APSR flags := FPSCR flags (přenos příznaků) |
6. Základní operace s FP hodnotami – načtení konstanty do FP registru
Pro načtení konstanty typu single/float a double do jednoho z pracovních registrů Sx či Dx se používá instrukce nazvaná FMOV. Ovšem vzhledem k tomu, že jak instrukční slovo, tak i konstanta mají dohromady pouhých 32 bitů (jako všechny ostatní RISCové instrukce), je zřejmé, že tímto způsobem není možné načíst libovolné číslo, ale pouze hodnotu odpovídající určitým pravidlům. Reprezentovatelná hodnota odpovídá výrazu ±n÷16×2r, kde n je celé číslo 16 ≤ n ≤ 31 a r je taktéž celé číslo –3 ≤ r ≤ 4. Tato čísla jsou reprezentována čtyřmi resp. třemi bity, další bit slouží pro uložení znaménka v instrukčním slovu (k tomuto omezení se ještě vrátíme):
# | Instrukce | Stručný popis |
---|---|---|
1 | FMOV Sd, #fpimm | načtení konstanty typu single/float do registru Sx |
2 | FMOV Dd, #fpimm | načtení konstanty typu double do registru Dx |
Speciálním případem je načtení nuly, které se provede jednoduše – použitím registrů XZR či WZR, které obsahují nulu a konstanta nula (0,0) je ve formátu IEEE 754 taktéž reprezentována samými nulovými bity (což je jedna z mnoha vychytávek IEEE 754).
Následující úryvek céčkového kódu:
float x = 0.0; double y = 0.0;
se přeloží následovně (jedná se o lokální proměnné ukládané na zásobníkový rámec, tedy relativně vůči SP):
// float x = 0.0 str wzr, [sp, 28] // double y = 0.0 str xzr, [sp, 16]
Další instrukce slouží pro načtení operandu z paměti a pro uložení operandů zpět do paměti. Tyto instrukce již známe, pouze došlo k jejich rozšíření i pro použití s FP registry. Operace s jednotlivými bajty se používají u vektorových operací. Samozřejmě nesmíme zapomenout ani na instrukce pro načtení a uložení registrového páru:
# | Instrukce | Stručný popis |
---|---|---|
1 | LDR Bt, adresa | načtení spodních osmi bitů |
2 | LDR Ht, adresa | načtení spodních šestnácti bitů |
3 | LDR St, adresa | načtení 32 bitů (float) |
4 | LDR Dt, adresa | načtení 64 bitů (double) |
5 | LDR Qt, adresa | načtení 128 bitů (quad) |
6 | STR Bt, adresa | uložení spodních osmi bitů |
7 | STR Ht, adresa | uložení spodních šestnácti bitů |
8 | STR St, adresa | uložení 32 bitů (float) |
9 | STR Dt, adresa | uložení 64 bitů (double) |
10 | STR Qt, adresa | uložení 128 bitů (quad) |
11 | LDP S1, S2, adresa | načtení registrového páru (single) |
12 | LDP D1, D2, adresa | načtení registrového páru (double) |
13 | LDP Q1, Q2, adresa | načtení registrového páru (quad) |
14 | STP S1, S2, adresa | uložení registrového páru (single) |
15 | STP D1, D2, adresa | uložení registrového páru (double) |
16 | STP Q1, Q2, adresa | uložení registrového páru (quad) |
Podívejme se opět na jednoduchý příklad využití těchto instrukcí v praxi. Následující fragment céčkového kódu s inicializací čtyř lokálních proměnných:
float x = 1.0; float y = 10.0; float z = 100.0; float w = 1000.0;
se přeloží takto:
// float x = 1.0; fmov s0, 1.0e+0 str s0, [sp, 12] // float y = 10.0; fmov s0, 1.0e+1 str s0, [sp, 8] // float z = 100.0; adrp x0, .LC0 add x0, x0, :lo12:.LC0 ldr s0, [x0] str s0, [sp, 4] // float w = 1000.0; adrp x0, .LC1 add x0, x0, :lo12:.LC1 ldr s0, [x0] str s0, [sp]
První proměnné lze načíst přímo instrukcí FMOV (konstanta je součástí instrukce), další pouze nepřímo z operační paměti.
Konstanty uložené v operační paměti:
.LC0: .word 1120403456 .LC1: .word 1148846080
7. Praktický příklad – načtení konstant do registrů D1 a S1
Použití instrukcí FMOV pro načtení konstanty je snadné v případě, že je konstantu možné uložit přímo do instrukčního slova. Nejdříve načteme konstantu 1.0 do registru D1 (typu double):
# asmsyntax=as # Načtení FP konstanty do registru d1 # v assembleru GNU AS pro architekturu AArch64. # # Autor: Pavel Tišnovský # Linux kernel system call table sys_exit=93 # List of syscalls for AArch64: # https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h #----------------------------------------------------------------------------- .section .data #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start // tento symbol má být dostupný i z linkeru _start: fmov d1, #1.00 // načtení konstanty do registru mov x8, #sys_exit // číslo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volání Linuxového kernelu
Obsah výsledného binárního souboru po překladu a slinkování:
$ objdump -d a.out a.out: file format elf64-littleaarch64 Disassembly of section .text: 0000000000400078 <_start>: 400078: 1e6e1001 fmov d1, #1.000000000000000000e+00 40007c: d2800ba8 mov x8, #0x5d // #93 400080: d2800000 mov x0, #0x0 // #0 400084: d4000001 svc #0x0
Podobný příklad, ovšem pro registr S1 a tudíž konstantu typu single:
# asmsyntax=as # Načtení FP konstanty do registru s1 # v assembleru GNU AS pro architekturu AArch64. # # Autor: Pavel Tišnovský # Linux kernel system call table sys_exit=93 # List of syscalls for AArch64: # https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h #----------------------------------------------------------------------------- .section .data #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start // tento symbol má být dostupný i z linkeru _start: fmov s1, #1.00 // načtení konstanty do registru mov x8, #sys_exit // číslo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volání Linuxového kernelu
Obsah výsledného binárního souboru po překladu a slinkování je nyní poněkud odlišný:
$ objdump -d a.out a.out: file format elf64-littleaarch64 Disassembly of section .text: 0000000000400078 <_start>: 400078: 1e2e1001 fmov s1, #1.000000000000000000e+00 40007c: d2800ba8 mov x8, #0x5d // #93 400080: d2800000 mov x0, #0x0 // #0 400084: d4000001 svc #0x0
8. Nepodporované konstanty aneb (logické) omezení možností RISCových instrukcí
Některé konstanty (přesněji řečeno jejich naprostou většinu) není možné uložit do instrukčního slova instrukce FMOV, o čemž se můžeme velmi snadno přesvědčit:
.section .text .global _start // tento symbol má být dostupný i z linkeru _start: fmov s1, #0.00 // načtení konstanty do registru mov x8, #sys_exit // číslo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volání Linuxového kernelu
Při pokusu o překlad tohoto kódu nastane chyba:
fmov3.s: Assembler messages: fmov3.s:33: Error: invalid floating-point constant at operand 2 -- `fmov s1,#0.00'
9. Vynulování registrů Dx a Sx
V šesté kapitole jsme si řekli, že vynulování registrů Dx či Sx dosáhneme přesunem nulové hodnoty z registru XZR resp. WZR. Nejdříve si ukažme vynulování registru S1, tedy typu single:
# asmsyntax=as # Načtení FP konstanty do registru s1 # v assembleru GNU AS pro architekturu AArch64. # # Autor: Pavel Tišnovský # Linux kernel system call table sys_exit=93 # List of syscalls for AArch64: # https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h #----------------------------------------------------------------------------- .section .data #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start // tento symbol má být dostupný i z linkeru _start: fmov s1, wzr // načtení konstanty do registru mov x8, #sys_exit // číslo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volání Linuxového kernelu
Způsob překladu:
$ objdump -d a.out a.out: file format elf64-littleaarch64 Disassembly of section .text: 0000000000400078 <_start>: 400078: 1e2703e1 fmov s1, wzr 40007c: d2800ba8 mov x8, #0x5d // #93 400080: d2800000 mov x0, #0x0 // #0 400084: d4000001 svc #0x0
A vynulování registru D1, tedy typu double:
# asmsyntax=as # Načtení FP konstanty do registru d1 # v assembleru GNU AS pro architekturu AArch64. # # Autor: Pavel Tišnovský # Linux kernel system call table sys_exit=93 # List of syscalls for AArch64: # https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h #----------------------------------------------------------------------------- .section .data #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start // tento symbol má být dostupný i z linkeru _start: fmov d1, xzr // načtení konstanty do registru mov x8, #sys_exit // číslo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volání Linuxového kernelu
A způsob překladu do objektového kódu:
$ objdump -d a.out a.out: file format elf64-littleaarch64 Disassembly of section .text: 0000000000400078 <_start>: 400078: 9e6703e1 fmov d1, xzr 40007c: d2800ba8 mov x8, #0x5d // #93 400080: d2800000 mov x0, #0x0 // #0 400084: d4000001 svc #0x0
10. Nepřímé načtení konstant s libovolnou hodnotou do registrů Dx a Sx
Konstanty, které nelze zakódovat do instrukčního slova instrukce FMOV, se většinou celé (32 bitů či 64 bitů) ukládají do paměti a načítají instrukcí LDR. Jediný problém spočívá v tom, že assembler (resp. GNU Assembler) nedokáže rozpoznat konstanty typu single či double, takže je nutné hodnotu nejdříve získat konverzí do decimálního či hexadecimálního tvaru. K tomuto účelu lze použít aplikaci dostupnou na adrese https://baseconvert.com/ieee-754-floating-point.
Konkrétně může načtení 64bitové konstanty do registru D1 vypadat takto:
ldr d1, =0x3FF0000000000000 // načtení konstanty do registru
Následuje příklad použití:
# asmsyntax=as # Načtení FP konstanty do registru d1 # v assembleru GNU AS pro architekturu AArch64. # # Autor: Pavel Tišnovský # Linux kernel system call table sys_exit=93 # List of syscalls for AArch64: # https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h #----------------------------------------------------------------------------- .section .data #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start // tento symbol má být dostupný i z linkeru _start: ldr d1, =0x3FF0000000000000 // načtení konstanty do registru mov x8, #sys_exit // číslo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volání Linuxového kernelu
Způsob překladu do strojového kódu je v tomto případě velmi zajímavý, protože samotná konstanta je uložena za samotným kódem a je načtena s využitím „krátké“ adresy uložené přímo v instrukčním slovu instrukce ldr:
$ objdump -d a.out a.out: file format elf64-littleaarch64 Disassembly of section .text: 0000000000400078 <_start>: 400078: 5c000081 ldr d1, 400088 <_start+0x10> 40007c: d2800ba8 mov x8, #0x5d // #93 400080: d2800000 mov x0, #0x0 // #0 400084: d4000001 svc #0x0 400088: 4048f5c3 .word 0x4048f5c3 40008c: 00000000 .word 0x00000000
11. Přenos operandů mezi registry
Další skupina instrukcí mikroprocesorů s architekturou AArch64 sice taktéž používá mnemotechnickou zkratku FMOV, ovšem neslouží k načtení konstanty, ale k přenosu operandu (tedy konkrétní hodnoty) mezi různými registry. Zajímavé je, že je možné přenášet operandy mezi celočíselnými registry a FP registry; v takovém případě se přenese přesný bitový obraz uloženého čísla a neprovádí se žádné konverze (zaokrouhlení atd.). Poslední dvě instrukce jsou užitečné pro přenos 64 bitů do nebo naopak ze 128 bitového registru Vd:
# | Instrukce | Stručný popis |
---|---|---|
1 | FMOV Sd, Sn | přenos mezi registry (oba typu single) |
2 | FMOV Wd, Sn | přenos mezi registry (32bitový integer, single) |
3 | FMOV Sd, Wn | přenos mezi registry (32bitový integer, single) |
4 | FMOV Dd, Dn | přenos mezi registry (oba typu double) |
5 | FMOV Xd, Dn | přenos mezi registry (64bitový integer, double) |
6 | FMOV Dd, Xn | přenos mezi registry (64bitový integer, double) |
7 | FMOV Xd, Vn.D[1] | přenos 64 bitů Vn<127:64> → Xd |
8 | FMOV Vd.D[1], Xn | přenos 64 bitů Xn → Vd<127:64>, ostatní bity Vd se nezmění |
12. Ukázka přenosu mezi celočíselným a FP registrem
V dalším demonstračním příkladu je ukázán způsob přenosu dat mezi registry X1, D1 a X2 s využitím instrukce FMOV:
# asmsyntax=as # Přesuny mezi celočíselnými a FP registry # v assembleru GNU AS pro architekturu AArch64. # # Autor: Pavel Tišnovský # Linux kernel system call table sys_exit=93 # List of syscalls for AArch64: # https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h #----------------------------------------------------------------------------- .section .data #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start // tento symbol má být dostupný i z linkeru _start: mov x1, #0x1234 // načtení celočíselné konstanty fmov d1, x1 // přenos do FP registru fmov x2, d1 // zpětný přenos do celočíselného registru mov x8, #sys_exit // číslo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volání Linuxového kernelu
Přenosy si můžeme ověřit „naživo“ v GNU Debuggeru. Pro tento účel se provede překlad s využitím přepínače -g:
$ as -g -o a.o src.s $ ld -g -o a.out a.o
Výsledný binární soubor načteme do GNU Debuggeru:
$ gdb a.out
Nastavíme breakpoint na začátek kódu, tedy na návěští _start:
(gdb) b _start
Následně program spustíme:
(gdb) r
Program se zastaví na první instrukci (díky breakpointu), takže si zobrazíme obsah pracovních registrů:
(gdb) info registers
x0 0x0 0 x1 0x0 0 x2 0x0 0 ... ... ...
Další instrukce se provede příkazem n (next). Opět si zobrazíme obsah registrů:
(gdb) info registers
x0 0x0 0 x1 0x1234 4660 x2 0x0 0 ... ... ...
Další instrukce provádí přenos do D1, takže si musíme zobrazit obsah registrů matematického koprocesoru:
(gdb) info float
d0 {f = 0x0, u = 0x0, s = 0x0} {f = 0, u = 0, s = 0} d1 {f = 0x0, u = 0x1234, s = 0x1234} {f = 2.3023459096202089e-320, u = 4660, s = 4660} d2 {f = 0x0, u = 0x0, s = 0x0} {f = 0, u = 0, s = 0} ... ... ...
A poslední instrukce přenese stejná data do celočíselného registru X2:
(gdb) info registers
x0 0x0 0 x1 0x1234 4660 x2 0x1234 4660 x3 0x0 0 ... ... ...
13. Konverze mezi různými formáty
Pro konverzi hodnot mezi různými numerickými formáty s plovoucí řádovou čárkou (half float, single, double) slouží instrukce nazvaná FCVT (neboli float convert). Některé převody lze provést bez problémů (neztratí se tedy ani přesnost ani rozsah), u dalších převodů buď ztratíme přesnost nebo bude hodnota převedena na ∞ nebo -∞ (což je ovšem očekávané chování):
# | Instrukce | Stručný popis |
---|---|---|
1 | FCVT Sd, Hn | převod mezi formátem half float a single (bez ztráty) |
2 | FCVT Hd, Sn | převod mezi formátem single a half float (ztráta přesnosti a/nebo rozsahu) |
3 | FCVT Dd, Hn | převod mezi formátem half float a double (bez ztráty) |
4 | FCVT Hd, Dn | převod mezi formátem double a half float (ztráta přesnosti a/nebo rozsahu) |
5 | FCVT Dd, Sn | převod mezi formátem single a double (bez ztráty) |
6 | FCVT Sd, Dn | převod mezi formátem double a single (ztráta přesnosti a/nebo rozsahu) |
Opět se podívejme na prozatím velmi jednoduchý příklad použití při konverzi mezi hodnotami lokálních proměnných:
float x = 1.0; double y = 1.0; double z = x; float w = y;
Překlad tohoto úryvku kódu do assembleru:
// float x = 1.0; fmov s0, 1.0e+0 str s0, [sp, 28] // double y = 1.0; fmov d0, 1.0e+0 str d0, [sp, 16] // double z = x; ldr s0, [sp, 28] fcvt d0, s0 str d0, [sp, 8] // float w = y; ldr d0, [sp, 16] fcvt s0, d0 str s0, [sp, 4]
14. Převod FP hodnot na celá čísla (zaokrouhlení)
Poměrně rozsáhlá skupina strojových instrukcí slouží pro převod FP hodnot (tedy numerických hodnot reprezentovaných v systému plovoucí řádové čárky) na celá čísla. Podívejme se na tabulku se seznamem těchto instrukcí:
# | Instrukce | Stručný popis |
---|---|---|
1 | FCVTAS | konverze FP na typ signed integer (tedy se znaménkem), zaokrouhlení směrem k nekonečnům |
2 | FCVTAU | dtto, ale konverze na datový typ unsigned integer |
3 | FCVTMS | konverze FP hodnoty na signed integer se zaokrouhlením směrem k -∞ |
4 | FCVTMU | konverze FP hodnoty na unsigned integer se zaokrouhlením směrem k -∞ |
5 | FCVTNS | konverze FP hodnoty se zaokrouhlením na nejbližší sudé číslo |
6 | FCVTNU | dtto, ovšem nyní pro unsigned integer |
7 | FCVTPS | konverze FP hodnoty na signed integer se zaokrouhlením směrem k +∞ |
8 | FCVTPU | konverze FP hodnoty na unsigned integer se zaokrouhlením směrem k +∞ |
9 | FCVTZS | konverze FP hodnoty na signed integer se zaokrouhlením směrem k nule |
10 | FCVTZU | konverze na unsigned integer se zaokrouhlením směrem k nule |
11 | SCVTF | zpětná konverze na FP hodnotu (desetinná část bude pochopitelně nulová) |
12 | UCVTF | zpětná konverze na FP hodnotu (desetinná část bude pochopitelně nulová) |
Instrukce FCVTNS a FCVTNU zaokrouhlují na nejbližší sudé číslo ty hodnoty, které leží přesně v polovině intervalu (1/2).
Nezapomeneme si samozřejmě ukázat, jak tyto instrukce používá překladač v praxi:
float x = 1.0; double y = 2.0; int i = x; int j = y;
Způsob překladu tohoto programového bloku do assembleru vypadá následovně:
// float x = 1.0; fmov s0, 1.0e+0 str s0, [sp, 28] // double y = 2.0; fmov d0, 2.0e+0 str d0, [sp, 16] // int i = x; ldr s0, [sp, 28] fcvtzs w0, s0 str w0, [sp, 12] // int j = y; ldr d0, [sp, 16] fcvtzs w0, d0 str w0, [sp, 8]
15. Základní aritmetické operace s hodnotami s plovoucí řádovou čárkou
Poměrně rozsáhlá je skupina instrukcí určených pro provádění základních aritmetických operací, k nimž navíc přidáváme instrukce pro výpočet absolutní hodnoty, odmocniny, minima, maxima atd.:
# | Instrukce | Stručný popis |
---|---|---|
1 | FABS | výpočet absolutní hodnoty (jeden zdrojový operand) |
2 | FNEG | negace hodnoty (jeden zdrojový operand) |
3 | FSQRT | výpočet druhé odmocniny (jeden zdrojový operand) |
4 | FADD | součet |
5 | FSUB | rozdíl |
6 | FMUL | součin |
7 | FNMUL | součin a následná změna znaménka výsledku |
8 | FDIV | podíl |
9 | FMIN | výpočet minima, pokud je jeden ze zdrojových operandů NaN, vrací NaN |
10 | FMAX | výpočet maxima, pokud je jeden ze zdrojových operandů NaN, vrací NaN |
11 | FMINNUM | výpočet minima, pokud je jeden ze zdrojových operandů NaN, vrací druhý operand |
12 | FMAXNUM | výpočet maxima, pokud je jeden ze zdrojových operandů NaN, vrací druhý operand |
13 | FMADD | (MAC) cíl = zdroj1 + zdroj2 × zdroj3 |
14 | FMSUB | cíl = zdroj1 – zdroj2 × zdroj3 |
15 | FNMADD | cíl = -zdroj1 + zdroj2 × zdroj3 |
16 | FNMSUB | cíl = -zdroj1 – zdroj2 × zdroj3 |
Opět se podívejme na příklad, tentokrát s nepatrně složitějším výpočtem:
float x = 1.0; float y = 2.0; float z = 3.0; float w = x*y + y/z + fabs(z);
Tento příklad se (bez optimalizací) přeloží následovně:
// float x = 1.0; fmov s0, 1.0e+0 str s0, [sp, 12] // float y = 2.0; fmov s0, 2.0e+0 str s0, [sp, 8] // float w = x*y + y/z + fabs(z); fmov s0, 3.0e+0 str s0, [sp, 4] // float w = x*y + y/z + fabs(z); ldr s1, [sp, 12] ldr s0, [sp, 8] fmul s1, s1, s0 // x*y ldr s2, [sp, 8] ldr s0, [sp, 4] fdiv s0, s2, s0 // y/z fadd s1, s1, s0 ldr s0, [sp, 4] fabs s0, s0 fadd s0, s1, s0
Kombinace aritmetické operace s konverzí výsledku:
float x = 1.0; double y = 1.0; float z = x+y;
Se může přeložit takto:
// float x = 1.0; fmov s0, 1.0e+0 str s0, [sp, 28] // double y = 1.0; fmov d0, 1.0e+0 str d0, [sp, 16] // float z = x+y; ldr s0, [sp, 28] fcvt d1, s0 ldr d0, [sp, 16] fadd d0, d1, d0 fcvt s0, d0 str s0, [sp, 12]
16. Porovnání operandů s hodnotami s plovoucí řádovou čárkou
Instrukce, které slouží pro porovnání obsahu dvou FP registrů, nastavují příznakové bity N, V, Z a C (prakticky stejným způsobem, jako instrukce celočíselné). To znamená, že tyto instrukce je možné přímo zkombinovat například s podmíněnými skoky:
# | Instrukce | Stručný popis |
---|---|---|
1 | FCMP | porovnání dvou FP operandů na rovnost, popř. porovnání s nulou |
2 | FCMPE | dtto, ovšem pokud je jeden z operandů NaN, dojde k výjimce |
3 | FCCMP | pokud je podmínka splněna, provede se porovnání, jinak se příznakové bity nastaví na určenou konstantu |
4 | FCCMPE | dtto ale s kontrolou operandů na NaN |
5 | FCSEL | obdoba CSEL, ovšem pro FP operandy (čtvrtým parametrem je podmínka) |
Podívejme se nyní na jednoduchý demonstrační příklad, opět využívající lokální proměnné uložené na zásobníkovém rámci:
float x = 1.0; float y = 10.0; float z = 20.0; int i = x == y; int j = x < y; int k = x <= y; int l = x != y; int m = x > y;
Způsob překladu neoptimalizujícím překladačem:
float x = 1.0; fmov s0, 1.0e+0 str s0, [sp, 28] // float y = 10.0; fmov s0, 1.0e+1 str s0, [sp, 24] // float z = 20.0; fmov s0, 2.0e+1 str s0, [sp, 20] // int i = x == y; ldr s1, [sp, 28] ldr s0, [sp, 24] fcmp s1, s0 cset w0, eq // testuje se příznakový bit Z (zero) uxtb w0, w0 // rozšíření osmibitové hodnoty na 32 bitů str w0, [sp, 16] // int j = x < y; ldr s1, [sp, 28] ldr s0, [sp, 24] fcmpe s1, s0 cset w0, mi uxtb w0, w0 // rozšíření osmibitové hodnoty na 32 bitů str w0, [sp, 12] // int k = x <= y; ldr s1, [sp, 28] ldr s0, [sp, 24] fcmpe s1, s0 cset w0, ls uxtb w0, w0 // rozšíření osmibitové hodnoty na 32 bitů str w0, [sp, 8] // int l = x != y; ldr s1, [sp, 28] ldr s0, [sp, 24] fcmp s1, s0 cset w0, ne // testuje se příznakový bit Z (zero) uxtb w0, w0 // rozšíření osmibitové hodnoty na 32 bitů str w0, [sp, 4] // int m = x > y; ldr s1, [sp, 28] ldr s0, [sp, 24] fcmpe s1, s0 cset w0, gt uxtb w0, w0 // rozšíření osmibitové hodnoty na 32 bitů
17. SIMD operace
Zbývá nám si popsat „maličkost“ a to konkrétně SIMD operace, které umožňují provádět výpočty nad celými vektory hodnot. Ve skutečnosti je počet „vektorových“ instrukcí větší, než počet všech zbývajících instrukcí (včetně instrukcí matematického koprocesoru), takže si na jejich popis vyhradíme celý článek.
18. Repositář s demonstračními příklady
Všechny minule i dnes popisované demonstrační příklady byly společně s podpůrným souborem Makefile určeným pro jejich překlad či naopak pro disassembling, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/presentations/. Všechny příklady jsou určeny pro standardní GNU Assembler a používají výchozí syntaxi procesorů AArch64. Následuje tabulka s odkazy na zdrojové kódy příkladů i na již zmíněné podpůrné skripty:
19. Předchozí články o architektuře AArch64
S architekturou AArch64 jsme se již na stránkách Roota setkali, a to konkrétně v následujících článcích, z nichž v dnešním článku vycházíme:
- 64bitové mikroprocesory s architekturou AArch64
https://www.root.cz/clanky/64bitove-mikroprocesory-s-architekturou-aarch64/ - Instrukční sada AArch64
https://www.root.cz/clanky/instrukcni-sada-aarch64/ - Instrukční sada AArch64 (2.část)
https://www.root.cz/clanky/instrukcni-sada-aarch64–2-cast/ - Tvorba a ladění programů v assembleru mikroprocesorů AArch64
https://www.root.cz/clanky/tvorba-a-ladeni-programu-v-assembleru-mikroprocesoru-aarch64/ - Instrukční sada AArch64: technologie NEON
https://www.root.cz/clanky/instrukcni-sada-aarch64-technologie-neon/ - Specifické vlastnosti procesorů AArch64: základní instrukce
https://www.root.cz/clanky/specificke-vlastnosti-procesoru-aarch64-zakladni-instrukce/ - Specifické vlastnosti procesorů AArch64: podmíněné a nepodmíněné skoky, adresování dat
https://www.root.cz/clanky/specificke-vlastnosti-procesoru-aarch64-podminene-a-nepodminene-skoky-adresovani-dat/ - Specifické vlastnosti procesorů AArch64: přenos bloků dat a instrukce s podmínkou
https://www.root.cz/clanky/specificke-vlastnosti-procesoru-aarch64-prenos-bloku-dat-a-instrukce-s-podminkou/
20. Odkazy na Internetu
- Arm Architecture Reference Manual for A-profile architecture
https://developer.arm.com/documentation/ddi0487/latest - The GNU Assembler – macros
http://tigcc.ticalc.org/doc/gnuasm.html#SEC109 - GNU Binutils
https://sourceware.org/binutils/ - Documentation for binutils 2.38
https://sourceware.org/binutils/docs-2.38/ - AArch64 Instruction Set Architecture
https://developer.arm.com/architectures/learn-the-architecture/aarch64-instruction-set-architecture/instruction-sets-in-the-arm-architecture - Arm Armv8-A A32/T32 Instruction Set Architecture
https://developer.arm.com/documentation/ddi0597/2021–12/?lang=en - Comparison of ARMv8-A cores
https://en.wikipedia.org/wiki/Comparison_of_ARMv8-A_cores - Cortex-A32 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a32-processor.php - Cortex-A35 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a35-processor.php - Cortex-A53 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a53-processor.php - Cortex-A57 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a57-processor.php - Cortex-A72 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a72-processor.php - Cortex-A73 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a73-processor.php - Apple A7 (SoC založen na CPU Cyclone)
https://en.wikipedia.org/wiki/Apple_A7 - System cally pro AArch64 na Linuxu
https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h - Architectures/AArch64 (FedoraProject.org)
https://fedoraproject.org/wiki/Architectures/AArch64 - SIG pro AArch64 (CentOS)
https://wiki.centos.org/SpecialInterestGroup/AltArch/AArch64 - The ARMv8 instruction sets
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - A64 Instruction Set
https://developer.arm.com/products/architecture/instruction-sets/a64-instruction-set - Switching between the instruction sets
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - The A64 instruction set
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - Introduction to ARMv8 64-bit Architecture
https://quequero.org/2014/04/introduction-to-arm-architecture/ - MCU market turns to 32-bits and ARM
http://www.eetimes.com/document.asp?doc_id=1280803 - Cortex-M0 Processor (ARM Holdings)
http://www.arm.com/products/processors/cortex-m/cortex-m0.php - Cortex-M0+ Processor (ARM Holdings)
http://www.arm.com/products/processors/cortex-m/cortex-m0plus.php - ARM Processors in a Mixed Signal World
http://www.eeweb.com/blog/arm/arm-processors-in-a-mixed-signal-world - ARM Architecture (Wikipedia)
https://en.wikipedia.org/wiki/ARM_architecture - DSP for Cortex-M
https://developer.arm.com/technologies/dsp/dsp-for-cortex-m - Cortex-M processors in DSP applications? Why not?!
https://community.arm.com/processors/b/blog/posts/cortex-m-processors-in-dsp-applications-why-not - White Paper – DSP capabilities of Cortex-M4 and Cortex-M7
https://community.arm.com/processors/b/blog/posts/white-paper-dsp-capabilities-of-cortex-m4-and-cortex-m7 - Q (number format)
https://en.wikipedia.org/wiki/Q_%28number_format%29 - TriCore Architecture & Core
http://www.infineon.com/cms/en/product/microcontroller/32-bit-tricore-tm-microcontroller/tricore-tm-architecture-and-core/channel.html?channel=ff80808112ab681d0112ab6b73d40837 - TriCoreTM V1.6 Instruction Set: 32-bit Unified Processor Core
http://www.infineon.com/dgdl/tc_v131_instructionset_v138.pdf?fileId=db3a304412b407950112b409b6dd0352 - TriCore v2.2 C Compiler, Assembler, Linker Reference Manual
http://tasking.com/support/tricore/tc_reference_guide_v2.2.pdf - Infineon TriCore (Wikipedia)
https://en.wikipedia.org/wiki/Infineon_TriCore - C166®S V2 Architecture & Core
http://www.infineon.com/cms/en/product/microcontroller/16-bit-c166-microcontroller/c166-s-v2-architecture-and-core/channel.html?channel=db3a304312bef5660112c3011c7d01ae - Comparing four 32-bit soft processor cores
http://www.eetimes.com/author.asp?section_id=14&doc_id=1286116 - RISC-V Instruction Set
http://riscv.org/download.html#spec_compressed_isa - RISC-V Spike (ISA Simulator)
http://riscv.org/download.html#isa-sim - RISC-V (Wikipedia)
https://en.wikipedia.org/wiki/RISC-V - David Patterson (Wikipedia)
https://en.wikipedia.org/wiki/David_Patterson_(computer_scientist) - OpenRISC (oficiální stránky projektu)
http://openrisc.io/ - OpenRISC architecture
http://openrisc.io/architecture.html - Emulátor OpenRISC CPU v JavaScriptu
http://s-macke.github.io/jor1k/demos/main.html - OpenRISC (Wikipedia)
https://en.wikipedia.org/wiki/OpenRISC - OpenRISC – instrukce
http://sourceware.org/cgen/gen-doc/openrisc-insn.html - OpenRISC – slajdy z přednášky o projektu
https://iis.ee.ethz.ch/~gmichi/asocd/lecturenotes/Lecture6.pdf - Berkeley RISC
http://en.wikipedia.org/wiki/Berkeley_RISC - Great moments in microprocessor history
http://www.ibm.com/developerworks/library/pa-microhist.html - Microprogram-Based Processors
http://research.microsoft.com/en-us/um/people/gbell/Computer_Structures_Principles_and_Examples/csp0167.htm - Great Microprocessors of the Past and Present
http://www.cpushack.com/CPU/cpu1.html - A Brief History of Microprogramming
http://www.cs.clemson.edu/~mark/uprog.html - What is RISC?
http://www-cs-faculty.stanford.edu/~eroberts/courses/soco/projects/2000–01/risc/whatis/ - RISC vs. CISC
http://www-cs-faculty.stanford.edu/~eroberts/courses/soco/projects/2000–01/risc/risccisc/ - RISC and CISC definitions:
http://www.cpushack.com/CPU/cpuAppendA.html - FPGA
https://cs.wikipedia.org/wiki/Programovateln%C3%A9_hradlov%C3%A9_pole - The Evolution of RISC
http://www.ibm.com/developerworks/library/pa-microhist.html#sidebar1 - disasm.pro
https://disasm.pro/ - Exploring AArch64 assembler – Chapter 5
https://thinkingeek.com/2016/11/13/exploring-aarch64-assembler-chapter-5/ - Brain Floating Point – nový formát uložení čísel pro strojové učení a chytrá čidla
https://www.root.cz/clanky/brain-floating-point-ndash-novy-format-ulozeni-cisel-pro-strojove-uceni-a-chytra-cidla/ - Why Intel is betting on BFLOAT16 to be a game changer for deep learning training? Hint: Range trumps Precision
https://hub.packtpub.com/why-intel-is-betting-on-bfloat16-to-be-a-game-changer-for-deep-learning-training-hint-range-trumps-precision/ - half-rs (pro Rust)
https://github.com/starkat99/half-rs - float16 (pro Go)
https://github.com/x448/float16 - bfloat16 – Hardware Numerics Definition
https://software.intel.com/en-us/download/bfloat16-hardware-numerics-definition - Intel Prepares To Graft Google’s Bfloat16 Onto Processors
https://www.nextplatform.com/2019/07/15/intel-prepares-to-graft-googles-bfloat16-onto-processors/ - Norma IEEE 754 a příbuzní: formáty plovoucí řádové tečky
https://www.root.cz/clanky/norma-ieee-754-a-pribuzni-formaty-plovouci-radove-tecky/ - IEEE-754 Floating-Point Conversion
http://babbage.cs.qc.cuny.edu/IEEE-754.old/32bit.html - Small Float Formats
https://www.khronos.org/opengl/wiki/Small_Float_Formats - Binary-coded decimal
https://en.wikipedia.org/wiki/Binary-coded_decimal - Chen–Ho encoding
https://en.wikipedia.org/wiki/Chen%E2%80%93Ho_encoding - Densely packed decimal
https://en.wikipedia.org/wiki/Densely_packed_decimal - A Summary of Chen-Ho Decimal Data encoding
http://speleotrove.com/decimal/chen-ho.html - Art of Assembly language programming: The 80×87 Floating Point Coprocessors
https://courses.engr.illinois.edu/ece390/books/artofasm/CH14/CH14–3.html - Art of Assembly language programming: The FPU Instruction Set
https://courses.engr.illinois.edu/ece390/books/artofasm/CH14/CH14–4.html - INTEL 80387 PROGRAMMER'S REFERENCE MANUAL
http://www.ragestorm.net/downloads/387intel.txt - Floating-Point Formats
http://www.quadibloc.com/comp/cp0201.htm - Floating Point Numbers
https://floating-point-gui.de/formats/fp/ - Float exposed
https://float.exposed/0×40490000 - Float Toy
http://evanw.github.io/float-toy/ - IEEE-754 visualization
https://bartaz.github.io/ieee754-visualization/ - IEEE-754 Floating Point Converter
https://www.h-schmidt.net/FloatConverter/IEEE754.html - IEEE 754 Calculator
http://weitz.de/ieee/ - IEEE-754 Float Calculator
https://mason.cc/float/