- Zahrnuje RV32I: registry, ABI a řízení toku s eCall.
- Cvičení s Jupiterem a cvičení: záporné číslo, faktory, řetězce, rekurze.
- Hlavní křížový nástrojový řetězec, skript linkování a ladění pomocí objdump.
Pokud vás zajímá assembler a máte pocit, že RISC-V je ta správná cesta, jste na správném místě. Začít s ASM na RISC-V je dostupnější, než se zdá Pokud rozumíte nástrojům, modelu programování a některá klíčová pravidla architektury.
V následujících řádcích jsem zkombinoval to nejlepší z několika zdrojů: cvičení se simulátory typu Jupiter, konvence základního repertoáru RV32I, příklady smyček a rekurze, systémová volání a dokonce i pohled na návrh CPU RISC-V ve VHDL (s ALU, řízením paměti a stavovým automatem), plus přehled skriptů pro navzájem využívající nástroje a propojení.
Co je assembler RISC-V a jak se liší od strojového jazyka?
Ačkoli jsou oba připojeni k technické vybavení, Strojový jazyk je čistě binární (jedničky a nuly) které CPU interpretuje přímo, zatímco assembler používá mnemotechnické pomůcky a symboly čitelnější než assembler, pak se převede do binárního formátu.
RISC-V definuje otevřený ISA s velmi čistým základním repertoárem. Profil RV32I (32bitový) obsahuje 39 uživatelských instrukcí. s pozoruhodnou ortogonalitou, oddělující přístup k paměti od čistého výpočtu, a s vynikající podporou v GCC/LLVM.
Záznamy, dohody a vstupní bod
V RV32I máte 32 registrů pro všeobecné použití (x0–x31) 32bitový; x0 se vždy čte jako 0 a nelze do něj zapisovat. Aliasy jako a0–a7 (argumenty), t0–t6 (dočasné) nebo s0–s11 (uložené) jsou také užitečné pro sledování ABI.
V závislosti na prostředí nebo simulátoru může program začít na určitém návěstí. V Jupiteru programy začínají na globálním tagu __start., který musíte deklarovat jako viditelný (například s .globl) pro označení vstupního bodu.
the tagy končí dvojtečkou, na řádek můžete vložit pouze jednu instrukci a komentáře lze začínat znakem # nebo ;, takže je assembler ignoruje.
Nástroje a simulátory: Jupiter a základní pracovní postup
Abyste mohli cvičit bez komplikací, máte Simulátor/assembler Jupiteru, grafický nástroj inspirovaný programy SPIM/MARS/VENUS, který usnadňuje editaci, sestavování a provádění v jednom prostředí.
V Jupiteru můžete vytvářet, upravovat a mazat soubory na kartě Editor. Po uložení proveďte assemble stisknutím F3 a spusťte ladit tok instrukce po instrukci s využitím pohledů na registry a paměť k pochopení stavu stroje.
Programy musí končit voláním prostředí: nastavení ukončení volání a0 s kódem 10 (ukončení). V RISC-V jsou ecalls ekvivalentní systémovým voláním nebo trapům do prostředí/systému.
Minimální struktura programu a systémová volání
Typická struktura v akademických příkladech definuje výchozí bod, vykonává práci a končí elektronickým zavoláním (ecall). Argumenty eCall obvykle putují v a0–a2 a volič služeb v a7, v závislosti na prostředí.
V jednom Linux Například v RISC-V můžete vypsat pomocí systémového volání write a ukončit s příslušným kódem. Pro zápis se používají a0 (fd), a1 (buffer), a2 (length) a a7 s číslem služby.Nakonec se a0 nastaví na návratový kód a a7 na číslo ukončení.
# Ejemplo mínimo (Linux RISC-V) para escribir y salir
.global _start
_start:
addi a0, x0, 1 # fd = 1 (stdout)
la a1, msg # a1 = &msg
addi a2, x0, 12 # a2 = longitud
addi a7, x0, 64 # write
ecall
addi a0, x0, 0 # return code
addi a7, x0, 93 # exit
ecall
.data
msg: .ascii "Hola mundo\n"
Pokud pracujete mimo Linux, například v vzdělávací simulátory s vlastní službou IoT, změňte číslo elektronického volání a registry dle dokumentace prostředí.
Úvodní cvičení, která vám pomohou seznámit se s podmíněnými výrazy, smyčkami a pamětí
Typickým rozcvičením je zjistit, zda je celé číslo záporné. Můžete vrátit 0, pokud je kladná, a 1, pokud je záporná.; u RV32I řeší problém porovnání s 0 a nastavení na méně než jedinou promyšlenou instrukcí.
Dalším velmi užitečným cvičením je vyjmenovat dělitele čísla: prochází od 1 do n, vypíše dělitele a vrátí, kolik jich byloProcvičíte si podmíněné větvení, dělení (nebo opakované odčítání) a smyčky se sčítáním a porovnáváním.
Práce s řetězci vás nutí spravovat paměť: Navštivte každý znak řetězce v paměti a na místě převeďte malá písmena na velká. pokud se vejdou do rozsahu ASCII. Po dokončení vrátí původní adresu řetězce.
Smyčky, funkce a rekurze: faktoriál, Fibonacciho a Hanojská věž
Při navrhování smyček myslete na tři bloky: podmínku, tělo a krok. S beq/bne/bge a bezpodmínečnými skoky jal/j se sestavují while/for bez záhad, spoléhající se na doplňky a srovnání.
.text
.globl __start
__start:
li t0, 0 # i
li t1, 10 # max
cond:
bge t0, t1, end # si i >= max, salta
# cuerpo: usar a0/a1 segun servicio IO del entorno
addi t0, t0, 1 # i++
j cond
end:
li a0, 10
ecall
Při voláních funkcí respektujte ABI: ušetřete, pokud budete řetězit více hovorů, zachovává s0–s11 , pokud je upravíte, a používá zásobník s sp pohybujícím se v násobcích slova.
Faktoriál je klasická rekurze: základní případ n==0 vrací 1; jinak zavolejte factorial(n-1) a vynásobte n. Chraňte ra a registry uložené na zásobníku před voláním a obnovte je po návratu.
factorial:
beq a0, x0, base
addi sp, sp, -8
sw ra, 4(sp)
sw s0, 0(sp)
mv s0, a0
addi a0, a0, -1
jal factorial
mul a0, a0, s0
lw s0, 0(sp)
lw ra, 4(sp)
addi sp, sp, 8
jr ra
base:
li a0, 1
jr ra
Fibonacci je užitečný pro procvičování obou rekurze se dvěma voláními jako efektivní iterační verzi s akumulátorovými proměnnými. A pokud chcete problém s řízením toku a parametrů, přeložte řešení do assembleru. Hanojské věže se čtyřmi argumenty: disky, zdroj, cíl a pomocná věž; respektuje pořadí volání a zobrazuje každý pohyb.
Přístup k paměti, pole a manipulace s řetězci
V RISC-V se přístup do paměti provádí pomocí příkazů load/store: lw/sw pro slova, lh/sh pro půlslova a lb/sb pro bajty, s variantami v nábojích se znaménkem nebo bez znaménka (lb vs lbu, lh vs lhu).
Pro procházení celočíselných polí použijte 4bajtové offsety na index; pro textové řetězce posouvá se bajt po bajtu, dokud nenajde terminátor pokud to konvence vyžaduje (např. \0). Nezapomeňte ukládat základní adresy a ukazatele opracovávat pomocí addi/auipc/la dle potřeby.
Návrh CPU RV32I od nuly: Základní přehled
Pokud máte chuť se pustit do křemíku, vzdělávací projekt vybuduje CPU RV32I ve VHDL, syntetizovatelné v FPGA Nízký až střední rozsah. Obsahuje programovou ROM, datovou RAM a jednoduchý GPIO pro rozsvícení LED.
Jádro implementuje základní repertoár (bez rozšíření M/A/C nebo CSR), používá 32bitovou adresovou sběrnici a umožňuje 8-/16-/32bitový přístup do paměti s rozšířeným znaménkem, kde je to vhodné. Konstrukce jasně odděluje registry, ALU, řadič paměti a stavový automat.
ALU, posuny a myšlenka „zpožděného načítání“
ALU je popsána kombinačně s operacemi, jako je sčítání, odčítání, XOR, OR, AND, porovnávání (se znaménkem i bez znaménka) a logické/aritmetické posuny.
Pro úsporu LUT v FPGA jsou implementovány vícebitové posuny iterování 1bitových posunů řízených stavovým automatemSpotřebujete sice několik cyklů, ale snížíte spotřebu logických zdrojů.
V synchronních obvodech jsou změny pozorovány na hranách hodinových signálů. Koncept „zpožděného načítání“ připomíná, že to, co je vybráno multiplexorem, ovlivňuje registr v dalším cyklu., což je klíčový aspekt při návrhu stavového automatu typu načtení-dekódování-spuštění.
Řadič paměti a mapa: ROM, RAM a GPIO
Paměťový blok integruje ROM a RAM do souvislého prostoru, zjednodušení rozhraní procesoruŘadič přijímá signály AddressIn (32 bitů), DataIn, šířku signálu (bajt/polovina/slovo), signál rozšíření znaménka, WE (čtení/zápis) a Start pro zahájení transakcí.
Až operace skončí, ReadyOut je nastaveno na 1 a pokud bylo přečteno, DataOut obsahuje data (s rozšířeným znaménkem, když je požadováno). Pokud byla data zapsána, zůstanou v RAM.
Příklad praktické mapy: ROM od 0x0000 do 0x0FFF, bajt GPIO na adrese 0x1000 (bit 0 pinu) a RAM od 0x1001 do 0x1FFFS tímto můžete vytvořit blinkr zápisem a přepínáním výstupního bitu.
Registry, multiplexory a stavové automaty
CPU definuje 32 registrů pro všeobecné použití, jejichž instance jsou vytvořeny pomocí polí ve VHDL, s dekodér vybrat cíl zápisu z ALU a ponechat zbytek.
Multiplexory řídí vstupy ALU (operandy a operace), signály do řadiče paměti (šířky, adresa, řízení spuštění a čtení/zápis) a speciální registry: PC, IR a pomocný čítač pro iterační posuny.
Státní stroj začíná s obnovit, načte instrukci, na kterou ukazuje PC (čtení 4 bajtů), je načten do IR, jakmile je připraven, a předáván prováděcím uzlům: ALU (jedna instrukce v 1 cyklu kromě posunů), načítání/ukládání, větvení a skoky, a to vše navíc ke speciálním instrukcím, jako je ebreak.
Cross-toolchain, propojení a ladění
Pro generování binárních souborů RV32I použijte Cross GCC (cíl riscv32-none-elf)Zkompilujete zdrojové kódy C/C++/ASM, propojíte je se skriptem, který definuje mapu paměti, a převedete výstup do podoby, kterou vaše ROM/FPGA očekává.
Jednoduchý hook skript může umístit .text v ROM od 0x0000 a .data v RAM od 0x1004 (pokud jsou 0x1000–0x1003 obsazeny registry GPIO). Spouštěcí rutina může být „nahá“ a umístit ukazatel zásobníku na konci RAM (např. 0x1FFC) před voláním main.
/* Mapa simple
* ROM: 0x00000000 - 0x00000FFF
* GPIO: 0x00001000 - 0x00001003
* RAM: 0x00001004 - 0x00001FFF
*/
SECTIONS {
. = 0x00000000;
.text : { *(.startup) *(.text) *(.text.*) *(.rodata*) }
. = 0x00001004;
.data : { *(.data) *(.data.*) }
}
S riscv32-none-elf-objdump můžete rozeberte ELF a zkontrolujte adresynapříklad uvidíte, bota na adrese 0x00000000 s instrukcemi jako lui/addi/jal a přechodem do hlavního adresáře. Pro simulaci VHDL generuje GHDL stopy, které můžete otevřít pomocí GtkWave.
Po ověření v simulaci přeneste návrh do FPGA (Quartus nebo jiného toolchainu). Pokud je RAM odvozena jako interní bloky a kód je jasný RTL, měli byste syntetizovat bez překvapení, a to i na zkušených zařízeních.
Praktické připomínky a typické chyby při začátcích
Nezapomeň na to x0 je vždy nula; zápis do něj nemá žádný účinek a čtení vrací 0. Využijte to ve svůj prospěch při sčítání, porovnávání a čištění registru.
Když implementujete funkce, ukládat upravené záznamy ra a sNa spravuje zásobník pomocí sčítání/odčítání zarovnaných podle slov do sp. Po návratu obnovuje v obráceném pořadí a skáče s jr ra.
V simulátorech jako Jupiter to zkontrolujte __start je globální a ukončíte ho pomocí ecall správně (a0=10 pro ukončení). Pokud se něco nespustí, zkontrolujte návěští, globalitu a znovu zkompilujte (F3).
V cvičeních s IO, respektovat protokol prostředí: které registry nesou parametry, číslo služby a zda se očekává adresa nebo okamžitá hodnota. Použijte dokumentaci k simulátoru nebo operačnímu systému.
Díky přehledné základně ISA (RV32I, registry a ABI), pohodlnému simulátoru jako Jupiter a rostoucímu počtu příkladů (záporné, faktory, velká písmena, smyčky, faktoriál, Fibonacci a Hanoi) přestává být assembler RISC-V zdí a stává se šťavnatým terénem pro pochopení myšlení CPU. A pokud se odvážíte přejít na VHDL, uvidíte, jak do sebe zapadá ALU, paměť a řízení.od načítání instrukcí a líného načítání až po paměťová rozhraní a mapu s ROM, RAM a GPIO, která vám umožňuje blikat LED diodou pomocí vašeho vlastního procesoru.
Vášnivý spisovatel o světě bytů a technologií obecně. Rád sdílím své znalosti prostřednictvím psaní, a to je to, co budu dělat v tomto blogu, ukážu vám všechny nejzajímavější věci o gadgetech, softwaru, hardwaru, technologických trendech a dalších. Mým cílem je pomoci vám orientovat se v digitálním světě jednoduchým a zábavným způsobem.