Navrhujeme a vyrábíme vlastní CPU: první pokusy s FPGA

4. 3. 2025
Doba čtení: 8 minut

Sdílet

Vývojová deska s FPGA
Autor: Martin Beran
V tomto článku začneme jednoduchými obvody, postavíme digitální budík a nakonec připravíme periferní zařízení pro náš budoucí počítač: VGA výstup, vstup z klávesnice PS/2 a sériový port.

Začínáme s FPGA a VHDL

Z minulého článku máme vybranou literaturu, abychom se mohli seznámit se základy jazyka VHDL: učebnici pro začátečníky Data, čipy, procesory a referenční příručku VHDL Handbook. Pro praktické pokusy máme připravený vývojový FPGA kit s obvodem Cyclone IV EP4CE6 a prostředí pro programování Quartus Prime. Můžeme se tedy pustit do navrhování digitálních logických obvodů. Všechny dále popisované konstrukce jsou dostupné v repozitáři na GitHubu a z textu vedou odkazy na příslušné zdrojové soubory.

Tento seriál nemá za cíl sloužit jako výukový materiál pro jazyk VHDL, proto zde probereme pouze základní pojmy nutné pro pochopení dalšího textu. Zájemci o naučení se jazyka najdou další informace ve výše zmíněných, velmi dobře zpracovaných publikacích. Hned na začátku je třeba zdůraznit, že VHDL (a stejně tak ostatní jazyky pro popis hardwaru) není programovací jazyk. Kód ve VHDL nedefinuje instrukce pro počítač, ale popisuje zapojení elektronického obvodu, chování jeho elementárních částí a jejich propojení.

Logické obvody se rozdělují do dvou skupin, na kombinační a sekvenční. Kombinační obvod (combinatorial logic circuit) se vyznačuje tím, že jeho výstupy závisí pouze na aktuálním stavu vstupů. Typickým jednoduchým příkladem kombinačního obvodu je hradlo realizující některou základní logickou funkci, například OR nebo NAND. Každý logický element v našem FPGA obsahuje kombinační obvod (look-up table, LUT), který lze nakonfigurovat pro počítání libovolné boolovské funkce se čtyřmi vstupy a jedním výstupem. Jejich propojováním vznikají složitější kombinační obvody s více vstupy a výstupy.

Zásadní odlišností sekvenčních obvodů je paměť. Jejich výstup nezávisí jen na vstupech, ale také na předchozím vnitřním stavu obvodu. Například v našem FPGA obsahuje každý logický element jednobitový registr, v němž je možné uložit výstup z LUT. Později uvidíme příklady složitějších sekvenčních obvodů, například paměti, posuvné registry a stavové automaty.

Logické obvody se obvykle navrhují jako synchronní, řízené hodinovým signálem. Naše vývojová deska obsahuje krystalový oscilátor generující signál o frekvenci 50 MHz. Aktuální stav obvodu je definován hodnotami registrů v jednotlivých logických elementech. Z nich se přivádí nuly nebo jedničky na vstupy kombinačních obvodů. Při příchodu vzestupné hrany hodinového signálu (tj. při změně z 0 na 1) se do registrů zapíší nové hodnoty z výstupů kombinačních obvodů. Toto se opakuje stále dokola s každým taktem hodin.

Pro specifikaci obvodů se v jazyce VHDL používá kombinace tří přístupů. První z nich je strukturní, tedy skládání obvodů z jednotlivých částí, v kódu VHDL reprezentovaných jako entity. Každá entita má pojmenované vstupní a výstupní porty, které se propojují (výstupní port jedné entity na vstup druhé) pomocí signálů. Entity se mohou do sebe hierarchicky vnořovat, kdy entita na nejvyšší úrovni reprezentuje celý obvod a obsahuje menší entity pro jednotlivé části obvodu. Složitější části se mohou dále dělit na ještě menší entity.

Druhý způsob popisu obvodu je data flow. Pomocí přiřazení a logických (AND, OR, XOR, NOT, atd.) i jiných (aritmetických, relačních) operátorů se definuje, jak ze vstupních vznikají výstupní signály. Přiřazovací příkazy mohou být i podmíněné (WHEN, SELECT).

