Navrhujeme a vyrábíme vlastní CPU: architektura instrukční sady

18. 3. 2025
Doba čtení: 5 minut

Sdílet

Vývojová deska s FPGA
Autor: Martin Beran
Dnešní díl série o cestě k vlastnímu procesoru věnujeme návrhu ISA, tedy tomu, jak bude CPU vypadat z pohledu programátora v assembleru, kolik bude mít registrů, a jak budou vypadat instrukce.

Jméno a repozitář

Úplně na začátku si náš budoucí výtvor pojmenujeme. Počítače a jejich komponenty častou nesou názvy sestávající z kryptických shluků písmen a číslic. Já jsem se rozhodl dodržet tuto tradici a celý počítač jsem pojmenoval MB50. Samotné CPU pak MB5016.

Význam těchto zkratek nechám na fantazii čtenáře. Kompletní zdrojové kódy CPU a počítače (ve VHDL), assembleru a debuggeru (v C++) a ukázkových programů (v assembleru MB5016) jsou na GitHubu.

Cíle a omezení ISA

Nejprve si ujasníme, co od našeho CPU očekáváme a jaká máme omezení při návrhu a implementaci. Základní limit je velikost dostupného FPGA, do něhož se musí celý počítač vejít. Z celkového počtu 6272 logických elementů jsme zatím spotřebovali přibližně 700 na implementaci řadičů klávesnice, obrazovky a sériového portu. Zbývá nám kolem 5500 logických elementů a 30 KiB statické paměti RAM.

Pro člověka, který něco takového dosud nedělal, je cíl postavit úplně nový procesor docela ambiciózní sám o sobě, proto si ho nebudeme ještě zbytečně komplikovat. Budeme tedy naše úsilí směřovat k relativně jednoduchému CPU, který bude sekvenčně číst a provádět instrukce strojového kódu. Naším cílem není extrémní výkon, proto vynecháme pokročilé vlastnosti a optimalizace známé z moderních procesorů, jako jsou několikaúrovňové cache, pipelining, predikce skoků, out-of-order execution nebo více procesorových jader.

Ve fázi zkoušení hotového počítače by bylo užitečné, aby se pro náš procesor dal relativně snadno psát kód. Nebudeme se zabývat implementací nějakého vyššího programovacího jazyka, například C, proto potřebujeme instrukční sadu, která se bude dobře používat v assembleru. Zároveň chceme, abychom mohli snadno implementovat i samotný assembler.

Velikost slova a instrukcí, registry

První důležité rozhodnutí při návrhu ISA je velikost slova, tedy počet bitů, se kterými se bude pracovat jako s jednou hodnotou. Nebudeme zkoušet žádné exotické varianty a budeme se držet klasických mocnin dvou. Vzhledem k tomu, že máme 30 KiB paměti, potřebujeme aspoň 16 bitů na adresu (opravdu nechceme 15).

U 32bitového procesoru už hrozí riziko, že by mohl být pro cílové FPGA příliš velký. Proto zvolíme 16 bitů jako velikost slova, a tím i velikost každého registru. Pro jednoduchost definujeme i všechny instrukce o délce 16 bitů. Na kódování textu budeme používat osmibitové znaky v kódování ASCII, protože Unicode je pro takto malý počítač příliš složité. Z toho důvodu potřebujeme mít možnosti pracovat i s jednotlivými bajty, proto pro datovou sběrnici paměti použijeme šířku 8 bitů a budeme adresovat po bajtech.

Podle vzoru RISCových procesorů bude náš procesor vybaven relativně velkým počtem univerzálních registrů. Jako rozumný počet registrů vypadá číslo 16, což se vejde do 4 bitů. Registry „nápaditě“ pojmenujeme r0r15. Instrukce pak budou mít jednotný formát, sestávající z jednoho bajtu pro opcode a druhého bajtu se dvěma čísly registrů. Některé registry mají speciální význam:

  • r15 (pc) je program counter, obsahující adresu následující instrukce.
  • r14 (f) je příznakový registr (flags). Obsahuje čtyři bity (Zero, Carry, Sign a Overflow) nastavované aritmetickými a logickými instrukcemi a čtyři bity s nedefinovaným významem, které je možné nastavovat programově. Všech osm bitů lze testovat pomocí podmíněných instrukcí. Dále registr obsahuje bit pro povolení přerušení a indikátory čekajících přerušení.
  • r13 (ia) obsahuje adresu rutiny pro obsluhu přerušení (interrupt address).
  • r12 (ca) se používá pro uložení návratové adresy (call address) při volání podprogramu.
  • r11 (sp) ukazuje na hodnotu na vrcholu zásobníku (stack pointer).