Poslední přístup k popisu funkce obvodu je behaviorální. K tomu ve VHDL slouží příkaz PROCESS. Ten popisuje, jak se postupně vstupní signály transformují na výstupy. Mezivýsledky se mohou ukládat do proměnných nebo signálů a existují i konstrukce IF-THEN-ELSE nebo cykly (LOOP). Stále se ale nejedná o zápis algoritmu pro počítač, syntetizér musí být schopen behaviorální popis transformovat do zapojení obvodu. Proto nelze používat např. cykly WHILE nebo cykly FOR, jestliže není předem znám maximální počet kroků. Iterace procesu se spouští na základě podnětu, kterým může být takt hodin nebo změna logické úrovně monitorovaného signálu. Proces může být implementován kombinačním nebo sekvenčním obvodem, podle toho, zda si proces ukládá hodnoty mezi iteracemi do proměnných nebo signálů.

První jednoduché obvody

Jako první ukázku jednoduchého obvodu ve VHDL si ukážeme blikání LED, ovládané pomocí tlačítek start/stop. Překladač nám sdělí, že pro syntézu tohoto obvodu použil celkem 118 logických elementů, 67 registrů a 7 I/O pinů (pro hodiny, dvě tlačítka a čtyři LED). Vývojové prostředí umí nakreslit i schéma výsledného obvodu, buď jako RTL (Register Transfer Level, propojení logických funkcí a registrů), nebo jako technology map (detailní zapojení jednotlivých použitých logických elementů FPGA).

 Logické (RTL) schéma obvodu

 Schéma hardwarové realizace obvodu (technology map)

V gitovém repozitáři jsou další ukázkové obvody o různé složitosti. Za pozornost stojí například:

  • basic_logic – Ukázka logických hradel (elementárních kombinačních obvodů) a několika typů klopných obvodů (elementárních sekvenčních obvodů). Je třeba zdůraznit, že zapojení klopných obvodů používající logická hradla se zpětnou vazbou funguje při realizaci pomocí diskrétních součástek, ale v FPGA se silně nedoporučuje takové konstrukce používat. Pro zapamatování hodnot je vhodné používat registry a do každého zpětnovazebního cyklu by měl být zařazen aspoň jeden registr. Kvůli tomu, že optimalizátor někdy sloučí několik logických hradel do jednoho logického elementu, některá zapojení vůbec nefungují.
  • demo_lib_led – Zahrnuje detekci stisků tlačítek, blikání LED s různými frekvencemi, zobrazování na sedmisegmentovém displeji a generování zvuku.
  • infrared_receiver – Infračervené dálkové ovládání. Vývojová deska obsahuje infračervený přijímač a v balení byl přiložen dálkový ovladač. Tento projekt implementuje příjem, dekódování a zobrazování kódů vysílaných ovladačem při každém stisku tlačítka.

Digitální budík

Na závěr seznamování se základy VHDL a programování FPGA jsem si nechal projekt alarm_clock. Jedná se o digitální budík demonstrující různé techniky, které se nám budou hodit později při implementaci našeho počítače. Základem je entita time_keeping. Ta obsahuje několik čítačů, jeden pro každou číslici reprezentace času ve formátu HH:MM:SS. Čítače jsou řízeny entitou rt_clock, což je dělička kmitočtu převádějící základní hodinový signál 50 MHz na kmitočet 1 Hz. Entita time_keeping se stará také o řízení zobrazování času a aktivaci zvuku budíku.

Další důležitou entitou je control. Obsahuje stavový automat pro ovládání pomocí tlačítek, zahrnující přepínání zobrazení aktuálního času (hodiny a minuty, nebo sekundy) a času buzení, nastavování aktuálního času a času buzení, zapínání a vypínání buzení. Automat je implementovaný pomocí dvou propojených procesů. Proces step se sekvenční logikou provádí přechod do nového stavu při každém taktu hodin. Proces transition s čistě kombinační logikou určuje nový stav na základě aktuálního stavu a hodnot vstupů (ovládacích tlačítek).

Vše je propojeno top-level entitou alarm_clock. Kromě výše zmíněných obsahuje i několik dalších pomocných entit. Ty se starají například o řízení zobrazování na čtyřmístném sedmisegmentovém displeji nebo o generování zvuku.

Budík na FPGA 

Sériová komunikace: RS-232 a PS/2

Pro náš počítač budeme potřebovat nějaké vstupy a výstupy. Použitá vývojová deska obsahuje konektory RS-232 a PS/2. Nabízí se tedy implementovat sériový port a rozhraní pro připojení PS/2 klávesnice. Obě tato rozhraní používají sériovou komunikaci. Rozdíl je v tom, že sériový port má jeden datový vodič pro každý směr, naproti tomu PS/2 používá jeden vodič pro hodinový signál a druhý pro přenos dat v obou směrech.