Speciální význam registrů pc, f a ia je definován v hardwaru. Registry ca a sp jsou definovány konvencí a software může pro stejný účel použít i libovolné jiné registry, a naopak tyto dva použít jako obecné registry. Existence testovatelných příznaků bez pevně daného významu f0, f1, f2, f3 je experiment, kterým se MB5016 liší od jiných běžně používaných procesorů: x86 a ARM mají příznaky s pevně definovaným významem, naopak RISC-V příznakový registr nemá. Příklad nastavení a testování těchto příznaků je v ovladači klávesnice.

Podle vzoru RISCových procesorů se při volání podprogramu návratová adresa neukládá na zásobník, ale do registru ca, alternativně do libovolného jiného registru. Pokud podprogram potřebuje sám provést volání dalšího podprogramu, musí obsah registru uložit na zásobník a před návratem obnovit softwarově. Naopak rutina, která nic dalšího nevolá, ušetří několik taktů vynecháním zápisu a čtení paměti.

Kromě obecných registrů používaných pro výpočty a přístup k paměti existuje ještě 16 řídicích registrů (CSRs, Control and Status Registers). Tyto registry mají pevně definovaný význam a možné operace jsou omezeny na kopírování hodnot z a do obecných registrů. V registru csr0 procesor indikuje příčinu poslední výjimky, například přečtení neznámého kódu instrukce, a rozlišení, zda byla výjimka nebo přerušení vygenerováno hardwarově nebo softwarově nastavením bitu v registru f. Registr csr1 slouží pro uložení adresy obslužné rutiny přerušení. Registry csr2, csr3 se používají jako pomocné místo při ukládání obsahu registrů do paměti při vstupu do obsluhy přerušení nebo do podprogramu. Ostatní řídicí registry csr4csr15 se nepoužívají a mají vždy nulovou hodnotu.

Při zapnutí nebo resetu procesoru jsou všechny registry nastavené na nulovou hodnotu. Běh programu tedy začíná na adrese 0×0000 a se zakázaným přerušením.

Instrukční sada

Množina dostupných instrukcí také vychází z RISCové filozofie malého počtu relativně jednoduchých instrukcí. Všechny aritmetické a logické instrukce pracují s hodnotami v registrech. Jestliže je některý operand uložený v paměti, je potřeba jej nejprve načíst instrukcí LD (load) do registru. Naopak výsledek se do paměti zapíše instrukcí STO (store).

Všechny instrukce mají stejný formát o délce dva bajty. Jak instrukce, tak data se do paměti ukládají jako little endian, tedy první je nižší bajt. První bajt instrukce obsahuje instrukční kód (opcode, specifikující prováděnou operaci), ve druhém bajtu jsou dvě čísla registrů. V assemblerovém zápisu instrukce je vždy první cílový a druhý zdrojový registr. Binární operace používají cílový registr jako první a zdrojový jako druhý operand.

Nepodmíněné instrukce mají nulový nejvyšší bit a výběr operace ve zbylých 7 bitech opcode. Podmíněné instrukce mají opcode rozdělený na čtyři části. Nejvyšší bit je jedničkový, 3 bity definují prováděnou operaci, 3 bity vybírají testovaný příznakový bit (číslo bitu v nižším bajtu příznakového registru) a 1 bit obsahuje očekávanou hodnotu příznaku.

+---------+---------+---+---------------+
| 1 1 1 1 | 1 1 0 0 | 0 | 0 0 0 0 0 0 0 | Bits
| 5 4 3 2 | 1 0 9 8 | 7 | 6 5 4 3 2 1 0 |
+---------+---------+---+---------------+
|   DSTR  |   SRCR  |      OPCODE       | Meaning
+---------+---------+---+---------------+
|   DSTR  |   SRCR  | 0 |      OP       |
+---------+---------+---+---------------+

Formát nepodmíněných instrukcí

+---------+---------+---+-------+---+-------+
| 1 1 1 1 | 1 1 0 0 | 0 | 0 0 0 | 0 | 0 0 0 | Bits
| 5 4 3 2 | 1 0 9 8 | 7 | 6 5 4 | 3 | 2 1 0 |
+---------+---------+---+-------+---+-------+
|   DSTR  |   SRCR  |          OPCODE       | Meaning
+---------+---------+---+-------------------+
|   DSTR  |   SRCR  | 1 |   OP  |   COND    |
+---------+---------+---+-------+---+-------+
|   DSTR  |   SRCR  | 1 |   OP  | V | FLAG  |
+---------+---------+---+-------+---+-------+

Formát podmíněných instrukcí

Obsah následujícího dílu

V příštím díle dokončíme návrh ISA. Probereme jednotlivé druhy instrukcí a podíváme se na obsluhu přerušení.

Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

Vystudoval informatiku na MFF UK v Praze, kde následně několik let učil programování v Unixu. Poté se dlouhá léta věnoval síťové bezpečnosti a programování firewallů. V současnosti se zabývá vývojem interních backendových systémů ve společnosti Gen (dříve Avast).