Při sériové komunikaci se přenáší postupně jednotlivé bity. V počítači chceme pracovat s celými bajty, proto použijeme posuvný registr. Při posílání do registru uložíme bajt určený k odeslání. Řadič sériového rozhraní postupně z registru odebírá jednotlivé bity a vysílá je po datovém vodiči. Na závěr signalizuje pomocí řídicího registru ukončení přenosu a připravenost odeslat další bajt. Naopak příchozí bity řadič postupně ukládá do registru. Po přijetí celého bajtu nastaví příznak v řídicím registru, že je možné přečíst přijatý bajt.

Další důležitou částí sériového rozhraní je hodinový signál, udávající frekvenci odesílání jednotlivých bitů, respektive vzorkování přijímaných bitů. V případě RS-232 si hodiny řídí každá strana samostatně a synchronizují se pomocí start a stop bitů. U PS/2 generuje hodový signál připojené zařízení (klávesnice).

Celé rozhraní je řízeno pomocí stavového automatu. Ten ovládá zahájení a ukončení komunikace, přenos jednotlivých datových bitů, případně i paritního bitu. Stará se také o manipulaci s řídicími a datovými registry, prostřednictvím nichž je rozhraní přístupné ze softwaru.

VGA, RAM, PLL

Na desce s FPGA máme k dispozici i VGA konektor, což nám dovoluje implementovat řadič grafického výstupu. Stejně jako u sériových rozhraní, i zde máme do FPGA připojené jednotlivé datové vodiče a musíme se sami starat o správné generování a časování všech signálů. VGA vyžaduje řídicí signály pro horizontální a vertikální synchronizaci o správné frekvenci. Využijeme pro ně obvod PLL (Phase-Locked Loop), který je dostupný uvnitř našeho FPGA. Zjednodušeně řečeno se jedná o obvod konvertující vstupní signál, v našem případě systémové hodiny o frekvenci 50 MHz, na signály s jinou frekvencí. Pro základní VGA rozlišení 640×480, 60 Hz budeme potřebovat pixelovou frekvenci 25,175 MHz. Z ní pak pomocí čítačů odvodíme signály HSYNC a VSYNC.

Budeme používat rozlišení 640×480 pixelů. Barva jednotlivých pixelů je řízená analogovými signály pro kanály R, G, B. My máme k dispozici pouze jeden bit na každý kanál, což nám umožňuje zobrazit celkem 8 různých barev. Pokud bychom chtěli využít celý potenciál těchto parametrů, potřebovali bychom pro videopaměť 115200 B. I monochromatické zobrazení by zabralo 38400 B, což je více, než celá dostupná statická RAM v našem FPGA o velikosti 30 KiB. Abychom se vešli do paměti a zbyla nám ještě dostatečná kapacita mimo video RAM, zvolíme formát uložení obrazových dat podobný tomu, který byl použit v počítači ZX Spectrum. Obraz má 256×192 pixelů, každý uložený v jednom bitu definujícím, zda má pixel barvu popředí nebo pozadí. Jednotlivé pixely jsou v paměti uloženy souvisle po řádcích, tedy 32 B na každý řádek. Následuje 32×24 bajtů definujících atributy (barvu popředí, barvu pozadí a blikání) pro každý blok 8×8 pixelů. Každý pixel se zobrazuje jako čtvereček 2×2 VGA pixelů. Celý obraz zabere 512×384 VGA pixelů. Okraj okolo je vyplněn jednou barvou. Takto se video paměť vejde do méně než 7 KiB.

V souvislosti se zobrazováním musíme vyřešit ještě jeden problém. Do oblasti paměti použité pro uložení obrazu bude současně přistupovat CPU i řadič VGA. Navíc každý z nich používá jinou frekvenci hodinového signálu. Naštěstí použité FPGA toto řeší za nás, protože interní RAM se dá nakonfigurovat jako dvouportová. To znamená, že stejná paměť je přístupná přes dvě sady adresové a datové sběrnice, řídicích a hodinových signálů. Přes jedno rozhraní může k paměti přistupovat CPU a přes druhé řadič VGA, zcela nezávisle.

hacking_tip

Obsah následujícího dílu

V příštím díle se pustíme do tvorby našeho procesoru. Začneme definicí ISA (Instruction Set Architecture).

(Autorem obrázků je Martin Beran.)

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).