ˇ ENI´ TECHNICKE´ V BRNEˇ VYSOKE´ UC BRNO UNIVERSITY OF TECHNOLOGY
ˇ NI´CH TECHNOLOGII´ FAKULTA INFORMAC ˇ ´ITAC ˇ OVY´CH SYSTE´MU ˚ ´ STAV POC U FACULTY OF INFORMATION TECHNOLOGY DEPARTMENT OF COMPUTER SYSTEMS
IMPLEMENTACE OBECNE´HO VLIW PROCESORU V FPGA
´ PRA ´ CE DIPLOMOVA MASTER’S THESIS
´ CE AUTOR PRA AUTHOR
BRNO 2011
ˇ NA Bc. PETR KUBE
ˇ ENI´ TECHNICKE´ V BRNE ˇ VYSOKE´ UC BRNO UNIVERSITY OF TECHNOLOGY
ˇ NI´CH TECHNOLOGII´ FAKULTA INFORMAC ˇ ´ITAC ˇ OVY´CH SYSTE´MU ˚ ´ STAV POC U FACULTY OF INFORMATION TECHNOLOGY DEPARTMENT OF COMPUTER SYSTEMS
IMPLEMENTACE OBECNE´HO VLIW PROCESORU V FPGA IMPLEMENTATION OF GENERIC VLIW PROCESSOR IN FPGA
´ PRA ´ CE DIPLOMOVA MASTER’S THESIS
´ CE AUTOR PRA
ˇ NA Bc. PETR KUBE
AUTHOR
´ CE VEDOUCI´ PRA SUPERVISOR
BRNO 2011
´R Ing. ADAM HUSA
Abstrakt VLIW procesory jsou paralelní výpočetní zařízení používaná v oblastech od vestavěných zařízení po servery. Práce obsahuje popis jejich architektury. Hlavním zaměřením je návrh a následně tvorba vlastního obecného VLIW procesoru s rozsáhlými možnostmi konfigurace. Nedílnou součástí je funkční implementace takového procesoru v jazyce VHDL, kterou je možné vyzkoušet na platformě FITkit.
Abstract VLIW processors are parallel computing devices that are used in embedded devices as well as in servers. My thesis contains description of this architecture. It is aimed at making and subsequently implementing design of custom general-purpose VLIW processor with wide range of configurable parameters. Operational implementation of such processor in VHDL which can be tested on FITkit platform is an integral part.
Klíčová slova VLIW procesor, VHDL, FPGA, zřetězení, FITkit
Keywords VLIW Processor, VHDL, FPGA, pipelinening, FITkit
Citace Petr Kuběna: Implementace obecného VLIW procesoru v FPGA, diplomová práce, Brno, FIT VUT v Brně, 2011
Implementace obecného VLIW procesoru v FPGA Prohlášení Prohlašuji, že jsem tuto bakalářskou práci vypracoval samostatně pod vedením pana Ing. Adama Husára. ....................... Petr Kuběna 24. května 2011
Poděkování Rád bych tímto poděkoval vedoucímu práce Ing. Adamu Husárovi za vhodné směrování, dobré rady a velkou dávku trpělivosti, kterými oplýval v průběhu tvorby diplomového projektu.
c Petr Kuběna, 2011.
Tato práce vznikla jako školní dílo na Vysokém učení technickém v Brně, Fakultě informačních technologií. Práce je chráněna autorským zákonem a její užití bez udělení oprávnění autorem je nezákonné, s výjimkou zákonem definovaných případů.
Obsah 1 Úvod 1.1 Použité zdroje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Používané konvence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3 Struktura práce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3 5 5 5
2 Architektury procesorů, FPGA 2.1 Popis architektur . . . . . . . . 2.2 Vestavěné systémy . . . . . . . 2.3 ARM, MIPS, 80x86 a další . . 2.4 Hradlová pole (FPGA) . . . . .
. . . .
6 6 7 8 9
3 Popis architektury VLIW 3.1 Pohled na architekturu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 ILP a porovnání se superskalárními procesory . . . . . . . . . . . . . . . . . 3.3 VEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11 11 13 14
4 Návrh 4.1 Základní struktura procesoru 4.2 Řízení procesoru . . . . . . . 4.3 Instrukce a jejich generování . 4.4 Forwarding . . . . . . . . . . 4.5 Použité prostředky . . . . . . 4.5.1 VHDL . . . . . . . . . 4.5.2 FITkit a QDevKit . .
. . . . . . .
15 15 16 16 20 20 20 21
. . . . . . . . . . . .
23 24 24 25 27 27 28 30 30 31 31 32 32
. . . . . . .
. . . .
. . . . . . .
. . . .
. . . . . . .
5 Implementovaná architektura a její 5.1 Pohled shora . . . . . . . . . . . . 5.2 Změny konfigurace procesoru . . . 5.3 Použitá konfigurace . . . . . . . . . 5.4 FETCH . . . . . . . . . . . . . . . 5.5 DECODE . . . . . . . . . . . . . . 5.6 READ . . . . . . . . . . . . . . . . 5.7 EXECUTE . . . . . . . . . . . . . 5.7.1 ALU . . . . . . . . . . . . . 5.7.2 MEM . . . . . . . . . . . . 5.7.3 BRANCH . . . . . . . . . . 5.7.4 SPEC . . . . . . . . . . . . 5.8 WRITE . . . . . . . . . . . . . . .
. . . .
. . . . . . .
. . . .
. . . . . . .
. . . .
. . . . . . .
. . . .
. . . . . . .
popis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
. . . .
. . . . . . .
. . . . . . . . . . . .
5.9 Paměti a registry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.10 Přidávání operací . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Testování a návrh dalšího směřování 6.1 Simulace . . . . . . . . . . . . . . . . 6.2 Simulace a praktické testy . . . . . . 6.3 Časování a spotřebované zdroje . . . 6.4 Směry dalšího vývoje . . . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
32 33 35 35 38 39 39
7 Závěr
41
A Seznam instrukcí VEXu
44
B Operace vybrané k implementaci
47
C Simulace
50
D Schémata
54
2
Kapitola 1
Úvod Architektura VLIW (Very Long Instruction Word, tedy procesor s velmi dlouhým instrukčním slovem) vznikla až v osmdesátých letech - tedy relativně pozdě. Přesto se neobjevila z ničeho nic. Naopak k ní vedlo několik cest. O jedné hardwarově založené cestě si můžete přečíst v následujících odstavcích. Je zajímavé sledovat vývoj počítačů. Od prvních pokusů nulté generace, které často byly elektromechanické, nebo z pohledu výpočetního modelu nebyly turingovsky úplné. Přes první generaci, která dovedla elektronky k dokonalosti“. Nebo druhou generaci, jež ” představila tranzistory. Ale teprve počátkem šedesátých let, po objevu integrovaného obvodu (a tedy s třetí generací počítačů) přichází mikroprocesory. Rychlé zvyšování úrovně integrace, jež bylo později formulováno ve známém Moorově zákonu, podnítilo hledání metod pro co nejlepší využití přibývajícíh tranzistorů. Proto není náhoda, že právě do šedesátých let spadají počátky technik, které dnes souhrně nazýváme ILP (Instruction Level Parallelism, tedy paralelismus na úrovni instrukcí). Jako první se objevil projekt Illinoiské univerzity ILLIAC II, který představil řetězení instrukcí. Projekt IBM Stretch sice odstartoval o něco později, ale funkční počítač představil ve stejné době jako ILLIAC II. Navíc už uměl nejen řetězit vykonávání instrukcí, ale i vykonávání mimo pořadí. Přestože IBM Stretch byl nakonec považován za neúspěšný, jednalo se v letech 1961 až 1964 o nejvýkonnější počítač světa s obrovským technologickým přínosem. CDC 6600, který vystřídal IBM Stretch na pozici nejvýkonnějšího počítače, pro změnu přinesl superskalární architekturu - tedy vykonávání několika instrukcí zároveň. Z poloviny šedesátých let pochází i IBM System/360 (Model 91), jenž taky ve své FPU implementoval vykonávání mimo pořadí, navíc ale k tomu využíval i přejmenovávání registrů. Je zajímavé, že snahy o tvorbu zcela obecného přístupu k ILP vyhasly po sérií testů, které se zaměřily na množství instrukčního paralelismu v programech. Ty sice ukázaly skoro neomezené možnosti škálování, ale neexistovaly metody, které by v hardwaru dokázaly ve větším množství ILP využít. Dalším krokem byly vektorové procesory. První pokus sice byl už na začátku šedesátých let, funkční byl teprve až ILLIAC IV z roku 1972. Ten měl 64 paralelně pracujících aritmeticko-logických jednotek. Podobně jako už dříve, ani tento první pokus nebyl úspěšný (dosahoval jen 1/8 původně plánovaného výkonu). Přesto se nejednalo o slepou uličku a v různých formách se tento přístup používá dodnes. Poslední důležitou technikou, která předznamenala příchod VLIW byly práce na horizontálním mikrokódu. Užívá se k emulaci složitějších CISC instrukcí a do značné míry se podobá práci s VLIW - je třeba u nich předem plánovat operace jednotlivých částí procesoru. Práce zabývající se horizontálním mikrokódem ve velké míře probíhaly na konci 3
Obrázek 1.1: Příjmy z trhu serverových procesorů podle IDC WorldWide Server Tracker. Převzato z [2].
sedmdesátých let. Idea VLIW procesoru se objevila počátkem let osmdesátých. Joseph A. Fisher roku 1983 vydal zakládající práci Very Long Instruction Word architectures and the ELI-512 [4]. Brzo vzniklo několik společností (Multiflow, Cydrome) usilujících o vznik počítačů založených na VLIW architektuře (možná přesněji filozofii návrhu, jak uvádí [5]). To se podařilo roku 1988, když Multiflow vydalo řadu mini superpočítačů TRACE. Obě firmy ale brzo skončily. Přes komerční neúspěch si potenciálu všimlo několik velkých firem. Z těch největších například Intel vydal roku 1989 procesor i860, ale taky bez úspěchu. Technologii si licencovalo i IBM a roku 1989 začalo pracovat na vlastní implementaci. Nová architektura byla později nazvána EPIC (Explicitly Parallel Instruction Computing). Roku 1994 se HP spojilo ve své snaze s Intelem a k týmu se přidal Joseph Fisher a Ramakrishna Rau (hlavní architekt ze Cydrome). Naděje byly velké. Čekalo se, že systém obsadí servery a dostane se i do domácích počítačů. I proto byla zahrnuta emulace x86. Bohužel projekt nabral proti původním plánům tříleté zpoždění a první procesor Itanium se objevil až roku 2001. V té době už nijak nepřevyšoval konkurenci, a proto nikdy neobsadil větší část trhu co se týká počtu prodaných procesorů. Přestože se očekává, že Intel postupně utlumí výrobu procesorů Itanium, ve čtvrtém čtvrtletí roku 2010 IDC WorldWide Server Tracker odhadoval příjmy z těchto procesorů na čtyři miliardy dolarů, což bylo skoro osm procent z celkových příjmů z trhu serverových procesorů (viz obrázek 1.1). Jedním z největších úspěchů VLIW bylo, když roku 2000 vydala společnost Transmeta procesor Crusoe. Jednalo se o VLIW procesor schopný vykonávat x86 instrukce. Toho bylo dosaženo pomocí Code Morphing Software, který za běhu překládal x86 instrukce do nativního kódu procesoru. Navíc při opakovaném běhu byl schopný dále optimalizovat přeložený kód. Procesor měl velmi nízký příkon, a proto se hodil do přenosných počítačů. Bohužel výkon byl výrazně nižší než u konkurence a to obzvláště v reálném nasazení, kde se narozdíl od benchmarků neprovádí opakovaně malý kus kódu. Naopak v benchmarcích je Efficeon (nástupce Crusoe), vyrobený 90nm procesem, dokonce po všech stránkách srovnatelný s Intel 4
Atom na 45nm a to včetně příkonu!
1.1
Použité zdroje
Informace prezentované v této práci pocházejí především z pěti zdrojů. V první řadě je to semestrální práce, na níž tato diplomová práce navazuje [14]. Z ní jsou jen s menšími úpravami převzaty tři kapitoly z počátku práce - tedy úvod (1), obecný popis architektury VLIW (3) a návrh (4). Obecné informace o různých architekturách a přístupech poskytla známá publikace J.L.Hennesyho a D.A.Pattersona Computer Architecture - A Quantitative Approach [8]. Předmět Architektura procesorů vedený prof. Ing. Václavem Dvořákem DrSc. poskytl shrnující informace o procesorech a technikách v nich využitých. Jmenované informace lze nalézt ve studijních textech pro tento předmět, především pak ve studijní opoře [3]. Specifika VLIW procesorů dále dopodrobna popsal A.J.Fisher v knize Embedded Computing: A VLIW Approach to Architecture, Compilers and Tools [5]. Z pohledu této práce se jednalo o základní zdroj. Při studiu a vytváření byly samozřejmě použity i další zdroje. Ty se ale povětšinou vztahovaly jen k malým oblastem a budou citovány přímo v textu.
1.2
Používané konvence
Materie zpracovávaná touto prací je v první řadě technického charakteru a pracuje s velkým množstvím termínů, které nemusí mít český ekvivalent anebo mají takový, jež není zaužívaný. Přesto zde pro lepší použití v textu bude snaha o překlad. V takovém případě bude alespoň při prvním výskytu následovat v závorce originální název. Většina názvů pochází z angličtiny a proto je její znalost vhodným předpokladem pro pochopení textu.
1.3
Struktura práce
V samotném úvodu jste si mohli přečíst stručnou historii VLIW procesorů a důležitějších převážně hardwarových technologií, které k nim vedly. V jednotlivých podkapitolách první kapitoly jsou vypsány použité zdroje. Příští kapitola obsahuje tematiku okolo architektury procesorů se zaměřením na vestavěné systémy. Je v ní popsáno rozdělení architektur a v závěru je pohled na technologii hradlových polí. V třetí kapitola je zpracována látka obecného popisu architektury VLIW a technik s ní spjatými. Konkrétně v ní naleznete porovnání instrukčního paralelismu využívaného ve VLIW vzhledem k superskalárním procesorům. Kapitola čtvrtá uvádí vlastní návrh procesoru - jeho části, instrukční slovo, modularitu atp. Dále je v ní načrtnut směr, jímž se má dále při vývoji postupovat. V posledních dvou podkapitolách jsou krátce popsány nástroje použité při realizaci projektu. Následující kapitola (pátá) podrobně popisuje implementaci navrženého procesoru ve VHDL. Nejdřívé je v ní pohled na procesor shora a následně se prochází a pitvají jeho části. V závěru jsou uvedeny a vysvětleny způsoby, jakými je možné procesor upravovat a to včetně doplnění vlastních operací do jeho repertoáru. Šestá kapitola se zabývá testováním výsledné implementace, uvádí, čeho bylo dosaženo. Dále se zde nachází navržené úpravy a vylepšení, které by bylo vhodné, případně zajímavé, do budoucna udělat. Závěrečná část shrnuje cíle a uvádí, kterých bylo dosaženo. 5
Kapitola 2
Architektury procesorů, FPGA V současnosti existuje velké množství architektur procesorů a pokud se podíváme dostatečně podrobně, u každého nového procesoru bychom mohli říct, že má svou vlastní architekturu. Takto detailní pohled ale není vhodný pro jakékoukoliv kategorizaci. Proto bude v první podkapitole představeno dělení architektury procesorů, jak jej uvádí odborná literatura. Po stručném seznámení se s pojmem vestavěný systém si následující část vybere nejpoužívanější druhy ISA (instruction set architecture, viz kapitola 2.1), uvede jejich užití a zařadí je do představeného schématu dělení. Popis architektury VLIW stojí v centru práce, a proto je mu vyhrazena samostatná kapitola 3. V závěru bude krátký vhled do programovatelných hradlových polí FPGA (field-programmable gate array).
2.1
Popis architektur
Většina popisů architektury procesorů vychází z vlastností jejich ISA (Instruction Set Architecture, doslovně Architektura instrukční sady“). ISA je sada definovaných instrukcí, ” jež je viditelná programátorovi a slouží jako rozhraní mezi hardwarem a softwarem. A je i logické, že ve chvíli kdy je definována instrukční sada, jsou technické prvky jako šířka datové sběrnice, způsob přístupu k paměti a jiné přesně dány. I když existují i výjimky, jakou je dříve zmíněný procesor Transmetta Crusoe, který neimplementoval přímo výslednou ISA, ale využil hardwarově asistované emulace, aby umožnil běh aplikací 80x86. Jedno z tradičních dělení rozlišuje procesory na RISC (Reduced Instruction Set Computer ) a CISC (Complete Instruction Set Architecture). Historicky starší je CISC. V šedesátých a sedmdesátých letech snahy o zvýšování výkonu vedly k vytváření čím dál komplexnějších instrukcí. Roku 1974 přišel John Cocke pracující pro IBM Research s nápadem implementovat pouze omezený počet instrukcí. Bylo to postaveno na myšlence, že skutečně je využíváno pouze 20% instrukcí, zbylých 80% leží ladem. [13] Z toho vychází i základní rozdíly v kódu - ten mají RISC procesory výrazně delší, zvládají ale zpracovávat instrukce v jediném taktu. Typická je pro ně LOAD-STORE architektura (přístup k paměti pouze přes specializované operace) a ostatní instrukce přistupují pouze k registrům. Z toho pro změnu vyplývá požadavek na větší pole registrů. V dnešní době se rozdíly mezi RISC a CISC do značné míry smazaly. Pokud už se někdo pokouší takto dělit současné procesory, používá se většinou označení post-RISC a postCISC. Podrobnější, multikriteriální dělení architektur podle ISA je uvedeno v [8] (str. 8-11). Vychází z pohledu na jednotlivosti, kterými jsou:
6
Třída ISA Rozlišujeme dva typy - architektury s registry pro obecné použití (generalpurpose register architecture) a bez nich (tzn. architektury jen se specializovanými registry). Snad všechny dnes používané jsou výhradně prvního typu. Ten dále dělíme na load-store a register-memory. Adresování paměti Většina architektur využívá adresování po bajtech. Do adresování paměti také spadá schopnost (nebo neschopnost) architektury číst nezarovnané (unaligned ) objekty (zarovnaný objekt se nachází na adrese, která po vydělení velikostí objektu dá zbytek nula). Módy adresování Jedná se o způsoby, jakými se provádí výpočet umístění objektu. Typy a velikosti operandů Může se jednat o různé délky operandů - nejčastěji se jedná o mocniny dvou počínaje osmi bity (můžou být ale i kratší). Pro vědecké výpočty jsou potřebné operandy s délkou 64 bitů i více. Typ operandu určuje způsob interpretace obsažené hodnoty - znak (character ), celé číslo (integer ), aproximace reálných čísel (floating point). Operace Tento znak sleduje, které ze čtyř základních typů operací architektura podporuje - tedy aritmetické a logické, aritmetické operace s plovoucí desetinnou čárkou, operace řízení toku (control ) a operace pro přenos dat (data transfer ; zde spadají zápis a čtení z pamětí a registrů). Instrukce řízení toku Jsou už zmíněné v předchozím bodu, ale s větším ohledem na konkrétní instrukce a jejich implementaci. Sleduje se podpora podmíněného i nepodmíněného větvení, skoků a volání procedur. Kódování ISA Základní rozdělení sleduje, zda ISA určuje pevnou délku instrukce (fixed length), nebo proměnnou délkou (variable length). Zatímco pevná délka usnadňuje dekódování a je typická pro RISC procesory, proměnlivá umožňuje efektivnější využití instrukční paměti a snadnější přidávání nových (i nestandardních) instrukcí.
2.2
Vestavěné systémy
Na začátku této kapitoly je vhodné definovat, co pojem vestavěný systém (v originále embedded system) znamená. Mohlo by se zdát, že jde udělat jasné rozdělení mezi vestavěnými systémy a systémy pro obecné použití (general-purpose systems). Přestože ve většině případů lze snadno určit, do které kategorie zařízení spadá, najdou se i případy zařízení, jenž stojí na pomezí. Příkladem jsou mobilní telefony. Tradičně se jednalo o vestavěné systémy. Jak ale přibývaly nové funkce a stávaly se chytřejšími, došlo k jejich přeměně na v podstatě plnohodnotné počítače. Ve které chvíli už je nemůžeme označit za vestavěné systémy? Nebo se stále ještě jedná o vestavěné systémy? Lepší by mohlo být nedívat se na vestavěné systémy a systémy pro obecné použití jako na kategorie, ale brát je za póly jediné spojité veličiny. Pro potřeby této práce přesto nějakou definici potřebujeme a za vestavěný systém bude po vzoru [5] považován takový, který nebyl navržen pro obecné použití. Může se to zdát jako definice kruhem, přesto jen stěží můžeme najít jinou, dostatečně obecnou a platnou definici. Většina vestavěných systémů je úzce spojena s funkcí, kterou vykonává. Jejich uživatelé často ani netuší, že se uvnitř skrývá procesor, který spouští program. Častým znakem bývá 7
použití jediného programu, nebo jen malé skupiny rutin. Absence univerzálnosti je jeden ze znaků spojujících vestavěné systémy. Nemůžeme ale říct, že žádný z vestavěných systémů není schopen vykonávat jiné účely, pouze k tomu nebyl určen. Podobný způsob dělení se zaměřuje na typ výpočetního jádra (opět podle [5]). Dělení je podrobnější a obsahuje čtyři typy: Procesory pro obecné použití jsou typicky používané jako centrální jednotky stolních počítačů, serverů apod. Převládající ISA architekturou je x86. Pomalu se tady tlačí procesory rodiny ARM. Vestavěné obecné procesory jsou většinou slabší. Převládající architekturou dnes už je ARM. DSP procesory se podobají předchozím, jsou ale zaměřeny na operace náročné na aritmetický výkon (především zpracování obrazu a zvuku). Mikrokontroléry jsou malá jádra, která najdeme například ve spotřební elektronice a výbavě domácnosti (pračky, ledničky apod.).
2.3
ARM, MIPS, 80x86 a další
Zatímco v segmentu stolních počítačů má architektura 80x86 prakticky monopol, u vestavěných systémů exituje při výběru velká volnost. Ponechme stranou případy, kdy pro zvolený účel stačí základní mikrokontrolér, nebo specializovaný DSP procesor. Jednou z nejrozšířenějších architektur ve vestavěných systémech je ARM (zkratka pro Advanced RISC Machines 1 ). Z pohledu kriterií uvedených v předchozí kapitole to je 32bitová load-store ISA s uniformním souborem registrů. V současnosti má už podporu čtení nezarovnaných dat (použitím se ale ztrácí kompatibilita se staršími procesory). Je zde podpora pro všechny čtyři základní typy operací. Instrukce mají pevnou délku. Firma ARM Holdings nicméně neprodává výsledné procesory. Zabývá se pouze jejich vývojem a výstup prodává ve formě IP cores (Intelectual Property Cores). IP core je funkční znovupoužitelný blok popisující výsledný hardware nebo jeho část. Popis může být buď na úrovni jazyka pro popis hardwaru (VHDL, Verilog), nebo i nízkoúrovňový fyzický popis. IP Cores architektury ARM jsou dále licencovány firmami, které je použijí jako základ vlastních implementací procesorů. Takový postup je v oblasti vývoje hardwaru častý. MIPS (akronym slov Microprocessor without Interlocked Pipeline Stages) je podobně jako předchozí instrukční sada typu RISC. Taky je to rodina load-store architektur využivajících sadu obecných registrů. Existuje 32 bitová a 64 bitová varianta. Kódování má pevnou délku. Také MIPS Technologies licencuje své procesory a to už od počátku devadesátých let. Dnes jsou nabízený formou IP cores. Přestože ustoupily z centra zájmu, pořád jsou používány v řadě vestavěných zařízení (routery od Cisco nebo Mikrotik, modemy, set-top boxy atp.). Procesory x86 jsou nejrozšířenější v oblasti stolních počítačů a dnes už taky u serverů. V případě vestavěných systémů se ale jedná spíše o okrajovou oblast a používají se v systémech, kde je třeba složitějšího řízení, možnost nahrávání nového softwaru apod. ISA těchto procesorů je notoricky známá a její podrobný popis jde nalézt například v Intel Architecture 1 Původní význam zkratky byl Acorn RISC Machines pojmenované po firmě Acorn Computers, která první procesory uvedla roku 1987.
8
Obrázek 2.1: Uspořádání konfigurovatelného logických bloků v FPGA Spartan 3. Převzato z [19]
Software Developer’s Manual. [10] Ve zkratce se jedná o post-CISC procesory se sadou registrů pro obecné použití. Třída ISA je typu register-memory, paměť je adresována po bajtech s možností načíst nezarovnané objekty (i když s výkonovou penalizací). Podporuje všechny běžné typy operandů a všechny kategorie operací (operace s čísly s pohyblivou desetinnou čárkou jsou až obsahem rozšiřujících sad). Délka instrukcí není konstantní. Zajímavým počinem z oblasti x86 je procesor Intel Atom. Krom možnosti ho použít ve vestavěných zařízeních (čímž se výrazně neliší od běžných procesorů), má řada E600C přímo na procesorové destičce připojené FPGA společnosti Altera. Druhou zajímavostí spojenou s procesorem Intel Atom je existence verze upravené pro syntézu v FPGA. K běhu tohoto procesoru je třeba FPGA Virtex-5 LX330. Jádro běží na frekvenci 50 MHz a je schopno spustit neupravenou verzi Windows XP a Linuxu. [11]
2.4
Hradlová pole (FPGA)
Pro popis FPGA bude užita terminologie z manuálu Spartan-3 Generation User Guide. [19] Základním prvkem pro implementaci kombinační i sekvenční logiky bývá CLB - konfigurovatelný logický blok (Configurable Logic Block ). CLB jsou uloženy do pravidelné matice. Každý CLB je jednak napojen na bloky ve svém okolí a taky na routovací matici, vstupně-výstupní buffery, specializované části případně jiné sdílené komponenty. V rámci dané rodiny FPGA mají všechny CLB stejnou funkčnost. CLB jsou dále složeny z částí zvané slice’s. Tyto plátky“ už jsou diferencované (viz ” obrázek 2.2). Nejčastěji obsahují 4-vstupové LUT tabulky (někdy i 6-vstupové), sčítačku, posuvný registr, distribuovanou RAM, multiplexory, registry apod. Krom výše zmíněných bloků, které jdou pomocí podkladové matice libovolně propojit a tím vytvořit požadovanou funkci, FPGA často obsahují i specializované části. Typickým příkladem jsou specializované násobičky nebo paměťové bloky. Výhody FPGA vyplývají z jejich programovatelnosti a velkého množství paralelně pracujících bloků. Z výhody masivního paralelismu těží oblasti jako zpracování signálů, hledání
9
Obrázek 2.2: Obsah konfigurovatelného logického bloku FPGA Spartan 3. Převzato z [19]
vzorů v DNA nebo třeba v kryptografii při pokusech lámat hesla hrubou silou. Pro snadnou programovatelnost jsou využívány k prototypování ASIC obvodů (Application-specific integrated circuit) případně i k jejich nahrazení, pokud je požadovaný objem příliš malý, aby se vyplatil dělat ASIC.
10
Kapitola 3
Popis architektury VLIW VLIW je zkratkou pro velmi dlouhé instrukční slovo. Charakteristická délka instrukčního slova je však spíš důsledkem myšlenky, která za tímto designovým stylem stojí. Ta spočívá ve zpřístupnění instrukčního paralelismu programátorovi resp. nejčastěji překladači. Podrobnější shrnutí VLIW najdete v první podkapitole. Dalším požadavkem často bývá nízký počet zdrojů nutných pro výrobu. Proto se na jednu stranu návrh značně podobá RISC procesorům, jež dokonce bývají mezistupněm při vývoji. Na druhou stranu jsou její vlastnosti i možnosti srovnávány s procesory superskalárními (více v podkapitole 3.2). Podkapitola 3.3 pojednává o architektuře VEX, kterou společnost Hewlett-Packard dala volně k dispozici jak pro akademické, tak pro komerční využití.
3.1
Pohled na architekturu
Právě z nutnosti explicitně popsat několik operací prováděných paralelně plyne požadavek na dlouhé instrukční slovo (obrázek 3.1). Jeho bitová šířka závisí na počtu a složitosti paralelně pracujících funkčních jednotek. Například u procesoru Crusoe to bylo 128 bitů, jeho nástupce Efficeon už pracoval s 256 bitovou instrukcí. Každá instrukce se dělí na operace (někdy taky označované jako slabiky), jejichž množství závisí na počtu funkčních jednotek - každé FJ odpovídá právě jedna operace. Jak celé instrukce, tak jednotlivé operace T r ans met aCr us oe:
I A64:
Obrázek 3.1: Kompozice instrukčního slova ( molecule“) procesoru Transmeta Crusoe (na” hoře) a jiné řešení tvorby instrukčního slova ( bundle“) architekturou IA-64 (dole). [18]. ”
11
Obrázek 3.2: Schéma generického VLIW procesoru [3]
mívají v rámci implementace stejnou šířku, ale není to nutností. Existují různé možnosti komprese, z nichž jednou je například vynechání NOP instrukcí. Struktura VLIW procesoru bývá blízká RISC procesorům - proto je poměrně jednoduchá. Většinou se jedná o architekturu harvardského typu s oddělenou datovou a instrukční pamětí. Zvláště u VLIW to značně zjednodušuje návrh, protože jednak je šířka instrukce výrazně větší než u datového slova, navíc dokonce může mít netradiční počet bitů, jenž nemusí být celočíselným násobkem datového slova. Další zjednodušení je dáno tím, že klasický VLIW neřeší paměťové závislosti. To je práce kompilátoru, který ale musí mít velice podrobné informace o vnitřní struktuře. Základní blokové schéma VLIW procesoru by mohlo vypadat jako na obrázku 3.2. Datové slovo je načteno z instrukční paměti (fetch). Jednotka dekódování buď úplně chybí, nebo je velice jednoduchá (decode). Následuje jednotka načítání operandů (read ), která ze souboru registrů načte potřebné operandy a pošle je funkčním jednotkám (execute). Po zpracování jsou v poslední fázi výsledky zapsány zpět do souboru registrů (write). V typickém VLIW procesoru najdeme až desítky paralelně pracujícíh funkčních jednotek (ALU, FPU, LOAD/STORE, BRANCH, atd). Například počítače TRACE od Multiflow jich měly 28. Problém tak velkého množství jednotek spočívá v přístupu k souboru registrů. Protože ke každé jednotce jsou třeba dva porty pro čtení a jeden pro zápis a s každým portem roste kvadraticky počet spojení, byl by jeden společný registr u většího počtu jednotek příliš nákladný. Proto se využívá tzv. clustering (podrobně v [5] stránky 145-148). Jedná se o metodu, pomocí které se vytvoří skupiny cca čtyř až osmi funkčních jednotek. Tyto mají vlastní soubor registrů. Ke komunikaci mezi clustery slouží další jednotka. Celkově dochází 12
Obrázek 3.3: Porovnání přístupu k ILP mezi architekturou superskalární a VLIW [5]
ke snížení výkonu (udává se 20 % u dvou a 30 % u čtyřech clusterů). Na druhou stranu se tím jednak výrazně sníží spotřebované zdroje a často se tím umožní i vyšší frekvence, které ztrátu výkonu vykompenzují.
3.2
ILP a porovnání se superskalárními procesory
Jak bylo už dříve zmíněno, základní vlastností VLIW je schopnost zpracování několika instrukcí zároveň - tedy ILP. Jedná se o charakteristiku společnou se superskalárními procesory. I superskalární procesory mívají několik paralelně běžících funkčních jednotek. Těch bývá ale podstatně méně - i dnes špičková architektura Nehalem (Core i7) dokáže v jednom taktu vyslat v rámci každého jádra pouze šest operací. Navíc i těchto šest operací se využije jen výjimečně a to převážně v situacích, kdy je zapnuté SMT (simultaneous multi-threading). Tak velký rozdíl ve využití ILP mezi superskalárními a VLIW procesory je dán odlišným přístupem k alokaci funkčních jednotek. Ten výborně ilustruje obrázek 3.3. Vstupem bývá stejný zdrojový kód. U superskalárních procesorů proběhne překlad relativně jednoduchým překladačem do lineárního strojového kódu a o souběžné provádění instrukcí se stará rezervační stanice přímo v procesoru. Ta je sice poměrně složitá a s každou novou generací umí lépe vyhledat ILP, je ale stále limitována velice omezenými prostředky a nutností zpracovávat kód v reálném čase. To v praxi znamená, že dokáže paralelismus vyhledat jen v pár po sobě jdoucích instrukcích. V případě VLIW taky nejprve dojde k běžnému překladu do 13
lineárního kódu. Dále se ale při kompilaci provede i naplánování využití funkčních jednotek. Mohlo by se zdát, že kompilátor jen navíc supluje funkci rezervační stanice. Ale optimalizace prováděné rezervační stanicí jsou jen zlomkem práce, jež musí vykonat dobrý VLIW kompilátor. Tyto techniky často pracují s větší částí programu než v rámci bloku (trace scheduling, loop unrolling, software pipelining, list scheduling atd.). Právě vývoj kompilátorů by v budoucnosti mohl udělat dostatečně velký rozdíl v efektivitě mezi VLIW a klasickými superskalárními CPU, aby se skutečně mohly prosadit.
3.3
VEX
VEX (zkratka pro VLIW EXAPLE ) je systém umožňující návrh a simulaci VLIW procesoru. Tento systém je složen ze tří hlavních komponent: • definice abstraktní 32 bitové architektury vyvinuté firmou Hewlett Packard, která vychází z ISA HP/ST Lx (ST200) a je do značné míry rozšiřitelná a modifikovatelná, • překladače jazyka C pro VEX a • simulačního systému. Tento balík nástrojů neobsahuje vlastní implementaci v FPGA. Tento nedostatek ale opravil projekt r-VEX [16]. Jedná se o velmi zajímavou implementaci, která posloužila jako inspirace při vlastním návrhu. Přestože vlastní návrh není kompatibilní s touto architekturou, do značné míry z ní vychází. Ze základu implementuje většinu instrukcí, žádná z nich ale není vyžadována (tzn. vlastní ISA nedefinuje povinný výčet instrukcí). Stejně tak není dodržena fixní délka slova, která je tady 32 bitů (délka je v rámci konkrétní implementace konstantní, ale nemusí být zrovna 32 bitů). Podrobnější informace o VEXu lze najít na stránkách projektu a v dokumentaci [9].
14
Kapitola 4
Návrh V této kapitole je popsán návrh implementace vlastního procesoru. V následujících odstavcích naleznete úvahy, ze kterých se vyšlo. První podkapitola popisuje procesor jednak na úrovni funkčních jednotek a dále i jeho komunikaci v rámci FPGA (tzn. nahrávání dat a možnosti testování). Podkapitola 4.3 se zabývá generováním instrukcí. V poslední podkapitole (4.4) je uvažováno nad možnostmi implementace forwardingu. Základní myšlenkou bylo vytvoření návrhu co nejjednoduššího VLIW procesoru, který by pokud možno šel nahrát i do nejjednodušších FPGA a současně byl snadno konfigurovatelný a rozšiřitelný. Kvůli jednoduchosti byla zvolena harvardská architektura s instrukční sadou vycházející z VEXu (load/store). Ze stejného důvodu základní design nepočítá s více clustery (i když bude implementován tak, aby tuto možnost do budoucna nevylučoval). Konfigurovatelnost se projeví v možnosti nastavit velikosti registrového pole, instrukční a datové paměti, měnit počet a sestavení paralelně pracujících funkčních jednotek a možnost volby šířky zpracování dat (plánované jsou možnosti 8, 16 a 32 bitů). Posledním důležitým rysem bude možnost přidávat nové instrukce. To se na úrovni návrhu nejvíce projeví při jejich generování.
4.1
Základní struktura procesoru
Přestože cílem je udělat architekturu, která by umožňovala v rámci clusteru značnou flexibilitu konfigurací funkčních jednotek, popisuje pro jednoduchost tato podkapitola případ čtyřcestného zpracování. Vzhled po přidání nebo ubrání si čtenář jistě sám snadno představí. Nejlépe lze přiblížit architekturu pomocí obrázku 4.1. Jak lze vidět, jedná se o pětistupňový zřetězený procesor. Jednotka FETCH se stará o načtení instrukcí z paměti. Následuje značně zjednodušená fáze DECODE. Ta se stará pouze o separaci jednotlivých operací a převedení operačních kódů do vhodnější reprezentace. Jednotka READ načítá operandy jednak ze souboru registrů pro obecné použití a potom i z registrů pro větvení. Výkonné jednotky z fáze EXECUTE budou do značné míry volně konfigurovatelné, nicméně bude vyžadována právě jedna kontrolní jednotka pro skoky a větvení, minimálně jedna ALU a nejméně jedna jednotka pro práci s pamětí (jen přes ni bude možno přistupovat do datové paměti). Závěrečná fáze WRITE se postará pouze o zapsání výsledku do správných registrů.
15
GENERALPURPOSEREGI STERFI L E I NSTR.MEM
BRANCHREGI STER EXECUTE
F E T C H
D E C O D E
CTRL+ AL U
R E A D
AL U AL U L / S+ AL U
PC
W R I T E
DATAMEM
Obrázek 4.1: Schéma procesoru
4.2
Řízení procesoru
Procesor bude ovládán pomocí externí jednotky, která (možná poněkud nepřesně) je na obrázku 4.2 označena jako CONTROL UNIT. Základní funkcí by mělo být nahrání dat do instrukční a datové paměti. Poté by měla pomocí signálů reset a enable spustit vykonávání. Zatímco reset bude sloužit čistě k inicializaci, a bude proto přímo napojen na jednotlivé stupně resp. jednotky, enable povede pouze k jednotce FETCH. Signál se poté se zpožděním bude propagovat jednotlivými stupni. Druhou funkcí zmíněné jednotky je právě možnost kontroly programového čítače. To by společně s interním čítačem reálného času mělo pomoct obzvláště při testování funkčnosti.
4.3
Instrukce a jejich generování
Základní instrukční sada vychází z té, kterou definuje VEX [9]. Plánovaná flexibilita ale znemožňuje předem vypsat strojové kódy jednotlivých instrukcí. Ještě by se například dalo ignorovat, že 8 bitová implementace nevyužije operace pracující s celým slovem (např. LDW, STW). Tím by se nanejvýš ztratil jeden bit operačního kódu 1 , nebo se zneplatnily některé kombinace. Na druhou stranu změna velikosti registrového pole ze 128 na 32 položek by už znamenala ztrátu 6 bitů (3 x 2 bity). Proto tu místo toho bude vypsáno, jakými zásadami se bude řídit generování kódů.
1
Termínem operační kód je v rámci této práce myšlena ta část operace, která definuje, co se má provést. Mezi další časti patří adresa registru nebo přímý operand.
16
FPGA C O N T R O L
DATARAM
CPU L OGI C
I NSTRRAM r es et
U N I T
enabl e
PC
Obrázek 4.2: Řízení procesoru
Nejdříve však několik návrhových rozhodnutí, která je potřeba udělat předem: Všechny operace budou mít v rámci jedné implementace stejný počet bitů. To má důležité implikace. Udává, že délka všech operací bude dána tou nejdelší. Což sice zjednoduší dekódování instrukcí, ale výrazně sníží efektivitu kódování a zvýší nároky na kapacitu instrukční paměti. Ve většině případů budou operace obsahovat hluché“ části, jež nebudou mít vliv na průběh vykonávání. ” V rámci každé implementace bude fixní šířka instrukce. Znamená, že nebudou použity kompresní metody ani v rámci celých instrukcí (příkladem by mohlo být zmíněné vynechání NOP). Operační kód bude mít jednotnou formu napříč všemi implementacemi. Jeho šířka bude osm bitů a obsahuje čtyři základní prostory pro různé typy instrukcí. Osmý bit bude určovat, zda se jedná o instrukci s přímým operandem. V některých případech sice bude hodně nevyužitého prostoru, snad to ale umožní případnou doimplementaci dalších instrukcí. Písmeno c značí generovaný bit kódu, i příznak přímého operandu. 2 Operace ALU a MUL MEM BRANCH SPEC
Kód 000cccci až 100cccci 101cccci 110cccci 111cccci
Po prostudování instrukcí VEXu (výpis v příloze A) lze rozdělit operace v rámci jejich nároků na strojový kód do pěti skupin uvedených níže. V této části není zajímavé jaké konkrétní operace se provedou (sčítání, násobení apod.), ale jaké jsou nároky jednotlivých skupin operací ve smyslu jejich zakódování. Operace jsou zapsány formou přiřazení, kdy na levé straně jsou cílová místa, kde dojde ke změně stavu, a na pravé straně místa a hodnoty použité k výpočtu provedené změny. Pro interpretaci zkratek můžete využít následující tabulky: 2
Adresní prostor pro instrukce byl proti původnímu návrhu ze semestrální práce [14] upraven.
17
Zkratka rf br lr im off pc
Význam soubor registrů (register file) registry pro větvení (branch register) link registr přímý operand (immediate) offset ve formě přímého operandu programový čítač
ALU a MUL operace s oběma zdrojovými operandy v registrech Všechny zde spadající lze popsat jedním z následujících vzorů práce s operandy: rf
= rf
rf
= rf, rf
br = rf, rf rf
= br, rf, rf
rf, br = br, rf, rf
Poslední z vypsaných vzorů je nejobsáhlejší, proto bude na základě něj generována operace (obrázek 4.3 a). Po operačním kódu následují adresy souboru registrů (pořadí cíl, zdroj1, zdroj2) a registrů pro větvení (pořadí cíl, zdroj). ALU a MUL operace s jedním přímým operandem Podobně jako v předchozím případě i zde je více vzorů: rf
= rf, im
br = rf, im rf
= br, rf, im
I zde jsou vzory seřazeny tak, aby poslední obsahoval nejvíce operandů. Zvolené pořadí v rámci operace tedy bude operační kód, cílový a zdrojový všeobecný registr, registr větvení (ať už cílový či zdrojový) a nakonec přímý operand (obrázek 4.3 b). Operace pro práci s pamětí Opět existují dva vzory. Tady ale navíc existují tři modifikátory, jež si vyžádají každý po jednom bitu. Modifikátor .d (značí dismissible 3 ) se používá při spekulativním načítání před větvením, kdy není jisté, zda bude obsahovat validní data. Zbylé dva - .s (streaming) a .l (local ) jsou jen nápovědy, jejichž význam není přesně specifikován. Přesto pro ně budou rezervovány bity. rf
= im[rf ]
im[rf ] = rf
Po třech bitech rezervovaných modifikátorům následuje adresa do pole registrů s cílovým, nebo zdrojovým operandem, adresa offsetu a nakonec přímý operand. 3
Historicky označovalo destructive, ale protože se jednalo o příliš silné“ slovo, které vyvolávalo obavy, ” byl význam revidován.
18
a) 0CCCCCC0
r f( t )
r f( s 1)
b) 0CCCCCC1
r f( t )
r f( s 1)
c ) 101CCCCI dsl r f( t/s 2)
r f( s 2)
br( s )
r f( s 1)
d) 110CCCC0 br( s )
e) 110CCCCI
br( t )br( s )
i m
i m
i m_of f
r f( t )
l r
i m_of f
Obrázek 4.3: Šablony pro tvorbu základních operací
Operace skoku Operace skoku jsou vzhledem k svému nízkému počtu nejrůznorodější z hlediska operandů. Navíc se tu objevuje speciální pole link registrů určené k uložení adres skoků. pc = of f pc, lr = of f pc = br, of f pc, rf
= rf, of f, lr
V tomto případě žádný ze vzorů nezahrnuje možnosti všech ostatních. Proto operace podmíněného skoku (tzn. vzor b, off) budou mít vlastní šablonu (obrázek 4.3 d). Ostatní operace jsou potom podmnožinou posledního vzoru (obrázek 4.3 e). Všechny operace z této kategorie navíc můžou měnit obsah programového čítače, ten ale není třeba nijak adresovat. Speciální operace Do této skupiny budou patřit i uživatelsky definované operace. Proto nelze předem udělat šablonu, podle které se budou řídit. Jediným společným znakem tedy bude operační kód začínající dvěmi jedničkami. V obecném případě nelze očekávat, že délka instrukce bude mocninou dvou, jak je v počítačích zvykem. Až při implementaci se proto ukáže, zda bude výhodné zaokrouhlit délku nahoru, nebo ji ponechat. Ani nelze rozhodnout, jak velký rozdíl bude u implementací, ve kterých se vhodně zvolí velikosti registrových polí. Kód operace bude sestavován zleva. Nejvyšších osm bitů proto bude obsahovat ve všech případech operační kód. V případě instrukcí zobrazených obrázkem 4.3 a) až c) budou po operačním kódu následovat zobrazené části. V případě, že délka operace přesáhne součet délek jednotlivých částí, zprava budou doplněny nevyužitou částí, která nebude mít vliv 19
na zpracování. U operací podle částí d) a e)bude tvorba instrukcí probíhat podobně, ale na nejpravějším místě (tzn. na nejnižších bitech) bude vždy přímý operand s offsetem. Případná nevyužitá část bude vkládána zleva před operand s offsetem.
4.4
Forwarding
Podkapitola přinese trochu bližší pohled na fáze READ a EXECUTION. Konkrétně se zaměří na jeden z implementačních cílů - forwarding (překládá se jako předávání údajů“). ” Jedná se o rychlé předání výsledků instrukce následující instrukci. Obchází se tím zpoždění, které by vzniklo odložením vykonávání závislé operace. Takové zpoždění by bylo důsledkem čekání, než se výsledek zapíše do souboru registrů a potom z něj opět přečte. Pro běžnou implementaci např. v dříve porovnávaném superskalárním procesoru vyžaduje další sběrnici, která propojuje výstup a vstup ALU, další multiplexory na vstupech ALU a komparátory adres, které by zjišťovaly, zda se má některý požadavek na registry nahradit. Problém u VLIW nastává s komplexitou snad ve všech bodech. U čtyřcestného VLIW procesoru by v každém taktu musel na každé z nich komparátor porovnat osm dvojic adres. To by znamenalo nutnost porovnat 32 dvojic adres za takt. Vzhledem k relativní jednoduchosti celého procesoru by se jednalo o příliš velkou spotřebu zdrojů. A se šířkou zpracování rostou nároky kvadraticky nejen na komparátory, ale i spoje. Proto navrhuji dvě možnosti zjednodušení. První vychází ze snahy obejít kvadratický nárůst prostředků tím, že by forwarding fungoval jen v rámci dané cesty. V takovém případě by spotřeba rostla s počtem linek pouze lineárně, nicméně by to znamenalo pro už velice složitý překladač, že musí ještě navíc plánovat obsazení linek tak, aby se výhod forwardingu mohlo vůbec využít. Druhý návrh o něco více zapadá do filosofie návrhu VLIW. Tou by bylo softwarové ovládání forwardingu. To by vyžadovalo úpravu instrukcí tak, aby místo adresy registru obsahovaly adresu jednotky, z výstupu které by se operand načetl (případně by se jednotky mohly chovat jako speciální adresy registrového pole). Na straně překladače by to znamenalo jen jednoduchou transformaci, jež by nahradila adresu vstupu u operandu, který by nestihl projít registrovým polem včas. Tento přístup by umožnil plnou funkčnost jako v případě plnohodnotného hardwarového řešení a přitom by vypadla nutnost za běhu porovnávat adresy. Bohužel kvadratickému nárůstu počtu spojení by se nezabránilo.
4.5 4.5.1
Použité prostředky VHDL
Jako mnoho technických prostředků, i VHDL (zkratka pro VHSIC Hardware Description Language, přižemž VHSIC je akronymem pro Very High Speed Integrated Circuit) bylo původně vyvinuto pro vojenské účely. Mělo jít původně jen o náhradu obsáhlých technických manuálů. Postupně ale vznikly i nástroje pro syntézu. Dnes patří VHDL společně s Verilogem k nejpoužívanějším jazykům pro popis integrovaných obvodů. Ze všech validních konstrukcí jazyka je jen malá část taky syntetizovatelná. Navíc většina nástrojů pracuje tak, že rozpoznává známé konstrukce (multiplexory, čítače, registry atp.) a ty mapuje na možnosti cílové technologie. V případě, že není konstrukce rozpoznána se syntéza nemusí zdařit, nebo je výstup neoptimální. Proto se doporučuje co nejvíce využívat zmíněných konstrukcí. 20
Obrázek 4.4: Platforma FITkit s popisem důležtých komponent (převzato z [6])
Jazyk umožňuje tři způsoby popisu - strukturní, behaviorální a data flow. K popisu se využívá dvou základních komponent. O komunikaci komponenty s okolím se stará entita. Architektura určuje chování nebo strukturu a je vždy spjata s konkrétní entitou. K podrobnému nastudování může sloužit knížka VHDL Design Represenation and Synthesis [1]. Druhým zdrojem informací byly stránky předmětu Pokročilé číslicové systémy [7]. Pro snadnou dostupnost dokumentace a implementačních nástrojů bylo VHDL použito jako hlavní implementační jazyk, na němž staví tato diplomová práce.
4.5.2
FITkit a QDevKit
FITkit je vývojová a testovací platforma postavena kolem dvou základních prvků. Prvním je mikrokontrolér s nízkým příkonem MCU rodiny MSP430 (Texas Instruments). Druhým, pro cíle tohoto projektu nejdůležitějším, prvkem je programovatelné hradlové pole Spartan 3 od firmy Xilinx (použitý a běžně dostupný model obsahuje Spartan 3 XC3S50-4PQ208C). Po hardwarové stránce obsahuje i další užitečné komponenty jako například paměť DRAM, klávesnici, dvouřádkový LCD displej nebo různé výstupní konektory a rozhraní. Většina z nich není pro účely projektu nezbytná, ale například pro prezentační účely se můžou hodit (konkrétně LED diody nebo LCD displej). Přesné specifikace lze nalézt na stránkách projektu FITkit [6]. K platformě FITkit lze stáhnout kvalitní vývojový nástroj QDevKit. QDevKit je multiplatformní terminálový program přímo spolupracující s FITkitem. Dokáže zařízení automaticky detekovat, komunikovat s ním a programovat jak mikrokontrolér, tak i FPGA. Součástí je překladový systém postavený na XML souborech [17]. Základní soubor project.xml obsahuje informace o umístění zdrojových souborů pro mikrokontrolér a FPGA a to buď přímo relativní adresou souboru, nebo přes jiný XML 21
Obrázek 4.5: Schéma překladového systému (převzato z [17])
soubor, jenž ji obsahuje. Soubor project.xml obsahuje i některé další informace o projektu (autor, revize, popis atp.) [17]. Po vytvoření výše uvedeného souboru se projekt objeví v nabídce aplikace QDevKit. Odtud je možné projekt otevřít a upravovat v jiných nástrojích - přímo v kontextové nabídce je možnost projekt otevřít např. v nástroji Xilinx ISE. Lepší představu o schématu překladového systému si lze udělat z obrázku 4.5.
22
Kapitola 5
Implementovaná architektura a její popis Jak bylo zmíněno v části popsané už semestrální prací, cílem bylo vytvořit obecný, lehce rozšiřitelný VLIW procesor, který by ale současně mohl být škálován směrem dolů i pro použití ve FITkitu, který má značně omezené prostředky integrovaného FPGA. Za tímto účelem byla vytvořena hierarchie funkčních bloků, které mají jasně definovanou vnitřní funkci a jsou s okolím spojeny rozhraním, které by potenciálně mělo umožnit přenést všechna potřebná data a informace pro širokou škálu instrukcí. Samotné bloky následují tradiční dělení stupňů řetězeného procesoru a jeho dalších částí (zejména pamětí). Některé z bloků dále obsahují uvnitř relativně samostatné podjednotky, z nichž některé jsou určeny k doimplementaci dalších operací na uživatelské úrovni (uživatelem je v daném případě myšlen vývojář, který případně bude upravovat tento procesor pro své potřeby). Implementace byla prováděna za pomoci více nástrojů. Procesor jako celek byl vytvářen v prostředí Xilinx ISE 12.4 (a později Xilinx ISE 13.1). Simulace některých komponent probíhala v aplikaci ModelSim 6.6d. To se ukázalo jako velice pomalé a neefektivní, proto v druhé iteraci tvorby procesoru byl pro simulaci využit nástroj ISim, který je součástí balíku nástrojů Xilinx ISE. V průběhu dokončování proběhlo zakomponování a upravení pro platformu FITkit v nástroji QDevKit. Jako referenční manuál o VHDL posloužily tři zdroje informací. Základním materiálem se stala kniha Marka Zwoli´ nského Digital System Design with VHDL[21]. Vynikajícím zdrojem byl i VHDL Tutorial: Learn by Example [20] umístěný na internetu, jehož autorem je Weijun Zhang. Posledním zdrojem byla knížka Advanced FPGA Design [12] napsaná Stevem Kiltsem. V této kapitole bude procesor popsán metodou shora-dolů. V první podkapitole proto najdete propojení a základní rozvržení jednotlivých bloků (podkapitola 5.1), v dalších pak budou jednotlivé bloky podrobně rozebrány. Popořadě najdete sekce popisující FETCH (podkapitola 5.4), DECODE (podkapitola 5.5), READ (podkapitola 5.6), EXECUTE (podkapitola 5.7), WRITE (podkapitola 5.8) a konečně paměťi (podkapitola 5.9). V úplném závěru kapitoly budou rozebrány možnosti konfigurace procesoru a způsob přidávání podpory nových operací.
23
5.1
Pohled shora
Tvorba procesoru probíhala metodou shora-dolů. První byla vytvořena struktura na nejvyšší úrovni. Základním bodem při její tvorbě byl návrh, jak je ukázaný na obrázku 4.1. Jednotlivé části se zde nacházely ve formě black-box“ modulů - měly definované pouze svá ” rozhraní, nikoliv funkčnost. V průběhu kódování přeci jen došlo k některým změnám oproti původnímu návrhu. Část z nich jsou pouze úpravy hierarchie, jiné mění funkčnost. K první skupině se řadí zahrnutí programového čítače do jednotky FETCH. Druhou úpravou podobného charakteru je zahrnutí datové paměti dovnitř podjednotky MEM. Cílem druhé změny bylo zjednodušení implementace. Výraznou změnou druhé kategorie je odebrání registrů mezi stupňemi EXECUTE a WRITE (podrobnosti viz podkapitola 5.8). Podobnou úpravou prošla i data čtená ze souboru registrů a registrů pro větvení. Samotným čtením dochází k jejich zpoždění, a proto mezi stupni READ a EXECUTE nejsou ukládána. Přídavkem jsou nové porty na jednotce WRITE, které nejsou v rámci procesoru zapojeny. Ty jsou určeny ke komunikaci s periferiemi.
5.2
Změny konfigurace procesoru
Procesor byl od počátku vytvářen jako snadno konfigurovatelný. Realizace myšlenky probíhala ve dvou proudech: 1. Systematické vytváření obecných konstrukcí. 2. Koncentrace proměnných definujících různé aspekty procesoru do jediného souboru. Právě druhý bod je důležitý z hlediska uživatele vytvořeného prototypu procesoru. Zmíněným souborem je vliw params.vhd a nachází se v kořenovém adresáři návrhu. Soubor obsahuje balík (v originále package) CPU params. Ten je dále rozdělen na dvě části. Druhá je pro účely změny konfigurace na úrovni celého procesoru méně důležitá - obsahuje definice nových typů užitých při tvorbě procesoru. Zato první část obsahuje konstanty popisující vlastnosti procesoru. Konkrétní možnosti konfigurace a jím příslušející konstanty jsou popsány dále. Počet cest (definovaný proměnnou op count). Udává šířku zpracování. V aktuální verzi je podporováno dvou a vícecestné zpracování (předpokládány jsou hodnoty 2-8). Omezení vyplývají z množství spotřebovaných prostředků, které s každou další cestou roste. Šířka operačního slova (udávaná v bitech; definovaná proměnnou op width). Je ji možno měnit, ale je třeba počítat s nutností zakódovat veškeré informace pro použité jednotky. Tedy například pro potřeby ALU tak, aby se do ní vešel operační kód, tři ukazatele do souboru registrů a dva do registrů pro větvení. Podrobnější informace o sestavování operací jsou v kapitole 4.3. Šířka datového slova (udávaná v bitech; definovaná proměnnou data width). Krom šířky operandů udává taky věci na nich přímo závislé, jako velikosti registrů. Velikost instrukční paměti (definovaná proměnnou imem size). Obsah proměnné určuje počet bitů použitých k adresování paměti. 24
Velikost datové paměti (definovaná proměnnou dmem width). Obsah proměnné určuje počet bitů použitých k adresování paměti a tedy i velikost paměti. Velikost souboru registrů (definovaná proměnnou rf size). Obsah proměnné určuje počet bitů použitých k adresování paměti. Velikost registrů pro větvení (definovaná proměnnou br size). Obsah proměnné určuje počet bitů použitých k adresování paměti. Konfigurace funkčních jednotek (definovaná proměnnou d conf). Určuje, které jednotky (a jejich dekodéry) budou na které z cest navěšeny. Konfigurace je zaznamenána polem hodnot boolovského typu o délce 4 × op count. Pro potřeby tří cest by to bylo dvanáct hodnot. Každá čtveřice hodnot se vztahuje k jedné cestě a definuje posloupnost ALU, MEM, BRANCH, SPEC. Pokud bychom chtěli například na první cestu navěsit jednotky ALU a BRANCH a na druhou ALU, MEM a SPEC, bude výsledné pole vypadat následovně: (true, false, true, false, true, true, false, true). Omezením, které bylo už zmíněno v předchozích kapitolách, je možnost navěšení nanejvýš jedné jednotky typu BRANCH.
5.3
Použitá konfigurace
V dalším textu se budou objevovat diagramy a popisy procesoru a jeho částí. Ve všech případech, pokud nebude řečeno jinak, se budou vázat na konfiguraci, jak je popsána v této kapitole. V konfiguračním souboru jsou nejdůležitější nastavení popsána následujícími řádky: constant constant constant constant constant constant constant constant
op_count : integer := 2; op_width : integer := 32; data_width : integer := 8; imem_size : integer := 7; dmem_size : integer := 4; rf_size : integer := 4; br_size : integer := 3; d_conf: decoder_config := (true, false, true, true, true, true, false, true);
Referenční procesor má dvě cesty, s šířkou operace 32 bitů (instrukce má tedy 64 bitů) a pracuje s 8 bitovými operandy. Instrukční paměť má 128 položek, datová paměť a soubor registrů má 16 položek a registr skoků 8 položek. Na první cestě jsou navěšeny jednotky ALU, BRANCH a SPEC. Druhá cesta obsahuje jednotky ALU, MEM a SPEC.
25
Obrázek 5.1: Schéma procesoru. Napojení hodinového signálu není pro přehlednost zakresleno.
26
r s t
c l k
pc
D r s t _ d2
pc
RE AD
D r s t _ d3
pc
dec oded_ ops _ r i dec oded_ ops _ r o
DE CODE
f et c hed_ i ns t r _ r i f et c hed_ i ns t r _ r o
F E T CH
D r s t _ d1
i mem_ da t a
i mem_ a ddr
I ME M
BRANCH RE GI S T E R
D r s t _ d4
pc
da t a _ a , da t a _ b, da t a _ br
r f _ a ddr _ a , r f _ a ddr _ b
en
pr _ da t a
j ump_ a ddr , j ump_ en
s t a l l
WRI T E
r f _ a ddr , r d_ da t a , r f _ en
br _ a ddr _ wr , br _ da t a , br _ en
ex ec ut ed_ ops
E XE CUT E
RE GI S T E R F I L E
r f _ dout _ a , r f _ dout _ b
br _ dout
5.4
FETCH
Jednotka FETCH se stará o načtení instrukčního slova z instrukční paměti. V současné implementaci je její součástí v první řadě programový čítač (označovaný jako program counter, zkráceně PC). Vstupem je adresa skoku a bit určující její platnost. Výstupem je adresa do instrukční paměti. V praxi se jedná o klasickou implementaci čítače. Obecně by šlo programový čítač postavit zcela mimo základní jednotky. Druhou, spíše formálně logickou, funkcí je předání načtené instrukce dalšímu stupni ve formě pole operací. To se na úrovni hardwaru nijak neprojeví - jedná se jen o logické rozdělení, které na úrovni implementace umožní lépe s jednotlivými operacemi dále pracovat.
5.5
DECODE
Jedná se o první jednotku, ve které se naplno projeví modulární vlastnosti procesoru. Vstupem je pole operací ve formě instrukce. Kromě možnosti měnit počet operací v rámci jedné instrukce (před syntézou), která je vlastní celé instrukční pipeline, se zde projeví možnost navěsit na danou cestu dekodéry jen zvolených funkčních jednotek (na výběr jsou čtyři typy - ALU, MEM, BRANCH a SPEC). Takové řešení má lehce omezit počet spotřebovaných prostředků a umožnit snadnější výběr jednotek - zvláště pak ve stupni vykonávání. Z uživatelského hlediska se v tomto stupni definuje jen případné dekódování vlastních instrukcí pro jednotku SPEC (případně i jinou jednotku, pokud by se tak uživatel rozhodl). Vstupem je pole operací převzaté ze stupně FETCH. Výstupem je pole záznamů typu op decoded, jehož deklarace je uvedena níže. Každý záznam obsahuje deset informací (výjmenovány a popsány dále), jež popisují vykonání operace na zvolené cestě. Selektor jednotky je dvoubitový příznak určující, která ze čtyř základních jednotek bude použita pro vykonání operace. Přesněji řečeno je později užit pro výběr jednotky, která svůj výsledek předá dál a zapíše. Jsou-li na dané cestě navěšeny například jednotka ALU, MEM a SPEC, provádějí všechny tři jednotky výpočty současně, ale jen jedna z nich (ta, jíž přísluší daný operační kód), svůj výsledek nakonec zapíše do registrů nebo paměti. Samozřejmě by šlo obejít potřebu pro selektor jednotky využitím přímo operačního kódu (prvních osmi bitů dané operace), pro úsporu prostředků a přehlednější kód je ale zvolený postup výhodnější. Operační kód obsahuje prvních sedm bitů každé operace. Osmý bit, typicky udávající využití přímého operandu, je uložen separátně. Další informací jsou povinně definované zápisové bity. Jimi se určuje, do kterých pamětí operace zapisuje - na výběr je soubor registrů, branch registr, programový čítač, speciální link registr a případně registry pro výstup na periferie. Danou informaci je možné, a v některých případech i nezbytné, specifikovat až v pozdějších stupních (zejména ve fázi vykonávání). Například v případě podmíněného větvení je až ve fázi vykonávání známo, zdali dojde k zápisu do programového čítače. Adresy zdrojových operandů se extrahují také v tomto kroku. Celkově je možné dále poslat ukazatele na dva zdrojové operandy do souborů registrů, jeden do registru pro větvení (přesnější popis by byl soubor registrů pro bitové příznaky). Navíc je možné dále poslat jeden přímý operand - v případě operace s indikovaným přímým operandem (příznak imm flag) tento nahradí ve stupni READ druhý operand ze souboru registrů. Poslední informací jsou adresy cílů - jeden ukazatel do souboru registrů a jeden ukazatel do registru pro větvení.
27
op_i n( 0)
c l k
r s t
op_i n( 1)
AL Uudec
op_out ( 0)
BRUudec
S PUudec
AL Uudec
op_out ( 1)
MUudec
S PUudec
Obrázek 5.2: Schéma stupně DECODE.
type op_decoded is ucode : wcode : opcode : imm_flag : src_a : src_b : immediate: target :
record std_logic_vector(1 downto 0); -- unit selector write_code; -- write selector std_logic_vector(opcode_width-2 downto 0); std_logic; std_logic_vector(rf_size-1 downto 0); std_logic_vector(rf_size-1 downto 0); std_logic_vector(imm_off_width-1 downto 0); std_logic_vector(rf_size-1 downto 0);
br_src : std_logic_vector(br_size-1 downto 0); br_target: std_logic_vector(br_size-1 downto 0); end record;
5.6
READ
Stupeň READ má dva vstupy. Prvním je pole dekódovaných operací, tak jak jsou popsány v předchozí podkapitole zabývající se částí DECODE. Druhým jsou vstupy z registrových polí - souboru registrů a registru pro větvení. Hlavní funkcí tohoto stupně, jak už název napovídá, je přečíst data z registrů a nahradit
28
br _da t a _i n
r f _da t a _a _i n
r f _da t a _b_i n
br _a ddr
r f _a ddr _b
r f _a ddr _a ops _ i n
op_ i n( i ) . a ddr _ b
ops _ out ( i ) . br
op_ i n( i ) . a ddr _ a
ops _ out ( i ) . a
op_ i n( i ) . a ddr _ br en
op_ i n( 0) . i mmedi a t e
op_ i n( 0) . i mm_a g c l k op_ i n( 1) . i mmf edi a t e
op_ i n( 1) . i mm_a g
D
ops _ out ( 0) . b
D
D
ops _ out ( 1) . b
D
D
en c l k
op_ i n( i ) . opc ode, . uc ode, . wc ode, . t a r get , . br _ t a r get , . i mmedi a t e
Obrázek 5.3: Schéma stupně READ spolu s registrem mezi stupněmi READ a EXECUTE.
ukazatele do registrových polí vlastními hodnotami operandů. Druhou, vedlejší funkcí je rozhodování o výběru druhého operandu mezi přímým a operandem z registru. Výstupem je opět pole záznamů, které se velice podobá předchozímu kroku a v němž je většina informací přeposlána dále (ucode, wcode, opcode, immediate, target, br target). Nahrazené informace jsou jen tři - a (první operand), b (druhý operand) a br (bitový příznak). Protože čtením operandů z registrů vznikne zpoždění, nejsou nahrazené informace dále ukládány do registrové příčky mezi stupni READ a EXECUTE. type op_ready is ucode wcode opcode
record : std_logic_vector(1 downto 0); : write_code; : std_logic_vector(opcode_width-2 downto 0);
a : b : immediate: target :
std_logic_vector(data_width-1 downto 0); std_logic_vector(data_width-1 downto 0); std_logic_vector(data_width-1 downto 0); std_logic_vector(rf_size-1 downto 0);
br : std_logic; br_target: std_logic_vector(br_size-1 downto 0); end record;
29
5.7
EXECUTE
Zde se provádí hlavní část zpracovávání dané instrukce. V případě aritmetických a logických operací dochází v této fázi k vlastnímu výpočtu. Stejně tak u operací skoku se zde vypočítává cílová adresa a zdali k vlastnímu skoku má vůbec dojít. V případě paměťových operací zde dochází k zápisu nebo čtení z paměti. Podobně jako v případě DECODE, i zde se ve velkém projeví možnosti konfigurace. Na kteroukoliv z cest lze navěsit téměř libovolnou kombinaci funkčních jednotek. Výjimku tvoří pouze omezení na jednu BRANCH jednotku v rámci procesoru. Toto omezení vychází z podstaty zpracování u VLIW procesorů, kdy všechny cesty zpracovávají jedinou dlouhou instrukci. Není tedy možné, aby jednotlivé cesty zpracovávaly v daném stupni operace, které jsou součástí různých instrukcí. Procesor obsahuje jediný programový čítač a změny jeho obsahu z více jednotek současně by přinesly jednak problémy s implementací, ale hlavně by tím nevznikly žádné výhody. Funkční jednotky (ALU, MEM, BRANCH, SPEC) jsou z hierarchického hlediska samostatné funkční celky, u nichž je pevně definováno jen rozhraní. Jejich funkci je možno volně definovat, i když v rámci zachování přehlednosti je vhodné minimálně zachovat rozdělení podle funkcí a případné nově podporované operace dodávat do jednotky SPEC. Vstupem je pole záznamů, které je výstupem ze stupně READ a je ve stejnojmenné sekci podrobně popsáno. Druhým vstupem, pro potřeby jednotky větvení, je také hodnota programového čítače. type op_executed wcode x target
is record : write_code; : std_logic_vector(data_width-1 downto 0); : std_logic_vector(rf_size-1 downto 0);
br : std_logic; br_target: std_logic_vector(br_size-1 downto 0); end record;
5.7.1
ALU
ALU je typem funkční jednotky, jež zpracovává aritmetické a logické operace. Operace prováděné touto jednotkou se dále dají rozdělit do následujících skupin (v závorkách jsou pro představu uvedeny některé z operací): • aritmetické operace (ADD, ADDCG, SUB), • logické operace (AND, ANDC, OR, ORC, XOR), • operace bitového posunu (SHR, SHL) někdy kombinované se součtem (SH1ADD), • rozšíření čísla znaménkově/nulou (SXTB, ZXTB), • porovnání (CMPG, CMPEQ), • vybrání maxima nebo minima (MIN, MAX). Ve všech případech si jednotka neuchovává žádný vnitřní stav. Výstupy jsou přímo závislé na aktuálních vstupech. 30
op_ i n( 0)
r s t
pc
AL U
op_ out ( 0)
BRU
S PU
op_ i n( 0) . uc ode op_ i n( 1)
AL U
op_ out ( 1)
MU s t a l l
S PU r s t op_ i n( 1) . uc ode
Obrázek 5.4: Schéma EXECUTE. Všimněte si, že struktura je podobná části DECODE. Přibylo zde zapojení clk a rst na podjednotky (využito pouze u MEM), připojení pc pro potřeby skoků a výstup stall pro pozastavení výkonávání procesoru při přístupu do datové paměti.
5.7.2
MEM
Tato jednotka se stará o zápis a čtení dat z paměti. K té má exkluzivní přístup a hierarchicky se připojená paměť řadí dovnitř této jednotky. Je proto možné, aby na více cestách byly navěšeny jednotky pro práci s pamětí, každá ale bude mít svůj vlastní nesdílený prostor. V současné implementaci je paměť přímo v FPGA a procesor nekomunikuje s externí pamětí DRAM. S takovou podporou je ale částečně počítáno a v případě čtení je třeba vystavit na port stall logickou jedničku. Tím dojde k pozastavení vykonávání celého procesoru (konkrétně dojde k zamezení zápisu do všech registrů a instrukční paměť bude po tuto dobu vystavovat operaci NOP). Pro plnou podporu externí paměti by bylo třeba ještě přidat požadované porty na tuto jednotku a všechny jí hierarchicky nadřazené.
5.7.3
BRANCH
Jednotka BRANCH implementuje operace skoku a větvení. V rámci procesoru se smí nacházet pouze jediná, jinak dojde k problému s překladem. I když by technicky šlo udělat procesor, který by obsahoval více jednotek pro větvení, nesloužilo by to žádnému účelu. Součástí této jednotky by měl být link registr určený k ukládání adres skoků. K jeho implementaci ale nedošlo a jednotka má značně omezené schopnosti (zůstaly operace GOTO, BR a BRF).
31
5.7.4
SPEC
Tato jednotka není přesně definována, protože je zamýšlena pro uživatelské doplnění speciální funkčnosti. Rozhraní jednotky je shodné s jednotkou ALU. Vstupem je jeden záznam typu op ready, jež je definován v části READ (podkapitola 5.6). Výstupem je jeden záznam typu op executed, jehož definice je pro změnu na začátku podkapitoly EXECUTE (5.7). O správné naplnění částí záznamu je třeba se postarat ve stupni DECODE a to konkrétně v mikrodekodéru přiřazeném jednotce SPEC. Jednotka SPEC má jedinou přesně definovanou operaci a tou je NOP (zkratka No Operation Performed ). Navíc byla pro účely prezentace přidána operace zápisu na periferie. Postup, jakým byla taková operace přidána, je uveden v části 5.10.
5.8
WRITE
Ačkoli se při návrhu zdálo, že tato jednotka bude obsahovat větší množství funkčnosti, ve výsledku se zde nenachází téměř žádná kombinační nebo sekvenční logika. Výstupy jsou bez dalšího zpracování přímo mapovány na vstupy. Přesto byla převážně pro přehlednost v rámci hierarchie procesoru zachována. Uvedené zjištění vedlo ke zrušení registrů mezi stupněmi EXECUTE a WRITE a tím ke snížení zpoždění a možnosti rychlejšího předání dat následujícím instrukcím. Opatření snižuje množství použitých prostředků a částečně kompenzuje vynechání forwardingu v současné implementaci. V průběhu přenášení návrhu na platformu FITkit (viz podkapitola 4.5.2) se ukázalo vhodné přidat do tohoto stupně výstupní porty pro komunikaci s periferiemi. Jedná se o jednoduché rozhraní tvořené dvěma výstupy. První obsahuje a drží data (délka slova je daná šířkou datové cesty v dané implementaci), druhé vystaví logickou jedničku v případě, že dochází ke změně prvního v daném hodinovém cyklu. Podobně jako v případě celého návrhu, množství portů je dáno počtem cest. Zpracování signálu je nutno provést mimo procesor.
5.9
Paměti a registry
V procesoru se nachází celkem pět typů paměťových prvků. Nejméně problémovou částí jsou mezistupňové registry. Ty umožňují zřetězené zpracování instrukcí. Instrukční paměť byla upravena dvěma způsoby. V obecné implementaci se jednalo o klasickou paměť typu RAM v FPGA. Tato úprava je už přítomna jen ve formě komentáře v příslušném souboru. Pro potřeby testování a prezentace je pro FITkit vložena paměť ROM s pevně zakódovanými instrukcemi. Datová paměť je shodná v obou implementacích. Zde nebyl důvod odklánět se od RAM modulu. Největším problémem je způsob implementace souboru registrů. Jádro problému spočívá ve velkém množství portů pro čtení a zápis. Už v případě základního dvoucestného VLIW procesoru musí mít soubor registrů dva porty pro zápis a čtyři pro čtení. To vede k vysoké spotřebě zdrojů FPGA. Alespoň částečným řešením se zdá být časově multiplexovaný přístup k paměti. Snižuje se tím frekvenční strop procesoru, protože paměť musí pracovat na n-násobné frekvenci, kde n odpovídá počtu cest. Na druhou stranu spotřebované zdroje odpovídají pouze paměti s dvěma porty pro čtení a jedním pro zápis, konstantnímu příspěvku frekvenční děličky, jen pomalu rostoucímu příspěvku multiplexorů a několika
32
registrům pro udržení hodnoty na výstupních portech. Principielně je postup popsán v dokumentu Quad-Port Memories in Virtex Devices [15]. Tam se ale jedná o Block RAM. Pokus o obecné implementování této varianty bohužel neprošel simulací, proto je použita původní přímočará implementace, která je pro účely FITkitu omezena velikostí prostoru registrového pole. Posledním typem jsou různě umístěné jednoúčelové registry a registrová pole. Příkladem je registr na výstupu určenému k jednosměrné komunikaci s periferiemi.
5.10
Přidávání operací
V podkapitole 5.2 jste se mohli seznámit s úpravami na úrovni celého procesoru. Tato část bude naopak zaměřena na velice specifický požadavek zadání - na snadné přidávání nových instrukcí. Postup je shrnut v následujících čtyřech bodech: Ujasnit si, co má nová operace provádět. I když se může bod zdát samozřejmý, jedná se o nejtěžší část. Tady nespadá jen výkon ve fázi EXECUTE. Je nutné vědět, odkud bude operace brát data (registry/přímý operand) a kde se má projevit výsledek. Upravit mikrodekodér. K tomu je potřeba otevřít projekt a upravit soubor UDECODERS.vhd (nachází se ve složce components/decode). Ten obsahuje čtyři entity a k nim náležející architektury. Pro většinu nových instrukcí je vhodné použít poslední architekturu implementující entitu udecoder spec určenou pro vlastní operace. Upravit příslušnou funkční jednotku. Opět je nutné otevřít soubor upravující příslušnou funkční jednotku (ALU.vhd,BRU.vhd, MU.vhd nebo SPU.vhd) . Ten najdeme v adresáři components/execute. Pokud jsme v předchozím bodě vybrali mikrodekodér speciální, najdeme požadovanou jednotku v souboru SPU.vhd. Otestovat. Úpravy VHDL kódu jsou prováděny pouze v druhém a třetím bodě. Pro jednoznačné vysvětlení bude dále uveden příklad takového přidání nové operace, který umožnil výstup na displej FITkitu. Cílem bylo vytvořit instrukci pro demonstraci práce s periferiemi. Jako vstupní data proto stačí přímý operand a operační kód definující instrukci. Výstupem v rámci procesoru je úprava dat registru pro periferie. Po otevření souboru UDECODERS.vhd vidíme, že příslušný dekodér má všechny výstupní hodnoty připojeny na logickou nulu. Všechny operace, které chtějí využívat této jednotky mají operační kód začínající kombinací 111. Zbývající čtyři bity jsou volné pro adresaci vlastních instrukcí (až na kombinaci 1111, jež je rezervována pro operaci NOP). Rozhodneme se, že například kombinace 1110000 bude reprezentovat novou instrukci. Je nutné vytvořit proces, který bude detekovat tuto kombinaci, v němž určíme, že chceme zapisovat do periferního registru (a pouze do tohoto registru). To se provede nastavením op out.wcode.pr na logickou jedničku a zbylá část op out.wcode se ponechá na nule (nechceme měnit programový čítač, ani nic jiného). Současně nejnižší bity operace nasměrujeme do signálu op out.immediate. Protože žádná jiná operace nikam nic nezapisuje, je možné vynechat tvorbu procesu a pouze provést napojení. Ke všemu výše zmíněnému stačí dopsat následující kód:
33
op_out.immediate <= op(imm_off_width-1 downto 0); process(op(op_width-1 downto op_width-(opcode_width-1))) begin case op(op_width-1 downto op_width-(opcode_width-1)) is when 1110000‘‘ => op_out.wcode.pr <= ’1’;--detekce nové operace ’’ when others => op_out.wcode.pr <= ’0’; end case; end process; Pokud jsou některým změněným signálům již dříve přiřazovány hodnoty, je třeba původní přiřazení zrušit, jinak dojde k chybě při překladu. Úprava souboru SPU.vhd už je podstatně jednodušší. Protože wcode je už nastavený, stačí jej předat dále. Podobné je to v případě přímého operandu. Opět platí, že se jedná o jedinou operaci, která provádí jakýkoliv zápis, a proto není už třeba vytvářet žádný proces na určení operačního kódu. Celá úprava spočívá v přidání následujících dvou řádků a smazáním případného předchozího přiřazení daným signálům. op_out.wcode <= op_in.wcode; op_out.x <= op_in.immediate(data_width-1 downto 0); Tím jsme dosáhli zápisu přímého operandu do periferního registru. Ten je přístupný z vnějšku procesoru a o další zpracování a zobrazení se musí už postarat externí logika.
34
Kapitola 6
Testování a návrh dalšího směřování Testování procesoru probíhalo v několika fázích. V průběhu implementace jednotlivých komponent současně probíhala i jejich simulace. Z celkového pohledu ale není simulace kterékoliv z částí zvláště zajímavá, a proto se v podkapitole 6.1 setkáte výhradně s testováním procesoru jako celku. Současně s vytvářením byly prováděny strukturní kontroly RTL schémat. Podobnou sadou jako v případě simulace procesor prošel i v praktických testech prováděných na platformě FITkit. Krom potenciálního rozdílu mezi simulací a skutečným chováním byly rozdíly taky v zahrnutí dalších komponent obklopujících procesor. Způsob validace byl pochopitelně také jiný - docházelo pouze k odečtu vybraných hodnot z LCD displeje. Zmíněné praktické testy jsou také uvedeny v podkapitole 6.2. Podkapitola 6.3 shrnuje využití prostředků základní konfigurace a v závěru kapitoly najdete náčrt dalšího směru vývoje procesoru (podkapitola 6.4).
6.1
Simulace
Simulace většinou slouží k testování a hledání chyb návrhu. V případě níže uvedených se ale jedná o závěrečné ověření schopností navrženého procesoru a jsou zaměřeny na jeho funkčnost jako celku. 1. Základní funkčnost Simulace ověřovala správného napojení a chování stupňů procesoru. Vstupem byl následující seznam instrukcí v instrukční paměti:1 00 01 02 03 04 XX
PWi PWi PWi PWi PWi NOP
1; 2; 3; 4; 1; ;
NOP NOP NOP NOP NOP NOP -- pro zbytek instrukční paměti
1
instrukce PWi provádí zápis přímého operandu (indikované písmenem i) na periferní registr. V případě operace PW je bráno nejnižších n bitů operace jako ukazatel do souboru registrů, odkud bude hodnota vystavena na periferní registr.
35
Obrázek 6.1: Ukázka simulace základní funkčnosti.
Po dobu 50 ns je držen signál reset (rst) na logické jedničce. Poté je s nejbližší náběžnou hranou hodinového signálu spuštěno provádění instrukce z adresy 00h. Pro snadné ověření postupu instrukce jednotkami je dobré sledovat instrukci z adresy 01h (zápis hodnoty 02h na výstupní port). V čase 65 ns dochází k vystavení adresy, v čase 75 ns je instrukce na výstupu z jednotky FETCH, v čase 85ns na výstupu jednotky DECODE, v čase 95ns prochází část instrukce jednotkou READ a v čase 105ns je vystavena celá instrukce jednotce EXECUTE ke zpracování. V čase 115ns je požadovaná hodnota vystavena na výstupním portu 1. Výsledek si můžete prohlédnout na ilustraci 6.1, případně v plné velikosti v dodatku C na obrázku C.1. Výsledky testů jsou podle očekávání a test se dá označit za úspěšně dokončený. 2. Zpracování jednotkami ALU a BRANCH Simulace testovala provoz zmíněných jednotek na jednoduchých operacích (ADD, GOTO). Obsah instrukční paměti byl následující: 00 01 02 03 04 05 06 07 08 XX
PW Fh; PW Fh; PW Fh; PW Fh; PWi 1; PWi 2; PWi 3; PWi 4; PWi 5; NOP ;
NOP NOP ADDi Fh, Fh, 1 GOTO -2 NOP NOP NOP NOP NOP NOP -- pro zbytek instrukční paměti
Výstup simulace najdete v dodatku C na obrázku C.2. Po přičtení jedničky do registru na adrese Fh následuje instrukce relativního skoku o -2 na instrukce 01. Do 36
instrukce skoku (včetně) je na výstup posílán obsah registru Fh. Následující instrukce na periferní registr posílají svou relativní vzdálenost od operace skoku. Z výstupů simulace jde vyčíst, že skok je proveden za čtyři hodinové cykly od vyslání této operace do instrukční linky. Současně dochází k vyslání a vykonání čtyř instrukcí za skokem. Je to jedno z možných a akceptovatelných chování. Nicméně stojí za úvahu, jestli nepřesunout zpracování skoků do dřívějších stupňů (obtížné v případě podmíněných skoků). Další možností je plnění následných instrukcí operací NOP (ať už na straně softwaru vhodným naplněním instrukční paměti, nebo na straně hardwaru nahrazením čtyř instrukcí po zjištění skoku). Operace součtu správně inkrementuje registr Fh a operace PW požadovaný výsledek korektně čte. Tím je současně ověřeno, že jde předávat výsledky z jedné linky přes soubor registrů lince druhé. 3. Vykonávání rekonfigurovaným procesorem za využití všech typů jednotek. Pro potřeby tohoto testu byla konfigurace procesoru významně pozměněna. Procesor obsahuje dvojnásobný počet cest proti předchozím testům (tedy čtyři). Krom definování jednotek pro třetí a čtvrtou cestu byla změněna i konfigurace prvních dvou následujícím způsobem: constant d_conf: decoder_config := ( true, false, false, true, false, false, true, false, false, false, true, true,
true, true, true, true);
Podobně jako v předchozích testech i v tomto případě bude základní funkčnost ověřena na výkonu jednoduchého kódu. Protože je test zaměřen na změnu struktury, je v dodatku D uvedeno RTL schéma upraveného procesoru (obrázek D.6). Na daném schématu je pro kontrolu rozbalen stupeň EXECUTE. Pro množství údajů je sice v tištěné podobě obtížné přečíst jednotlivé popisky, jde ale poznat základní strukturní změny (větší počet signálů, více funkčních jednotek v rozbaleném stupni EXECUTE). 00 NOP; NOP; 01 NOP; ADDi 1h, 1h, (1); 02 NOP; NOP; 03 STB 0[1h], Fh; ADDCG Fh, Fh, 0h, 4h, 4h; 04 BRF 4h, (-4); NOP; XX NOP; NOP;
PW PW PW PW PW PW
Fh; NOP; Fh; ADDi 0h, 0h, (8); Fh; NOP; Fh; NOP; Fh; NOP; Fh; NOP;
Program opět běžel v cyklu. Tentokrát pro vytvoření cyklu nebyl použit příkaz GOTO, místo něj byl použit podmíněný skok BRF (branch on false). Ten kontroloval adresu 4h v registru pro větvení a skok prováděl, dokud se v něm neobjevila logická jednička. Pro otestování paralelního zápisu do souboru registrů prováděla instrukce 01 souběžnou inkrementaci souboru registrů na adresách 0h (inkrementace o osm) a 1h (inkrementace o jedna). Obsah adresy 0h byl využit při dalším součtu, kdy obsah adresy Fh byl o ní v cyklu inkrementován (posloupnost součtů tedy vypadala následovně: 8 + 16 + 24 + 32 + . . . ). K tomuto součtu byla využita operace ADDCG (add 37
and generate carry), která navíc generuje bit carry a ukládala ho do registru větvení právě na adresu 4h, kterou kontrolovala instrukce BRF. Po přetečení došlo k ukončení cyklu. Souběžně byla adresa registru 1h (cyklicky inkrementovaná o jedničku) využita jako ukazatel do datové paměti, kam se ukládaly mezivýsledky operace ADDCG pomocí instrukce STB (store byte). Procesor neprošel testem napoprvé - byla zjištěna chyba, kdy při operaci STB nedocházelo k pozastavení výstupů ze souboru registrů a registru pro větvení. Po opravení této chyby už test proběhl v pořádku a výstup si můžete prohlédnout na obrázku C.3.
6.2
Simulace a praktické testy
Za účelem praktického testování, ladění a prezentace bylo do návrhu pro FITkit přidáno několik specifických vlastností. Na hodinový signál byla připojena dělička a procesor pracuje až s jejím výstupem. Ten je vydělen hodnotou 224 a výsledná frekvence se řádově pohybuje kolem hodnoty 1 Hz. Druhou úpravou je přidání operace provádějící zápis na periferní registr (detailně komentováno v předchozí kapitole). Nejnižší čtyři bity zmíněného výstupu jsou pomocí další logiky překládány na kódy znaků užívané FITkitem. Pro výstup je využit řadič LCD displeje, jehož autorem je Zdeněk Vašíček (ve verzi ze dne 1.9.2008). Zmíněný řadič je součástí repozitáře, který stahuje aplikace QDevKit. Poslední úpravou využitou při testování je napojení LED diody D4 na výstup zpomaleného hodinového signálu. To umožní sledovat kolik taktů uběhlo. Pro ověření funkčnosti byly vytvořeny testy, které se nejprve zaměřily na základní funkčnost. Dále byly nároky zvyšovány a testy se poté zaměřily na další schopnosti procesoru nahraného v FPGA FITkitu. 1. Základní funkčnost Cílem prvního testu bylo ověřit, zda instrukce procházejí stupni procesoru, schopnost použít LCD displej a zdali je LED dioda D4 správně napojena na zdroj hodinového signálu. K otestování byl v instrukční paměti stejný kód, jako v případě první simulace. Instrukce PWi provádí zápis přímého operandu do periferního registru. Nejnižší čtyři bity jsou interpretovány jako hexadecimální hodnota a zaslány do LCD kontroléru. Výstupem na displej byly následující hodnoty (k odečítání hodnot docházelo v době, kdy byla LED D4 zhasnuta): 0-0-0-0 -1-2-3-4-1-1-1-1-... ...-1-2-3-4-1-1-1-1-...
První čtyři nuly jsou způsobeny zpožděním daným vícestupňovou architekturou procesoru. Po zapsání poslední hodnoty tato zůstala zobrazena. Neočekávané, přesto korektní zjištěné chování bylo, že po průchodu celou instruční pamětí došlo ke skoku na její začátek a sekvence se opakovala (už bez úvodních nul). Výstup odpovídal správným hodnotám a proto se dá test považovat za úspěšně dokončený. 2. Zpracování jednotkami ALU a BRANCH Test byl zaměřen na vykonávání jednoduché ALU operace (sčítání) v cyklu. Obsah 38
instrukční paměti odpovídal druhé simulaci. Výstup odečtený z LCD displeje vypadal následovně (v dané chvíli je na LCD zobrazena jediná hodnota, pro lepší čtení je tady výstup zformátovaný do logických skupin): 0-0-0-0 -0-0-0-0 -1-1 -2-2 -3-3
-1-2-3-4 -1-2-3-4 -1-2-3-4 -1-2-3-4-...
Opět je na začátku serie nul. První čtyři odpovídají naplnění instrukční linky a poté nasledují instrukce zobrazení obsahu registru Fh. V první iteraci je nulový. Následuje průchod prvními čtyřmi instrukcemi výstupu s přímým operandem. Poté dochází ke skoku. Každá další iterace zvýší obsah registru o jedna. Test se chová stejně jako simulace a proto se dá považovat za úspěšně dokončený.
6.3
Časování a spotřebované zdroje
Ke zjištění spotřebovaných zdrojů byla využita základní konfigurace. Jedinou úpravou bylo vynechání děličky hodinového signálu, aby mohla být určena maximální frekvence, na které je daná konfigurace schopna běžet (hodinový signál procesoru byl připojen na signál SMCLK). Hodnoty byly odečteny z logu, který QDevKit vytváří při překladu. Jelikož se pro popis využívají prakticky výhradně pouze anglické termíny, nebudou tyto v níže uvedené tabulce překládány. Slices Slice Flip Flops 4 input LUTs bonded IOBs BRAMs GCLKs DCMs
použito 744 631 1153 116 2 4 1
dostupných 768 1536 1536 124 4 8 2
využití 96% 41% 75% 93% 50% 50% 50%
Minimální zjištěná doba hodinového cyklu je 19,379 ns, což znamená maximální frekvenci 51,603 MHz.
6.4
Směry dalšího vývoje
Výše uvedené testování se zaměřilo převážně na základní funkčnost struktury procesoru a vynechalo podrobné testování jednotlivých operací. Proto v případě dalšího rozvoje tohoto projektu navrhuji zaměřit se právě na podrobné otestování. Druhým směrem vývoje by mohlo být vylepšení funkčnosti. V tomto ohledu je tu hned několik možností. První úpravy by měly směřovat na dokončení implementace všech operací. Z fáze návrhu se také nedostaly vlastnosti jako forwarding a jednotka pro správu procesoru. Mohlo by se jednat o zajímavé doplňky. Už ve fází návrhu byla vynechána možnost tvořit clustery. To by nejspíš znamenalo výraznější zásahy do kódu. 39
Jako poslední z hardwarových úprav příchází do úvahy optimalizace. Jedním cílem může být zvýšení výkonu - v případě časových omezení připadá v úvahu rozdělení stupně EXECUTE na dvě a dále tím prodloužit zřetězení procesoru. Opačným směrem může jít pokus o snížení spotřebovaných prostředků. V tomto místě je nejvíce třeba se zaměřit na datovou paměť. Její implementace mimo FPGA by ušetřila velké množství prostředků. Druhá komponenta, na kterou by se měly zaměřit optimalizace, je soubor registrů. Z uvedených úvah vybočuje poslední zamýšlená vlastnost. Tou je spolupráce s projektem LISSOM. Především pak využití assembleru, případně i kompilátoru jazyka C.
40
Kapitola 7
Závěr V rámci této diplomové práce byl implementován obecný, konfigurovatelný VLIW procesor. Ten se podařilo úspěšně syntetizovat pro různý počet cest a konfiguraci funkčích jednotek. Pro potřeby testování na platformě FITkit využívající FPGA rodiny Spartan 3 se pokusy zaměřily na dvoucestný VLIW procesor. V těchto praktických testech se nejprve podařila ukázat základní funkčnost vykonávat aritmetické operace s výstupem na LCD displej. Úspěšně proběhly i testy provádějící skoky, předávání výsledků přes soubor registrů a paralelní vykonávání operací na obou cestách. Navíc procesor prošel i testem rekonfigurace a byl úspěšně simulován jako čtyřcestný s jinak navěšenými jednotkami. Technická zpráva přináší v druhé kapitole obecný pohled na procesory a jejich dělení především v závislosti na instrukční sadě. Kapitola třetí popisuje vývojové paradigma VLIW procesorů a vysvětlení instrukčního paralelismu a jeho vztahu k superskalárním procesorům. Tyto dvě kapitoly shrnují teoretická východiska. Zmíněná východiska vedla k vytvoření návrhu generického procesoru založeného na architektuře VLIW (čtvrtá kapitola). Už vlastní návrh počítá s širokými možnostmi konfigurace procesoru - od velikosti instrukční a datové paměti, velikosti souboru registrů přes počet paralelních cest, šířku datové cesty, až po možnost jednoduchým a přímočarým postupem doplňovat instrukce nové. Důležitou součástí fáze návrhu byl postup při generování instrukčního slova resp. jeho operací. Důraz byl kladen na jednoduchost dekódování a flexibilitu přidávání nových operací. Ty navíc nijak neomezují možnost měnit počet a konfiguraci funkčních jednotek. Návrh bohužel obsahuje i části, které se z technických důvodů nepřenesly do implementace - jmenovitě forwarding a řídící jednotka pro sledování a úpravu vnitřního stavu procesoru. Cílem diplomové práce bylo implementovat obecný procesor. I proto je v technické zprávě kladen velký důraz právě na tuto oblast. V páté kapitole je do detailu popsána struktura a v následujících je předvedena funkčnost. Procesor prošel všemi provedenými testy, těžiště práce ale nejde hledat ve vytvoření bezchybného procesoru. K zajištění dokonalého chodu by bylo potřeba ještě mnoho testování. Ve zmíněném smyslu naplňuje vytvořený procesor význam slova prototyp. Hlavní přínos jde spatřit ve vytvoření velice flexibilní struktury, kterou lze upravovat nastavením několika parametrů. Přestože jednotlivé části můžou potenciálně obsahovat chyby, celkově se zdá být navržená struktura funkční a dostatečně robustní. Přitom i s omezenými prostředky, jaké poskytuje FPGA v platformě FITkit, umožňuje dvoucestné a v případě některých konfigurací i trojcestné zpracování.
41
Literatura [1] Armstrong, J.R., Gray, F.G.: VHDL Design Representation and Synthesis, 2nd edition. Prentice Hall, 2000, ISBN 0-13-021670-4. [2] De Gelas, J.: Westmere-EX: Intel’s Flagship Benchmarked. http://www.anandtech.com/show/4285/westmereex-intels-flagship-benchmarked, 2010 [cit. 2011-05-20]. [3] Dvořák, V.: Architektura procesorů. http://www.fit.vutbr.cz/study/course-l.php?id=6726, 2009 [cit. 2011-05-07]. [4] Fisher, A.J.: Very Long Instruction Word architectures and the ELI-512. Association for Computing Machinery, 1983, ISBN 0-89791-101-6. [5] Fisher, A.J.: Embedded Computing: A VLIW Approach to Architecture, Compilers and Tools. Morgan Kaufman Publishers, Inc., 2005, ISBN 1-55860-766-8. [6] Fučík, O.: FITkit. http://merlin.fit.vutbr.cz/FITkit/, 2008 [cit. 2011-05-07]. [7] Fučík, O.: Pokročilé číslicové systémy. http://www.fit.vutbr.cz/study/courses/index.php?id=5951, 2008 [cit. 2011-05-07]. [8] Hennessy, J.L., Patterson, D.A.: Computer Architecture - A Quantitative Approach. Morgan Kaufman Publishers, Inc., 2006, ISBN 1-55860-596-7. [9] Hewlett Packard: VEX Toolchain. http://www.hpl.hp.com/downloads/vex/, 2008 [cit. 2011-05-07]. [10] Intel: Intel(R) Architecture Software Developer’s Manual, Volume 2: Instruction Set Reference Manual. http://www.intel.com/design/itanium/manuals/245319.htm, 1999 [cit. 2011-05-07]. [11] Intel: Intel(R) atom processor core made FPGA-synthesizable. http://www.mendeley.com/research/intel-atom-processor-core-made -fpgasynthesizable/#, 2009 [cit. 2011-05-07]. [12] Kilts, S.: Advanced FPGA Design: Architecture, Implementation, and Optimization. Prentice Hall, 2007, ISBN 978-0-470-05437-6. [13] Krad, H., Al-Taie, A. Y.: A New Trend for CISC and RISC Architectures. http://qu.academia.edu/AwsYousif/Papers/120709/ A New Trend for CISC and RISC Architectures, 2007 [cit. 2011-05-14].
42
[14] Kuběna, P.: Implementace obecného VLIW procesoru v FPGA. VUT v Brně, 2009. [15] Sawyer, N., Defossez M.: Quad-Port Memories in Virtex Devices. http://www.xilinx.com/support/documentation/application notes/xapp228.pdf, 2002 [cit. 2011-05-14]. [16] Thijs van As: r-VEX: A Reconfigurable and Extensible VLIW Processor. http://code.google.com/p/r-vex/, 2008 [cit. 2011-05-07]. [17] Vašíček, Z.: QDevKit - Překladový systém. http://merlin.fit.vutbr.cz/FITkit/docs/navody/kompilacev2.html, 2009 [cit. 2011-05-14]. [18] Wang, P.H., Collins J.D., et.al: Intel(R) Itanium(R) Architecture Software Developer’s Manual - Volume 3: Instruction Set Reference. http://www.intel.com/design/itanium/manuals/245319.htm, 2010 [cit. 2011-05-07]. [19] Xilinx(R): Spartan-3 Generation FPGA User Guide. http://www.xilinx.com/support/documentation/user guides/ug331.pdf, 2010 [cit. 2011-05-14]. [20] Zhang, W.: VHDL Tutorial: Learn by Example. http://esd.cs.ucr.edu/labs/tutorial/, 2001 [cit. 2011-05-14]. [21] Zwoli´ nsky, M.: Digital System Design with VHDL, 2nd edition. Prentice Hall, 2004, ISBN 0-13-039985-X.
43
Dodatek A
Seznam instrukcí VEXu Seznam je převzat z [5]. Integer arithmetic operations: Operation ADD t=s1,s2im ADDCG t,b=b,s1,s2 AND t=s1,{s2/im} ANDC t=s1,{s2/im} DIVS t,b=b,s1,s2 MAX t=s1,{s2/im} MAXU t=s1,{s2/im} MIN t=s1,{s2/im} MINU t=s1,{s2/im} OR t=s1,{s2/im} ORC t=s1,{s2/im} SH1ADD t=s1,{s2/im} SH2ADD t=s1,{s2/im} SH3ADD t=s1,{s2/im} SH4ADD t=s1,{s2/im} SHL t=s1,{s2/im} SHR t=s1,{s2/im} SHRU t=s1,{s2/im} SUB t={s2/im},s1 SXTB t=s1 SXTH t=s1 ZXTB t=s1 ZXTH t=s1 XOR t=s1,{s2/im}
Description Add Add with carry and generate carry Bitwise AND Bitwise complement and AND Division step with carry and generate carry Maximum signed Maximum unsigned Minimum signed Minimum unsigned Bitwise OR Bitwise complement and OR Shift left 1 and add Shift left 2 and add Shift left 3 and add Shift left 4 and add Shift left Shift right signed Shift right unsigned Subtract Sign extend byte Sign extend half Zero extend byte Zero extend half Bitwise exclusive OR
44
Multiplication operations: Operation Description MPYLL t=s1,{s2/im} Multiply signed low 16 x low 16 bits MPYLLU t=s1,{s2/im} Multiply unsigned low 16 x low 16 bits MPYLH t=s1,{s2/im} Multiply signed low 16 x high 16 bits MPYLHU t=s1,{s2/im} Multiply unsigned low 16 x high 16 bits MPYHH t=s1,{s2/im} Multiply signed high 16 x high 16 bits MPYHHU t=s1,{s2/im} Multiply unsigned high 16 x high 16 bits MPYL t=s1,{s2/im} Multiply signed low 16 x 32 bits Multiply unsigned low 16 x 32 bits MPYLU t=s1,{s2/im} MPYH t=s1,{s2/im} Multiply signed high 16 x 32 bits MPYHU t=s1,{s2/im} Multiply unsigned high 16 x 32 bits MPYHS t=s1,{s2im} Multiply signed high 16 x 32, shift left 16 Logical and select operations: Operation Description CMPEQ {t/b}=s1,{s2/im} Compare (equal) CMPGE {t/b}=s1,{s2/im} Compare (greater equal - signed) CMPGEU {t/b}=s1,{s2/im} Compare (greater equal - unsigned) CMPGT {t/b}=s1,{s2/im} Compare (greater - signed) CMPGTU {t/b}=s1,{s2/im} Compare (greater - unsigned) CMPLE {t/b}=s1,{s2/im} Compare (less than equal - signed) CMPLEU {t/b}=s1,{s2/im} Compare (less than equal - unsigned) CMPLT {t/b}=s1,{s2/im} Compare (less than - signed) CMPLTU {t/b}=s1,{s2/im} Compare (less than - unsigned) CMPNE {t/b}=s1,{s2/im} Compare (not equal) NANDL {t/b}=s1,s2 Logical NAND NORL {t/b}=s1,s2 Logical NOR ORL {t/b}=s1,s2 Logical OR SLCT t=b,s1,{s2/im} Select s1 on true condition SLCTF t=b,s1,{s2/im} Select s1 on false condition Intercluster Operation SEND s1, im RECV t = im
operations: Description Send s1 to the path identified by im Assigns the value from the path identified by im to t
Memory operations: Operation LDW{.d}{.s}{.l} t = im[s] LDH{.d}{.s}{.l} t = im[s1] LDHU{.d}{.s}{.l} t = im[s1] LDB{.d}{.s}{.l} t = im[s1] LDBU{.d}{.s}{.l} t = im[s1] STW{.s}{.l} im[s1] = s2 STH{.s}{.l} im[s1] = s2 STB{.s}{.l} im[s1] = s2 PFT{.s}{.l} im[s1]
Description Load word Load halfword signed Load halfword unsigned Load byte signed Load byte unsigned Store word Store halfword Store byte Prefetch 45
Control operations: Operation Description GOTO off Unconditional relative jump IGOTO lr Unconditional absolute indirect jump to link register CALL lr = im Unconditional relative call ICALL lr = lr Unconditional absolute indirect call to link register BR b, off Conditional relative branch on true condition BRF b, off Conditional relative branch on false condition RETURN t = t, off, lr Pop stack frame (t = t + off) and goto link register Return from interrupt RFI XNOP n Multicycle nop (advance the pipeline for n cycles)
46
Dodatek B
Operace vybrané k implementaci Operační kód obsahuje pouze prvních sedm bitů. Osmý bit je vyhrazen pro indikátor přímého operandu. a - u operací porovnání určuje cíl zápisu (0 indikuje soubor registrů, 1 registr větvení) Konstrukce operací: ALU, MUL operandy v souboru registrů: opcode + target + src1 + src2 + br\_target + br\_src (+ fill blank) s přímým operandem: opcode + target + src1 + br\_src + immediate (+ fill blank) MEM opcode + dsl + target/src2 + src1 + immediate (+ fill blank) BRANCH operandy v souboru registrů: opcode + br_src (+ fill blank) + immediate_offset (+ fill blank) s přímým operandem: opcode + target + link_register (+ fill blank) + immediate_offset
47
Operační kód 0000000 0000001 0000010 0000100 0001000 0001001 0001100 0001101 0001110 0010000 0010001 0010010 0010011 0010100 0010101 0010110 0011000 0011001 0011010 0011011 0011100 0011101 0011110 0011111 01a0000 01a0001 01a0010 01a0011 01a0100 01a0101 01a0110 01a0111 01a1000 01x1001 01a1010 01a1011 01a1100 01a1101 01a1110
Název ADD ADDCG SUB DIVS AND ANDC OR XOR XORC SH1ADD SH2ADD SH3ADD SH4ADD SHR SHRU SHL ZXTB ZXTH SXTB SXTH MIN MINU MAX MAXU CMPG CMPGU CMPL CMPLU CMPGE CMPGEU CMPLE CMPLEU CMPEQ CMPNEQ SLCT SLCF ORL NORL NANDL
Implementováno ano ano ano ne ano ano ano ano ano ano ano ano ano ne ne ne ano ano ano ano ano ano ano ano ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne
48
Otestováno ano ano ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne ne
Operační kód 1010000 1010001 1010010 1011000 1011001 1011010 1100000 1100001 1100010 1100011 1100100 1100101 1100110 1100111 1110000 1111111
Název LDB LDH LDW STB STH STW GOTO GOTOI CALL CALLI BR BRF RET RFI PW NOP
Implementováno ano ne ne ano ne ne ano ne ne ne ano ano ne ne ano ano
49
Otestováno ano ne ne ano ne ne ano ne ne ne ne ano ne ne ano ano
Dodatek C
Simulace V této části najdete výstupy simulace provedené pro podkapitolu 6.1. Výstupy se nacházejí v pořadí, v jakém byly testy uváděny ve jmenované kapitole.
50
0 ns
20 ns
40 ns
60 ns
80 ns
100 ns
120 ns
140 ns
Obrázek C.1: Základní funkčnost
51
50 ns
100 ns
150 ns
200 ns
250 ns
300 ns
Obrázek C.2: Zpracování jednotkami ALU a BRANCH
52
0 ns
200 ns
400 ns
600 ns
800 ns
1,000 ns
Obrázek C.3: Vykonávání rekonfigurovaným procesorem za využití všech čtyř jednotek.
53
Dodatek D
Schémata Na následujících stránkách najdete RTL implementovaného VLIW procesoru a jeho komponent. Pro generování byla použita varianta pro FITkit. Pro vygenerování byl použit Xilinx ISE 13.1. Současná verze bohužel obsahuje chybu, která v případě víceúrovňového designu občas nevykresluje některé části.
54
Obrázek D.1: Nejvyšší úroveň pohledu na FPGA. Implementace této úrovně nebyla součástí projektu a jedná se o design přístupný k FITkitu. fpga:1 clkgen SMCLK
CLK
tlv_bare_ifc CLKFX_OUT
KOUT(3:0)
CLK1X_OUT RST
LOCKED_OUT
ACLK
KIN(3:0)
KIN(3:0)
RA(14:0)
RA(14:0)
ADIN
X(45:0)
IRQ
DCMclkgen
ADOUT
CLK
FCLK
FCLK
RESET
LE
LE
LEDF
LEDF
LRS
LRS
LRW
LRW
RCAS
RCAS
RCKE
RCKE
RCLK
RCLK
RCS
RCS
RDQM
RDQM
RRAS
RRAS
RWE
RWE
SPI_CS
or2b1 SMCLK
I1 O I0
SPI_CS
reset_imp_reset1
SPI_DI SPI_DI_REQ
SPI_DO
SPI_DO_VLD
AFBUS(11:0)
AFBUS(11:0)
LD(7:0)
LD(7:0)
P3M(7:0)
P3M(7:0)
RD(7:0)
RD(7:0)
ABCLK ALRCIN ALRCOUT
fpga_inst KOUT(3:0) ACLK
SPI_ctrl CLK
CS
DI
DI_REQ
RST DO SPI_CLK
SPI_CLK
SPI_FPGA_CS
SPI_CS
SPI_DO
SPI_MOSI
DO_VLD SPI_MISO
spictrl gnd G
XST_GND
fpga
55
SPI_DI
Obrázek D.2: Komponenta tlv bare ifc, v níž je obsažen samotný procesor. Dále se zde nachází LCD kontrolér (součást repozitáře QDevKitu), který je napojen na výstup z CPU, ROM paměť překládající hexadecimální hodnoty na znaky a čítač použitý k dělení frekvence. tlv_bare_ifc:1 KOUT(3:0)
ACLK
ADOUT
Mrom_dis_data1 addrA(3:0)
BUFT
gnd G
doA(7:0)
Mrom_dis_data1
I
O
AFBUS(11:0)
T
XST_GND
CLK
FCLK
ALRCIN SMCLK ALRCOUT ABCLK SPI_CS IRQ RCKE SPI_DI_REQ RCLK ADIN SPI_DO RWE RDQM SPI_DO_VLD SPI_DI RCS RRAS RCAS KIN(3:0) RA(14:0)
vcc P
XST_VCC
BUFT
COUNTER:1 I
COUNT up port_data
port_q
O
P3M(7:0)
O
RD(7:0)
T
port_clk
cnt1
vliw_cpu RESET
BUFT
clk
data(0)(7:0)
I
rst
data(1)(7:0)
T
CPU LEDF
lcd_ctrl_high DATA(7:0) CLEAR
LE
LE
LRS
LRS
LRW
LRW
LD(7:0)
LD(7:0)
CLK RESET WRITE
LCD_CTRL fpga_inst
56
rst
clk
din(63:0)
jump
wr_en
rst
en
jump_addr(6:0)
clk
dout(63:0)
en
IMEM
imem
clk
din(63:0)
addr(6:0)
FETCH
fetch
ops(1)(31:0)
ops(0)(31:0)
addr(6:0)
decoded_ops_ri(0)_src_a(3:0)
decoded_ops_ri(0)_wcode_pc decoded_ops_ri(0)_wcode_pr decoded_ops_ri(0)_wcode_t
ops(0)_wcode_pc ops(0)_wcode_pr ops(0)_wcode_t
decoded_ops_ri(1)_wcode_pr decoded_ops_ri(1)_wcode_t
ops(1)_wcode_pr ops(1)_wcode_t
br_data_in(1:0)
57
C
D
ops_out(0)_immediate(11:0)
ops_in(0)_src_a(3:0)
READ
ops_out(0)_br_target(2:0)
fd
Q
MP_RF
rf_dout_b(1)(7:0)
rf_dout_b(0)(7:0)
rf_dout_a(1)(7:0)
rf_dout_a(0)(7:0)
br_dout(1:0)
mp_reg_file
MP_BR
mp_br
ops_out(0)_ucode(1:0) ops_out(0)_wcode_br ops_out(0)_wcode_lr ops_out(0)_wcode_pc ops_out(0)_wcode_pr
ops_in(0)_wcode_lr ops_in(0)_wcode_pc ops_in(0)_wcode_pr
ops_out(1)_b(7:0)
C
D
rst_d2
fd
rst
en
clk
rf_data_b_in(1)(7:0)
rf_data_b_in(0)(7:0)
rf_data_a_in(1)(7:0)
rf_data_a_in(0)(7:0)
ops_in(1)_wcode_t
ops_in(1)_wcode_pr
ops_in(1)_wcode_pc
ops_in(1)_wcode_lr
Q
ops_out(1)_wcode_br
rf_addr_b(1)(3:0)
rf_addr_b(0)(3:0)
rf_addr_a(1)(3:0)
rf_addr_a(0)(3:0)
ops_out(1)_wcode_t
ops_out(1)_wcode_pr
ops_out(1)_wcode_pc
ops_out(1)_wcode_lr
ops_out(1)_ucode(1:0)
ops_out(1)_target(3:0)
ops_in(1)_wcode_br
ops_in(1)_ucode(1:0)
ops_out(1)_opcode(6:0)
ops_out(1)_immediate(11:0)
ops_out(1)_br_target(2:0)
ops_in(1)_target(3:0)
ops_in(1)_src_a(3:0) ops_in(1)_src_b(3:0)
ops_in(1)_opcode(6:0)
ops_in(1)_imm_flag
ops_out(1)_br
ops_out(1)_a(7:0)
ops_in(1)_br_target(2:0) ops_in(1)_immediate(11:0)
ops_in(1)_br_src(2:0)
ops_out(0)_wcode_t
ops_out(0)_target(3:0)
ops_in(0)_wcode_br
ops_in(0)_wcode_t
ops_out(0)_opcode(6:0)
ops_in(0)_src_b(3:0) ops_in(0)_target(3:0) ops_in(0)_ucode(1:0)
ops_out(0)_br
ops_in(0)_imm_flag
ops_out(0)_b(7:0)
ops_out(0)_a(7:0)
br_addr(1)(2:0)
br_addr(0)(2:0)
ops_in(0)_opcode(6:0)
rst_d1
rst
clk_base
wr_en(1:0)
wr_data(1)(7:0)
wr_data(0)(7:0)
wr_addr(1)(3:0)
wr_addr(0)(3:0)
rf_addr_b(1)(3:0)
rf_addr_b(0)(3:0)
rf_addr_a(1)(3:0)
rf_addr_a(0)(3:0)
rst
clk_base
wr_en(1:0)
wr_addr(1)(2:0)
wr_addr(0)(2:0)
rd_addr(1)(2:0)
rd_addr(0)(2:0)
read
CPU
D
C
en
ready_ops_ro(1)_wcode_t
ready_ops_ro(1)_wcode_pr
ready_ops_ro(1)_wcode_pc
ready_ops_ro(1)_wcode_lr
ready_ops_ro(1)_wcode_br
ready_ops_ro(1)_ucode(1:0)
ready_ops_ro(1)_target(3:0)
ready_ops_ro(1)_opcode(6:0)
ready_ops_ro(1)_immediate(11:0)
ready_ops_ro(1)_br_target(2:0)
ready_ops_ro(1)_br
ready_ops_ro(1)_b(7:0)
ready_ops_ro(1)_a(7:0)
ready_ops_ro(0)_wcode_t
ready_ops_ro(0)_wcode_pr
ready_ops_ro(0)_wcode_pc
ready_ops_ro(0)_wcode_lr
ready_ops_ro(0)_wcode_br
ready_ops_ro(0)_ucode(1:0)
ready_ops_ro(0)_target(3:0)
ready_ops_ro(0)_opcode(6:0)
ready_ops_ro(0)_immediate(11:0)
ready_ops_ro(0)_br_target(2:0)
rst_d3
fd Q
IS_REG_READ_EXEC
rst
clk
pc_out(6:0)
ready_ops_ri(1)_wcode_t
ready_ops_ri(1)_wcode_pr
decoded_ops_ro(1)_wcode_t
decoded_ops_ro(1)_wcode_pr
decoded_ops_ro(1)_wcode_pc
rst
IS_REG_FETCH_DECODE
br_din(1:0)
ready_ops_ri(1)_wcode_lr
decoded_ops_ro(1)_wcode_br
en
ops_in(0)_br_src(2:0) ops_in(0)_br_target(2:0)
pc_out(6:0)
ready_ops_ri(1)_wcode_br
decoded_ops_ro(1)_ucode(1:0)
ops_in(0)_immediate(11:0)
pc_in(6:0) fetched_instr_ro(1)(31:0)
ready_ops_ri(1)_ucode(1:0)
decoded_ops_ro(1)_target(3:0)
ready_ops_ri(1)_wcode_pc
ready_ops_ri(1)_target(3:0)
decoded_ops_ro(1)_wcode_lr
ready_ops_ri(1)_opcode(6:0)
decoded_ops_ro(1)_src_b(3:0)
ready_ops_ri(1)_immediate(11:0)
ready_ops_ri(1)_br_target(2:0)
ready_ops_ri(1)_br
ready_ops_ri(1)_b(7:0)
ready_ops_ri(1)_a(7:0)
ready_ops_ri(0)_wcode_t
ready_ops_ri(0)_wcode_pr
ready_ops_ri(0)_wcode_pc
decoded_ops_ro(1)_src_a(3:0)
decoded_ops_ro(1)_opcode(6:0)
decoded_ops_ro(1)_imm_flag
decoded_ops_ro(1)_immediate(11:0)
decoded_ops_ro(1)_br_target(2:0)
decoded_ops_ro(1)_br_src(2:0)
decoded_ops_ro(0)_wcode_t
decoded_ops_ro(0)_wcode_pr
decoded_ops_ro(0)_wcode_pc
clk
fetched_instr_ri(1)(31:0)
fetched_instr_ro(0)(31:0)
ready_ops_ri(0)_wcode_br
decoded_ops_ro(0)_wcode_br ready_ops_ri(0)_wcode_lr
ready_ops_ri(0)_ucode(1:0)
decoded_ops_ro(0)_ucode(1:0)
decoded_ops_ro(0)_wcode_lr
ready_ops_ri(0)_target(3:0)
decoded_ops_ro(0)_target(3:0)
ready_ops_ri(0)_opcode(6:0)
ready_ops_ri(0)_immediate(11:0)
decoded_ops_ro(0)_src_a(3:0) decoded_ops_ro(0)_src_b(3:0)
ready_ops_ri(0)_br_target(2:0)
decoded_ops_ro(0)_opcode(6:0)
ready_ops_ro(0)_br
ready_ops_ro(0)_b(7:0)
ready_ops_ro(0)_a(7:0)
pc_out(6:0)
reg_read_exec pc_in(6:0)
ready_ops_ri(0)_br
ready_ops_ri(0)_a(7:0) ready_ops_ri(0)_b(7:0)
decoded_ops_ro(0)_imm_flag
decoded_ops_ro(0)_immediate(11:0)
IS_REG_DECODE_READ
rst
en
clk
pc_in(6:0)
decoded_ops_ri(1)_wcode_lr decoded_ops_ri(1)_wcode_pc
ops(1)_wcode_pc
decoded_ops_ri(1)_wcode_br
ops(1)_wcode_br ops(1)_wcode_lr
decoded_ops_ri(1)_target(3:0) decoded_ops_ri(1)_ucode(1:0)
ops(1)_ucode(1:0)
ops(1)_src_b(3:0) ops(1)_target(3:0)
decoded_ops_ri(1)_src_a(3:0) decoded_ops_ri(1)_src_b(3:0)
ops(1)_src_a(3:0)
decoded_ops_ri(1)_opcode(6:0)
decoded_ops_ri(1)_imm_flag
decoded_ops_ri(1)_immediate(11:0)
decoded_ops_ri(1)_br_target(2:0)
ops(1)_opcode(6:0)
ops(1)_imm_flag
ops(1)_immediate(11:0)
ops(1)_br_target(2:0)
decoded_ops_ri(1)_br_src(2:0)
decoded_ops_ri(0)_wcode_lr
ops(0)_wcode_lr
ops(1)_br_src(2:0)
decoded_ops_ri(0)_wcode_br
decoded_ops_ri(0)_ucode(1:0)
ops(0)_ucode(1:0) ops(0)_wcode_br
decoded_ops_ri(0)_target(3:0)
ops(0)_target(3:0)
decoded_ops_ri(0)_src_b(3:0)
ops(0)_src_a(3:0) ops(0)_src_b(3:0)
decoded_ops_ri(0)_opcode(6:0)
decoded_ops_ri(0)_imm_flag
decoded_ops_ri(0)_immediate(11:0)
vliw_cpu:1
decoded_ops_ro(0)_br_target(2:0)
decoded_ops_ro(0)_br_src(2:0)
reg_decode_read decoded_ops_ri(0)_br_src(2:0) decoded_ops_ri(0)_br_target(2:0)
ops(0)_opcode(6:0)
ops(0)_imm_flag
ops(0)_immediate(11:0)
ops(0)_br_target(2:0)
ops(0)_br_src(2:0)
reg_fetch_decode
DECODE
decode
fetched_instr_ri(0)(31:0)
rst
clk
instr(1)(31:0)
instr(0)(31:0) addr(6:0)
inv O
rst_d4
fd
en_imp_en1
I
C
D
rst
clk
ops_in(1)_wcode_t
ops_in(1)_wcode_pr
ops_in(1)_wcode_pc
ops_in(1)_wcode_lr
ops_in(1)_wcode_br
ops_in(1)_ucode(1:0)
ops_in(1)_target(3:0)
ops_in(1)_opcode(6:0)
ops_in(1)_immediate(11:0)
ops_in(1)_br_target(2:0)
ops_in(1)_br
ops_in(1)_b(7:0)
ops_in(1)_a(7:0)
ops_in(0)_wcode_t
ops_in(0)_wcode_pr
ops_in(0)_wcode_pc
ops_in(0)_wcode_lr
ops_in(0)_wcode_br
ops_in(0)_ucode(1:0)
ops_in(0)_target(3:0)
ops_in(0)_opcode(6:0)
ops_in(0)_immediate(11:0)
ops_in(0)_br_target(2:0)
ops_in(0)_br
ops_in(0)_b(7:0)
ops_in(0)_a(7:0)
Q
EXEC
exec
stall_out
ops_out(1)_x(7:0)
ops_out(1)_wcode_t
ops_out(1)_wcode_pr
ops_out(1)_wcode_pc
ops_out(1)_wcode_lr
ops_out(1)_wcode_br
ops_out(1)_target(3:0)
ops_out(1)_br_target(2:0)
ops_out(1)_br
ops_out(0)_x(7:0)
ops_out(0)_wcode_t
ops_out(0)_wcode_pr
ops_out(0)_wcode_pc
ops_out(0)_wcode_lr
ops_out(0)_wcode_br
ops_out(0)_target(3:0)
ops_out(0)_br_target(2:0)
ops_out(0)_br ops_in(0)_br
rst
en
clk
ops_in(1)_x(7:0)
ops_in(1)_wcode_t
ops_in(1)_wcode_pr
ops_in(1)_wcode_pc
ops_in(1)_wcode_lr
ops_in(1)_wcode_br
ops_in(1)_target(3:0)
ops_in(1)_br_target(2:0)
ops_in(1)_br
ops_in(0)_x(7:0)
ops_in(0)_wcode_t
ops_in(0)_wcode_pr
ops_in(0)_wcode_pc
ops_in(0)_wcode_lr
ops_in(0)_wcode_br
ops_in(0)_target(3:0)
ops_in(0)_br_target(2:0)
WRITE
write
jump
rf_wren(1:0)
rf_data(1)(7:0)
rf_data(0)(7:0)
rf_addr(1)(3:0)
rf_addr(0)(3:0)
pr_data(1)(7:0)
pr_data(0)(7:0)
pr_change(1:0)
jump_addr(6:0)
br_wren(1:0)
br_data(1:0)
br_addr(1)(2:0)
br_addr(0)(2:0)
data(1)(7:0)
data(0)(7:0)
Obrázek D.3: Pohled na schéma samotného procesoru.
Obrázek D.4: Schéma stupně DECODE. decode:1 udecoder_alu instr(0)(31:0)
op(31:0)
ops(0)_br_src(2:0)
MUX:12
ops(0)_br_target(2:0) Data(23:0)
op_out_br_src(2:0)
port_result
op_out_br_target(2:0)
ops(0)_immediate(11:0) ops(0)_imm_flag
port_select op_out_immediate(11:0)
Mmux_ops<0>.br_src1
op_out_opcode(6:0)
ops(0)_opcode(6:0) ops(0)_src_a(3:0)
op_out_src_a(3:0)
ops(0)_src_b(3:0)
op_out_src_b(3:0)
ops(0)_target(3:0)
op_out_target(3:0) op_out_ucode(1:0)
ops(0)_wcode_br
MUX:20
op_out_imm_flag
ops(0)_wcode_lr Data(23:0)
op_out_wcode_br
port_result
op_out_wcode_lr
ops(0)_wcode_pc ops(0)_wcode_pr
port_select op_out_wcode_pc
Mmux_ops<0>.br_target1
op_out_wcode_pr
ops(0)_wcode_t ops(1)_br_src(2:0)
op_out_wcode_t
ops(1)_br_target(2:0) ops(1)_immediate(11:0)
decs[0].alu_udecs_cond.alu_udecs
ops(1)_imm_flag
clk
ops(1)_opcode(6:0)
udecoder_jump op(31:0)
ops(1)_src_a(3:0)
MUX:15
ops(1)_src_b(3:0) Data(95:0)
op_out_br_src(2:0)
port_result
ops(1)_target(3:0)
op_out_br_target(2:0) port_select op_out_immediate(11:0)
Mmux_ops<0>.immediate1
op_out_opcode(6:0)
ops(1)_wcode_pc
op_out_src_b(3:0)
ops(1)_wcode_pr
op_out_target(3:0)
rst
ops(1)_wcode_br ops(1)_wcode_lr
op_out_src_a(3:0)
ops(1)_wcode_t
op_out_ucode(1:0)
MUX:10
op_out_imm_flag Data(7:0)
op_out_wcode_br
port_result
op_out_wcode_lr port_select op_out_wcode_pc
Mmux_ops<0>.imm_flag1
op_out_wcode_pr op_out_wcode_t
decs[0].jump_udecs_cond.jump_udecs udecoder_spec op(31:0)
MUX:3 Data(55:0)
op_out_br_src(2:0)
port_result
op_out_br_target(2:0) port_select op_out_immediate(11:0)
Mmux_ops<0>.opcode1
op_out_opcode(6:0) op_out_src_a(3:0) op_out_src_b(3:0) op_out_target(3:0) op_out_ucode(1:0)
MUX:21
op_out_imm_flag Data(31:0)
op_out_wcode_br
port_result
op_out_wcode_lr port_select op_out_wcode_pc
Mmux_ops<0>.src_a1
op_out_wcode_pr op_out_wcode_t
decs[0].spec_udecs_cond.spec_udecs
udecoder_alu instr(1)(31:0)
op(31:0)
MUX:22 Data(31:0)
op_out_br_src(2:0)
port_result
op_out_br_target(2:0) port_select op_out_immediate(11:0)
Mmux_ops<0>.src_b1
op_out_opcode(6:0) op_out_src_a(3:0) op_out_src_b(3:0) op_out_target(3:0) op_out_ucode(1:0)
MUX:8
op_out_imm_flag Data(31:0)
op_out_wcode_br
port_result
op_out_wcode_lr port_select op_out_wcode_pc
Mmux_ops<0>.target1
op_out_wcode_pr op_out_wcode_t
decs[1].alu_udecs_cond.alu_udecs udecoder_mem op(31:0)
MUX:18 Data(7:0)
op_out_br_src(2:0)
port_result
op_out_br_target(2:0) port_select op_out_immediate(11:0)
Mmux_ops<0>.wcode.br1
op_out_opcode(6:0) op_out_src_a(3:0) op_out_src_b(3:0) op_out_target(3:0) op_out_ucode(1:0)
MUX:2
op_out_imm_flag Data(7:0)
op_out_wcode_br
port_result
op_out_wcode_lr port_select op_out_wcode_pc
Mmux_ops<0>.wcode.pc1
op_out_wcode_pr op_out_wcode_t
decs[1].mem_udecs_cond.mem_udecs udecoder_spec op(31:0)
MUX:9 Data(7:0)
op_out_br_src(2:0)
port_result
op_out_br_target(2:0) port_select op_out_immediate(11:0)
Mmux_ops<0>.wcode.t1
op_out_opcode(6:0) op_out_src_a(3:0) op_out_src_b(3:0) op_out_target(3:0) op_out_ucode(1:0)
MUX:11
op_out_imm_flag Data(23:0)
op_out_wcode_br
port_result
op_out_wcode_lr port_select op_out_wcode_pc
Mmux_ops<1>.br_src1
op_out_wcode_pr op_out_wcode_t
decs[1].spec_udecs_cond.spec_udecs
gnd G
MUX:14 Data(23:0)
XST_GND
port_result
port_select
Mmux_ops<1>.br_target1 MUX:5 Data(95:0)
port_result
port_select
Mmux_ops<1>.immediate1 MUX:13 Data(7:0)
port_result
port_select
Mmux_ops<1>.imm_flag1 MUX:1 Data(55:0)
port_result
port_select
Mmux_ops<1>.opcode1 MUX:16 Data(31:0)
port_result
port_select
Mmux_ops<1>.src_a1 MUX:17 Data(31:0)
port_result
port_select
Mmux_ops<1>.src_b1 MUX:6 Data(31:0)
port_result
port_select
Mmux_ops<1>.target1 MUX:19 Data(7:0)
port_result
port_select
Mmux_ops<1>.wcode.br1 MUX:7 Data(7:0)
port_result
port_select
Mmux_ops<1>.wcode.pr1 MUX:4 Data(7:0)
port_result
port_select
Mmux_ops<1>.wcode.t1
Mrom_ops<0>.ucode1 addrA(2:0)
doA(1:0)
ops(0)_ucode(1:0)
Mrom_ops<0>.ucode1 Mrom_ops<1>.ucode1 addrA(2:0)
doA(1:0)
ops(1)_ucode(1:0)
Mrom_ops<1>.ucode1 DECODE
58
Obrázek D.5: Schéma stupně EXECUTE. exec:1 alu
ops_out(0)_br
mu
ops_out(0)_br_target(2:0) ops_in(0)_a(7:0)
op_in_a(7:0)
ops_in(0)_b(7:0)
op_in_b(7:0)
ops_in(0)_br_target(2:0)
op_in_br_target(2:0)
ops_in(0)_immediate(11:0)
op_in_immediate(11:0)
ops_in(0)_opcode(6:0)
op_in_opcode(6:0)
op_out_br_target(2:0)
op_in_a(7:0)
op_out_br_target(2:0)
op_in_b(7:0)
ops_out(0)_target(3:0) op_out_target(3:0)
op_out_target(3:0)
op_in_br_target(2:0) ops_out(0)_wcode_br op_in_immediate(11:0)
op_out_x(7:0)
op_out_x(7:0) op_in_opcode(6:0)
ops_out(0)_wcode_lr op_out_br
ops_in(0)_target(3:0)
op_in_target(3:0)
ops_in(0)_ucode(1:0)
op_in_ucode(1:0)
clk
clk
op_in_target(3:0)
op_out_br
ops_out(0)_wcode_pc op_in_ucode(1:0) op_out_wcode_br
op_out_wcode_br
clk
ops_out(0)_wcode_pr op_out_wcode_lr
ops_in(0)_br
op_in_br
ops_in(0)_wcode_br
op_in_wcode_br
op_in_br
ops_in(0)_wcode_lr
op_in_wcode_lr
ops_in(0)_wcode_pc
op_in_wcode_pc
op_in_wcode_pc
ops_in(0)_wcode_pr
op_in_wcode_pr
op_in_wcode_pr
ops_in(0)_wcode_t
op_in_wcode_t
rst
rst
ops_out(0)_wcode_t
op_out_wcode_lr
op_in_wcode_br
op_out_wcode_pc
op_in_wcode_lr
ops_out(0)_x(7:0)
op_out_wcode_pc
op_out_wcode_pr ops_out(1)_br
op_out_wcode_pr
op_out_wcode_t
op_in_wcode_t stall op_out_wcode_t
stall_out
rst ops_out(1)_br_target(2:0)
func_units[0].alu_cond.alu_exec
func_units[1].mem_cond.mu_exec ops_out(1)_target(3:0)
ops_out(1)_wcode_br
ops_out(1)_wcode_lr
bru addr(6:0)
addr(6:0)
MUX:6 Data(11:0)
op_out_br_target(2:0)
ops_out(1)_wcode_pr
port_result
ops_out(1)_wcode_t
op_in_a(7:0) op_in_b(7:0)
ops_out(1)_x(7:0)
port_select
op_out_target(3:0)
Mmux_ops_out<0>.br_target1
op_in_br_target(2:0) op_out_x(7:0) op_in_immediate(11:0) op_in_opcode(6:0)
op_out_br
op_in_target(3:0) op_in_ucode(1:0)
op_out_wcode_br
MUX:10
clk op_out_wcode_lr
op_in_br
Data(3:0)
port_result
op_in_wcode_br op_out_wcode_pc
port_select
op_in_wcode_lr
Mmux_ops_out<0>.br1
op_in_wcode_pc
op_out_wcode_pr
op_in_wcode_pr op_in_wcode_t
op_out_wcode_t
rst
func_units[0].branch_cond.bru_exec spu op_in_a(7:0)
MUX:4 Data(15:0)
op_out_br_target(2:0)
port_result
op_in_b(7:0) port_select
op_out_target(3:0)
op_in_br_target(2:0)
Mmux_ops_out<0>.target1
op_in_immediate(11:0) op_out_x(7:0) op_in_opcode(6:0) op_in_target(3:0)
op_out_br
op_in_ucode(1:0) clk
op_out_wcode_br
MUX:3
op_in_br op_out_wcode_lr
op_in_wcode_br
Data(3:0)
port_result
op_in_wcode_lr op_out_wcode_pc
port_select
op_in_wcode_pc
Mmux_ops_out<0>.wcode.br1
op_in_wcode_pr
op_out_wcode_pr
op_in_wcode_t rst
op_out_wcode_t
func_units[0].spec_cond.spu_exec
alu ops_in(1)_a(7:0)
op_in_a(7:0)
ops_in(1)_b(7:0)
op_in_b(7:0)
ops_in(1)_br_target(2:0)
op_in_br_target(2:0)
ops_in(1)_immediate(11:0)
op_in_immediate(11:0)
ops_in(1)_opcode(6:0)
op_in_opcode(6:0)
MUX:9 Data(3:0)
op_out_br_target(2:0)
port_result
port_select
op_out_target(3:0)
Mmux_ops_out<0>.wcode.pc1 op_out_x(7:0)
ops_in(1)_target(3:0)
op_in_target(3:0)
ops_in(1)_ucode(1:0)
op_in_ucode(1:0)
op_out_br
clk ops_in(1)_br
op_in_br
ops_in(1)_wcode_br
op_in_wcode_br
ops_in(1)_wcode_lr
op_in_wcode_lr
ops_in(1)_wcode_pc
op_in_wcode_pc
ops_in(1)_wcode_pr
op_in_wcode_pr
op_out_wcode_br
MUX:11 op_out_wcode_lr
Data(3:0)
op_out_wcode_pc
ops_in(1)_wcode_t
port_result
port_select
Mmux_ops_out<0>.wcode.t1 op_out_wcode_pr
op_in_wcode_t rst
op_out_wcode_t
func_units[1].alu_cond.alu_exec spu op_in_a(7:0)
MUX:14 Data(31:0)
op_out_br_target(2:0)
port_result
op_in_b(7:0) op_in_br_target(2:0)
port_select
op_out_target(3:0)
Mmux_ops_out<0>.x1
op_in_immediate(11:0) op_out_x(7:0) op_in_opcode(6:0) op_in_target(3:0)
op_out_br
op_in_ucode(1:0) clk
op_out_wcode_br
MUX:1
op_in_br op_in_wcode_br
op_out_wcode_lr
Data(11:0)
port_result
op_in_wcode_lr op_out_wcode_pc
port_select
op_in_wcode_pc op_in_wcode_pr
Mmux_ops_out<1>.br_target1 op_out_wcode_pr
op_in_wcode_t rst
op_out_wcode_t
func_units[1].spec_cond.spu_exec gnd G
MUX:13 Data(3:0)
XST_GND
port_result
port_select
Mmux_ops_out<1>.br1 MUX:2 Data(15:0)
port_result
port_select
Mmux_ops_out<1>.target1 MUX:5 Data(3:0)
port_result
port_select
Mmux_ops_out<1>.wcode.br1 MUX:12 Data(3:0)
port_result
port_select
Mmux_ops_out<1>.wcode.pr1 MUX:8 Data(3:0)
port_result
port_select
Mmux_ops_out<1>.wcode.t1 MUX:7 Data(31:0)
port_result
port_select
Mmux_ops_out<1>.x1
EXEC
59
ops_out(1)_wcode_pc
rst
clk
addr(6: 0)
imem
din(127: 0)
en
rst
clk
j ump_addr(6: 0)
jump
dout(127:0)
en
IMEM
clk
wr_en
din(127: 0)
FETCH
fetch
ops(3)(31: 0)
ops(2)(31: 0)
ops(1)(31: 0)
ops(0)(31: 0)
addr(6: 0)
f et ched_i nst r_ri(1)(31: 0)
C
decoded_ops_ri(1)_wcode_pr decoded_ops_ri(1)_wcode_t
ops(1)_wcode_pr
ops(1)_wcode_t
ops(2)_im mediat e(11:0)
ops(2)_im m_f lag
ops(3)_br_t arget (2: 0)
ops(3)_im mediat e(11:0)
decoded_ops_ri(3)_b r_t arget (2: 0)
decoded_ops_ro(3)_t arget(3:0)
fet ched_instr_ro(0)(31:0)
pc_out (6: 0)
fet ched_instr_ro(3)(31:0)
fet ched_instr_ro(2)(31:0)
fet ched_instr_ro(1)(31:0)
mp_reg_file
Q
rf _dout_b(3)(7: 0)
rf _dout_b(2)(7: 0)
rf _dout_b(1)(7: 0)
rf _dout_b(0)(7: 0)
rf _dout_a(3)(7: 0)
rf _dout_a(2)(7: 0)
rf _dout_a(1)(7: 0)
rf _dout_a(0)(7: 0)
br_dout(3:0)
pc_out (6:0)
decoded_ops_ro(3)_wcode_t
decoded_ops_ro(3)_wcode_pr
READ
ops_out(0)_i mmediat e(11:0) ops_out(0)_opcode(6: 0) ops_out(0)_t arget(3:0)
ops_out(0)_wcode_pr
ops_out(1)_b (7: 0)
ops_in(1)_target(3:0)
ops_in(1)_src_b(3:0) ops_out(1)_i mmediat e(11:0) ops_out(1)_opcode(6: 0)
ops_out(3)_b (7: 0)
op s_in(3)_i mm_f lag
C
D
ops_out(3)_wcode_lr
rf _add r_b(3)(3: 0)
fd
rst_d2
rst
en
clk
Q
rf _add r_b(2)(3: 0)
rf _add r_b(1)(3: 0)
rf _add r_b(0)(3: 0)
rf _add r_a(1)(3: 0) rf _add r_a(2)(3: 0) rf _add r_a(3)(3: 0)
rf _data_a_in(3)(7:0)
rf _data_a_in(0)(7:0)
rf _dat a_b_in(2)(7:0) rf _dat a_b_in(3)(7:0)
rf _dat a_b_in(1)(7:0)
rf _dat a_b_in(0)(7:0)
rf _data_a_in(2)(7:0)
rf _data_a_in(1)(7:0)
rf _add r_a(0)(3: 0)
ops_out(3)_wcode_t
op s_in(3)_wcode_t
ops_out(3)_wcode_pr
ops_in(3)_wcode_pr
ops_out(3)_wcode_pc
ops_out(3)_wcode_br
ops_out(3)_u code(1:0)
ops_out(3)_t arget(3:0)
ops_out(3)_opcode(6: 0)
ops_out(3)_i mmediat e(11:0)
ops_out(3)_b r_t arget (2: 0)
ops_in(3)_wcode_br
ops_in(3)_wcod e_pc
ops_i n(3)_wcode_lr
ops_in (3)_ucode(1:0)
ops_in(3)_target(3:0)
ops_in(3)_src_b(3:0)
ops_in(3)_src_a(3:0)
ops_i n(3)_opcode(6:0)
ops_out(3)_b r
ops_in(3)_br_target(2:0) ops_in(3)_imm edi at e(11:0)
ops_out(3)_a(7:0)
ops_out(2)_wcode_t
ops_out(2)_wcode_pr
ops_out(2)_wcode_pc
ops_out(2)_wcode_lr
ops_out(2)_wcode_br
ops_out(2)_u code(1:0)
ops_out(2)_i mmediat e(11:0) ops_out(2)_opcode(6: 0) ops_out(2)_t arget(3:0)
ops_in(3)_b r_src(2:0)
op s_in(2)_wcode_t
ops_in(2)_wcode_pr
ops_in(2)_wcod e_pc
ops_i n(2)_wcode_lr
ops_in(2)_wcode_br
ops_in(2)_target(3:0) ops_in (2)_ucode(1:0)
ops_in(2)_src_a(3:0)
ops_out(2)_b r_t arget (2: 0)
ops_out(2)_b r
ops_out(2)_b (7: 0)
ops_out(2)_a(7:0)
ops_out(1)_wcode_t
ops_out(1)_wcode_pr
ops_out(1)_wcode_pc
ops_out(1)_wcode_lr
ops_out(1)_wcode_br
ops_out(1)_u code(1:0)
ops_out(1)_t arget(3:0)
ops_in(2)_src_b(3:0)
ops_i n(2)_opcode(6:0)
op s_in(2)_i mm_f lag
ops_in(2)_imm edi at e(11:0)
ops_in(2)_br_target(2:0)
ops_in(2)_b r_src(2:0)
op s_in(1)_wcode_t
ops_in(1)_wcode_pr
ops_in(1)_wcod e_pc
ops_i n(1)_wcode_lr
ops_in(1)_wcode_br
ops_in (1)_ucode(1:0)
ops_out(1)_b r_t arget (2: 0)
ops_out(1)_b r
ops_out(1)_a(7:0)
ops_out(0)_wcode_t
ops_out(0)_wcode_pc
ops_out(0)_wcode_lr
ops_out(0)_wcode_br
ops_out(0)_u code(1:0)
op s_in(0)_wcode_t
op s_in(1)_i mm_f lag
ops_in(1)_src_a(3:0)
ops_i n(1)_opcode(6:0)
ops_in(1)_imm edi at e(11:0)
ops_in(1)_br_target(2:0)
ops_in(1)_b r_src(2:0)
ops_in(0)_wcode_pr
ops_in(0)_wcod e_pc
ops_i n(0)_wcode_lr
ops_in(0)_wcode_br
ops_in(0)_target(3:0) ops_in (0)_ucode(1:0)
ops_out(0)_b r ops_out(0)_b r_t arget (2: 0)
ops_in(0)_src_a(3:0)
ops_out(0)_b (7: 0)
ops_out(0)_a(7:0)
br_addr(3)(2:0)
br_addr(2)(2:0)
br_addr(1)(2:0)
br_addr(0)(2:0)
ops_in(0)_src_b(3:0)
ops_i n(0)_opcode(6:0)
op s_in(0)_i mm_f lag
read
IS_REG_DECODE_READ
rst
en
clk
pc_in(6: 0)
decoded_ops_ro(3)_wcode_pc
decoded_ops_ro(3)_wcode_lr
decoded_ops_ro(3)_u code(1:0) decoded_ops_ro(3)_wcode_br
decoded_ops_ri(3)_wcode_l r
decoded_ops_ri(3)_wcode_pr decoded_ops_ri(3)_wcode_t
decoded_ops_ri(3)_wcode_br
decod ed_ops_ri(3)_wcode_pc
decoded_ops_ri(3)_t arget (3: 0) d ecoded_ops_ri(3)_ucode(1: 0)
ops(3)_wcode_pr
ops(3)_wcode_t
ops(3)_wcode_l r
ops(3)_wcode_pc
ops(3)_t arg et (3:0)
ops(3)_ucode(1: 0)
ops(3)_wcode_br
decoded_ops_ro(3)_opcode(6: 0)
decoded_ops_ro(3)_src_b(3: 0)
decoded_ops_ro(3)_src_a(3:0)
decoded_ops_ri(3)_src_a(3: 0)
decoded_ops_ro(3)_i mm_f lag
decoded_ops_ro(3)_i mmediat e(11:0)
decoded_ops_ro(3)_b r_t arget (2: 0)
decoded_ops_ro(3)_b r_src(2:0)
decoded_ops_ro(2)_wcode_t
decoded_ops_ro(2)_wcode_pr
decoded_ops_ro(2)_wcode_pc
decod ed_ops_ri(3)_src_b(3: 0)
ops_in(0)_br_target(2:0)
fd
decoded_ops_ro(2)_u code(1:0)
decoded_ops_ro(2)_wcode_lr
decoded_ops_ro(2)_wcode_br
decoded_ops_ri(3)_opcode(6: 0)
ops_in(0)_imm edi at e(11:0)
MP_RF
decoded_ops_ro(2)_src_b(3: 0)
decoded_ops_ro(2)_src_a(3:0)
decoded_ops_ro(2)_t arget(3:0)
ops(3)_opcode(6: 0)
rst
MP_BR
decoded_ops_ro(2)_i mmediat e(11:0) decoded_ops_ro(2)_i mm_f lag decoded_ops_ro(2)_opcode(6: 0)
ops(3)_src_b(3:0)
decoded_ops_ri(3)_im m_fl ag
decoded_ops_ri(3)_im mediat e(11: 0)
decoded_ops_ro(2)_b r_src(2:0)
decoded_ops_ro(1)_wcode_t
decoded_ops_ro(1)_wcode_pr
decoded_ops_ro(2)_b r_t arget (2: 0)
ops(3)_src_a(3:0)
ops(3)_im m_f lag
decoded_op s_ri(3)_br_src(2: 0)
decoded_ops_ri(2)_wcode_t
ops(3)_br_src(2:0)
decoded_ops_ri(2)_wcode_l r
decoded_ops_ri(2)_wcode_pr
decoded_ops_ri(2)_wcode_br
decod ed_ops_ri(2)_wcode_pc
decoded_ops_ri(2)_src_a(3: 0) decod ed_ops_ri(2)_src_b(3: 0) decoded_ops_ri(2)_t arget (3: 0) d ecoded_ops_ri(2)_ucode(1: 0)
decoded_ops_ri(2)_opcode(6: 0)
decoded_ops_ri(2)_im m_fl ag
decoded_ops_ri(2)_im mediat e(11: 0)
ops(2)_wcode_t
ops(2)_ucode(1: 0)
ops(2)_wcode_br
ops(2)_wcode_pr
ops(2)_t arg et (3:0)
ops(2)_wcode_pc
ops(2)_src_b(3:0)
ops(2)_wcode_l r
ops(2)_src_a(3:0)
ops(2)_opcode(6: 0)
decoded_ops_ri(2)_b r_t arget (2: 0)
decoded_op s_ri(2)_br_src(2: 0)
decoded_ops_ro(1)_wcode_lr decoded_ops_ro(1)_wcode_pc
decoded_ops_ro(1)_wcode_br
decoded_ops_ri(1)_wcode_l r
en
rst_d1
rst
en
clk_base
wr_en(3: 0)
wr_data(3)(7: 0)
wr_data(2)(7: 0)
wr_data(1)(7: 0)
wr_data(0)(7: 0)
wr_addr(3)(3: 0)
wr_addr(2)(3: 0)
wr_addr(1)(3: 0)
wr_addr(0)(3: 0)
rf _addr_b(3)(3: 0)
rf _addr_b(2)(3: 0)
rf _addr_b(1)(3: 0)
rf _addr_b(0)(3: 0)
rf _ad dr_a(3)(3: 0)
rf _ad dr_a(2)(3: 0)
rf _ad dr_a(1)(3: 0)
rf _ad dr_a(0)(3: 0)
rst
en
cl k_base
wr_en(3:0)
wr_addr(3)(2:0)
wr_addr(2)(2:0)
wr_addr(1)(2:0)
wr_addr(0)(2:0)
rd_addr(3)(2:0)
rd_addr(2)(2:0)
decoded_ops_ro(1)_src_b(3: 0) decoded_ops_ro(1)_t arget(3:0)
decoded_ops_ri(1)_wcode_br
IS_REG_FETCH_DECODE
rd_addr(1)(2:0)
decoded_ops_ro(1)_i mm_f lag decoded_ops_ro(1)_opcode(6: 0) decoded_ops_ro(1)_src_a(3:0)
CPU
ready_ops_ri(1)_br_t arget (2: 0)
D
C
ready_ops_ro(1)_br_target(2: 0) ready_ops_ro(1)_im mediate(11: 0)
ready_ops_ro(3)_wcode_t
ready_ops_ro(3)_wcode_pr
ready_ops_ro(3)_wcode_pc
ready_ops_ro(3)_wcode_l r
ready_ops_ro(3)_wcode_br
ready_ops_ro(3)_ucode(1: 0)
ready_ops_ro(3)_t arget (3: 0)
ready_ops_ro(3)_opcode(6: 0)
ready_ops_ro(3)_im mediate(11: 0)
ready_ops_ro(3)_br_target(2: 0)
ready_ops_ro(3)_br
ready_ops_ro(3)_b(7:0)
ready_ops_ro(3)_a(7:0)
ready_ops_ro(2)_wcode_t
ready_ops_ro(2)_wcode_pr
ready_ops_ro(2)_wcode_pc
ready_ops_ro(2)_wcode_l r
ready_ops_ro(2)_wcode_br
ready_ops_ro(2)_ucode(1: 0)
ready_ops_ro(2)_t arget (3: 0)
ready_ops_ro(2)_opcode(6: 0)
ready_ops_ro(2)_im mediate(11: 0)
ready_ops_ro(2)_br_target(2: 0)
ready_ops_ro(2)_br
ready_ops_ro(2)_b(7:0)
ready_ops_ro(2)_a(7:0)
ready_ops_ro(1)_wcode_t
ready_ops_ro(1)_wcode_pr
ready_ops_ro(1)_wcode_pc
ready_ops_ro(1)_wcode_l r
ready_ops_ro(1)_wcode_br
ready_ops_ro(1)_ucode(1: 0)
ready_ops_ro(1)_t arget (3: 0)
ready_ops_ro(1)_opcode(6: 0)
rst_d3
fd Q
IS_REG_READ_EXEC
rst
en
clk
ready_ops_ri(3)_wcode_t
ready_ops_ri(3)_wcode_pr
ready_ops_ri (3)_wcode_pc
ready_ops_ri(3)_wcode_l r
ready_ops_ri(3)_wcode_br
ready_ops_ri(3)_ucode(1: 0)
ready_op s_ri(3)_t arget (3: 0)
ready_ops_ri(3)_opcode(6: 0)
ready_op s_ri(3)_im mediate(11: 0)
ready_ops_ri(3)_br_t arget (2: 0)
ready_ops_ri(3)_br
ready_ops_ri(3)_b(7: 0)
ready_ops_ri(3)_a(7: 0)
ready_ops_ri(2)_wcode_t
ready_ops_ri(2)_wcode_pr
ready_ops_ri (2)_wcode_pc
ready_ops_ri(2)_wcode_l r
ready_ops_ri(2)_wcode_br
ready_ops_ri(2)_ucode(1: 0)
ready_op s_ri(2)_t arget (3: 0)
ready_ops_ri(2)_opcode(6: 0)
ready_op s_ri(2)_im mediate(11: 0)
ready_ops_ri(2)_br_t arget (2: 0)
ready_ops_ri(2)_br
ready_ops_ri(2)_b(7: 0)
ready_ops_ri(2)_a(7: 0)
ready_ops_ri(1)_wcode_t
ready_ops_ri(1)_wcode_pr
ready_ops_ri (1)_wcode_pc
ready_ops_ri(1)_wcode_l r
ready_ops_ri(1)_wcode_br
ready_ops_ri(1)_ucode(1: 0)
ready_op s_ri(1)_t arget (3: 0)
ready_ops_ri(1)_opcode(6: 0)
ready_op s_ri(1)_im mediate(11: 0)
ready_ops_ro(1)_br
ready_ops_ro(1)_b(7:0)
ready_ops_ro(1)_a(7:0)
ready_ops_ro(0)_wcode_t
ready_ops_ri(1)_br
ready_ops_ri(1)_b(7: 0)
ready_ops_ri(1)_a(7: 0)
ready_ops_ri(0)_wcode_t
decoded_ops_ro(1)_b r_src(2:0)
decod ed_ops_ri(1)_wcode_pc
ops(2)_br_t arget (2: 0)
ready_ops_ro(0)_wcode_l r ready_ops_ro(0)_wcode_pc
decoded_ops_ro(0)_wcode_t
decoded_ops_ro(1)_b r_t arget (2: 0) decoded_ops_ro(1)_i mmediat e(11:0)
ops(1)_wcode_l r
ops(2)_br_src(2:0)
ready_ops_ri(0)_wcode_l r ready_ops_ri (0)_wcode_pc
ready_ops_ro(0)_wcode_br
ready_ops_ro(0)_ucode(1: 0)
ready_ops_ro(0)_t arget (3: 0)
ready_ops_ro(0)_opcode(6: 0)
ready_ops_ro(0)_im mediate(11: 0)
ready_ops_ro(0)_br_target(2: 0)
ready_ops_ro(0)_br
ready_ops_ro(0)_b(7:0)
ready_ops_ro(0)_a(7:0)
ready_ops_ro(0)_wcode_pr
ops(1)_wcode_pc
ops(1)_wcode_br
ready_ops_ri(0)_br
ready_ops_ri(0)_wcode_br
ready_ops_ri(0)_ucode(1: 0)
ready_op s_ri(0)_t arget (3: 0)
ready_ops_ri(0)_opcode(6: 0)
pc_out (6:0)
reg_read_exec pc_in(6: 0) ready_ops_ri(0)_a(7: 0) ready_ops_ri(0)_b(7: 0)
ready_ops_ri(0)_br_t arget (2: 0) ready_op s_ri(0)_im mediate(11: 0)
ready_ops_ri(0)_wcode_pr
decoded_ops_ro(1)_u code(1:0)
decod ed_ops_ri(1)_src_b(3: 0) decoded_ops_ri(1)_t arget (3: 0)
ops(1)_t arg et (3:0)
vliw_cpu:1
decoded_ops_ro(0)_wcode_pr
decoded_ops_ro(0)_wcode_pc
d ecoded_ops_ri(1)_ucode(1: 0)
ops(1)_src_b(3:0)
ops(1)_ucode(1: 0)
decoded_ops_ri(1)_src_a(3: 0)
decoded_ops_ri(1)_im m_fl ag decoded_ops_ri(1)_opcode(6: 0)
ops(1)_opcode(6: 0)
ops(1)_src_a(3:0)
ops(1)_im m_f lag
decoded_op s_ri(1)_br_src(2: 0) decoded_ops_ri(1)_b r_t arget (2: 0) decoded_ops_ri(1)_im mediat e(11: 0)
br_data_in(3:0)
rd_addr(0)(2:0)
D
ops(1)_br_t arget (2: 0)
ops(1)_br_src(2:0)
ops(1)_im mediat e(11:0)
decoded_ops_ri(0)_wcode_t
decoded_ops_ri(0)_wcode_pr
ops(0)_wcode_pr
ops(0)_wcode_t
decoded_ops_ri(0)_wcode_l r decod ed_ops_ri(0)_wcode_pc
ops(0)_wcode_l r
ops(0)_wcode_pc
decoded_ops_ro(0)_wcode_lr
decoded_ops_ro(0)_t arget(3:0)
decoded_ops_ri(0)_wcode_br
decoded_ops_ro(0)_u code(1:0) decoded_ops_ro(0)_wcode_br
decoded_ops_ri(0)_t arget (3: 0) d ecoded_ops_ri(0)_ucode(1: 0)
ops(0)_t arg et (3:0)
ops_in(0)_b r_src(2:0)
mp_br
decoded_ops_ro(0)_opcode(6: 0)
decoded_ops_ro(0)_src_b(3: 0)
decoded_ops_ro(0)_src_a(3:0)
ops(0)_ucode(1: 0)
cl k
br_din(3:0)
decoded_ops_ro(0)_b r_t arget (2: 0) decoded_ops_ro(0)_i mmediat e(11:0) decoded_ops_ro(0)_i mm_f lag
ops(0)_wcode_br
pc_in (6: 0)
f et ched_i nst r_ri(3)(31: 0)
f et ched_i nst r_ri(2)(31: 0)
decoded_ops_ri(0)_im m_fl ag
decoded_ops_ri(0)_src_a(3: 0) decod ed_ops_ri(0)_src_b(3: 0)
decoded_ops_ri(0)_opcode(6: 0)
decoded_ops_ro(0)_b r_src(2:0)
reg_decode_read decoded_op s_ri(0)_br_src(2: 0) decoded_ops_ri(0)_b r_t arget (2: 0) decoded_ops_ri(0)_im mediat e(11: 0)
ops(0)_opcode(6: 0)
ops(0)_src_b(3:0)
ops(0)_src_a(3:0)
ops(0)_im m_f lag
ops(0)_im mediat e(11:0)
ops(0)_br_t arget (2: 0)
ops(0)_br_src(2:0)
reg_fetch_decode
DECODE
decode
f et ched_i nst r_ri(0)(31: 0)
rst
cl k
instr(3)(31: 0)
instr(2)(31: 0)
instr(1)(31: 0)
instr(0)(31: 0)
ops_in (0)_a(7: 0)
Q
op_i n_a(7:0) op_in_b(7:0)
EXEC
exec:1
addr(6: 0)
op_in_b(7: 0)
op_in_a(7: 0)
op_in_br_t arget (2: 0)
op_in_opcode(6: 0)
op_in_im mediate(11: 0)
op_out _wcode_lr
ops_in(3)_br_t arget (2: 0)
ops_in(3)_b(7: 0)
ops_in (3)_a(7: 0)
addr(6: 0)
op_in_wcode_t
op_i n_a(7:0) op_in_b(7:0) op_in_br_target(2:0)
spu
op_out _wcode_t
op_out _wcode_pr
op_out _wcode_pc
op_out _wcode_lr
op_out _wcode_br
op_out _br
op_out _x(7:0)
op_out _target(3:0)
op_out _br_t arget (2: 0)
60
ops_in(2)_br_t arget (2: 0)
ops_in(2)_b(7: 0)
ops_in (2)_a(7: 0)
fd
O
rst_d4 inv en_imp_en1
I
ops_i n(2)_wcode_t
ops_in(2)_wcode_pr
ops_in(2)_wcode_pc
ops_in(2)_wcode_l r
ops_in(2)_wcode_br
ops_in(2)_br
ops_in(2)_ucode(1: 0)
ops_in(2)_t arget (3: 0)
ops_in(2)_opcode(6: 0)
ops_in(2)_im mediate(11: 0)
C
op_in_br
op_in_wcode_t rst
op_in_wcode_l r
op_in_wcode_pr
op_in_wcode_br
op_in_wcode_pc
op_i n_a(7:0)
spu
op_out _wcode_t
op_out _wcode_pr
op_out _wcode_pc
op_out _wcode_lr
op_out _wcode_br
op_out _br
op_out _x(7:0)
op_out _target(3:0)
op_out _br_t arget (2: 0)
alu
op_out _wcode_t
op_out _wcode_pr
op_out _wcode_pc
op_out _wcode_lr
op_out _wcode_br
op_out _br
op_out _x(7:0)
op_out _target(3:0)
op_out _br_t arget (2: 0)
op_i n_a(7:0) op_in_b(7:0) op_in_br_target(2:0)
op_out _br_t arget (2: 0)
op_out _wcode_t
op_out _wcode_pr
op_out _wcode_pc
op_out _wcode_lr
op_out _wcode_br
op_out _br
op_out _x(7:0)
op_out _target(3:0)
XST_GND
gnd G
func_units[2].spec_cond.spu_exec
rst
op_in_wcode_t
op_in_wcod e_pr
op_in_wcode_pc
op_in_wcode_lr
op_in_wcod e_br
op_in_br
clk
op_in_ucode(1:0)
op_in_target(3: 0)
op_in_opcode(6: 0)
op_in_imm ed iate(11:0)
spu
func_units[2].alu_cond.alu_exec
rst
op_in_wcode_t
op_in_wcod e_pr
op_in_wcode_pc
op_in_wcode_lr
op_in_wcod e_br
op_in_br
clk
op_in_ucode(1:0)
op_in_target(3: 0)
op_in_opcode(6: 0)
op_in_imm ed iate(11:0)
op_in_br_target(2:0)
op_in_b(7:0)
op_i n_a(7:0)
func_units[1].spec_cond.spu_exec
rst
op_in_wcode_t
op_in_wcod e_pr
op_in_wcode_pc
op_in_wcode_lr
op_in_wcod e_br
op_in_br
clk
op_in_ucode(1:0)
op_in_target(3: 0)
op_in_opcode(6: 0)
op_in_imm ed iate(11:0)
op_in_br_target(2:0)
op_in_b(7:0)
mu op_out _br_target(2:0)
st all
op_out _wcode_t
op_out _wcode_pr
op_out _wcode_pc
op_out _wcode_lr
op_out _wcode_br
op_out _br
op_out _x(7:0)
op_out _target(3: 0)
spu
op_out _wcode_t
op_out _wcode_pr
op_out _wcode_pc
op_out _wcode_lr
op_out _wcode_br
op_out _br
op_out _x(7:0)
op_out _target(3: 0)
op_out _br_target(2:0)
port _select
MUX:15 port _result
port_sel ect
MUX:20
port_sel ect
MUX:11
port_resu lt
port_sel ect
MUX:9
port_resu lt
port_resu lt
port _select
port_sel ect
MUX:26
port _result
port_resu lt
port _select
MUX:2
port_sel ect
MUX:24
port _result
port_resu lt
port_sel ect
port_sel ect
MUX:14
port_resu lt
port_sel ect
port_resu lt
port _select
port_resu lt
port _result
port_sel ect
port _select
MUX:12
port_sel ect
MUX:3
port_resu lt
port_sel ect
MUX:7
port _result
port_resu lt
port_resu lt
port_sel ect
MUX:17
port _select
MUX:13
port_resu lt
port _result
port_sel ect
MUX:4
port _select
MUX:25
port_resu lt
port_sel ect
MUX:10
port _result
port_resu lt
port_sel ect
MUX:5
port_sel ect
MUX:19
port_resu lt
port_sel ect
MUX:1
port_resu lt
port_resu lt
port _select
MUX:6
port_sel ect
MUX:23
port _result
port_resu lt
Mmux_ops_out<3>.x1
Data(31:0)
Mmux_ops_out<3>.wcode.t1
Dat a(3: 0)
Mmux_ops_out<3>.wcode.pc1
Data(3:0)
Mmux_ops_out<3>.wcode.br1
Data(3:0)
Mmux_ops_out<3>.target1
Data(15:0)
Mmux_ops_out<3>.br1
Data(3:0)
Mmux_ops_out<3>.br_target1
Data(11: 0)
Mmux_ops_out<2>.x1
Data(31:0)
Mmux_ops_out<2>.wcode.t1
Dat a(3: 0)
Mmux_ops_out<2>.wcode.br1
Data(3:0)
Mmux_ops_out<2>.target1
Data(15:0)
Mmux_ops_out<2>.br1
Data(3:0)
Mmux_ops_out<2>.br_target1
Data(11: 0)
Mmux_ops_out<1>.x1
Data(31:0)
MUX:16
Mmux_ops_out<1>.wcode.t1
Dat a(3: 0)
MUX:18
Mmux_ops_out<1>.wcode.pr1
Data(3:0)
MUX:22
Mmux_ops_out<1>.wcode.br1
Data(3:0)
Mmux_ops_out<1>.target1
Data(15:0)
MUX:8
Mmux_ops_out<1>.br1
Data(3:0)
Mmux_ops_out<1>.br_target1
Data(11: 0)
Mmux_ops_out<0>.x1
Data(31:0)
Mmux_ops_out<0>.wcode.t1
Dat a(3: 0)
MUX:21
Mmux_ops_out<0>.wcode.br1
Data(3:0)
Mmux_ops_out<0>.target1
Data(15:0)
Mmux_ops_out<0>.br1
Data(3:0)
Mmux_ops_out<0>.br_target1
Data(11: 0)
func_units[3].spec_cond.spu_exec
rst
func_units[1].alu_cond.alu_exec
op_in_wcode_t op_out _wcode_t
op_out _wcode_pr
op_out _wcode_pc
op_out _wcode_lr
op_in_wcod e_pr
op_in_br
op_in_wcode_lr op_in_wcode_pc
op_in_wcod e_br
ops_in(1)_br
ops_in(1)_wcode_pr ops_i n(1)_wcode_t
ops_in(1)_wcode_l r ops_in(1)_wcode_pc
ops_in(1)_wcode_br
cl k
op_in_t arget (3: 0) op_in_ucode(1: 0)
clk
op_in_opcode(6: 0)
op_in_target(3: 0) op_in_ucode(1:0)
op_out _br
op_in_b(7: 0)
op_in_a(7: 0)
op_in_br_t arget (2: 0) op_in_im mediate(11: 0)
op_in_opcode(6: 0)
op_out _wcode_br
op_ou t_wcode_t
op_ou t_wcode_pr
op_ou t_wcode_pc
op_ou t_wcode_lr
op_ou t_wcode_br
op_ou t_br
op_ou t_x(7:0)
op_ou t_target (3: 0)
op_ou t_br_target(2:0)
func_units[3].mem_cond.mu_exec
rst
op_in_wcode_t
op_in_wcode_pr
op_in_wcode_pc
op_in_wcode_l r
op_in_wcode_br
op_in_br
cl k
op_in_ucode(1: 0)
op_in_t arget (3: 0)
op_in_opcode(6: 0)
ops_in(1)_t arget (3: 0)
op_out _x(7:0)
op_out _target(3:0)
op_out _br_t arget (2: 0)
op_in_a(7: 0) op_in_b(7: 0) op_in_br_t arget (2: 0) op_in_im mediate(11: 0)
ops_in(1)_ucode(1: 0)
alu
bru
func_units[3].branch_cond.bru_exec
ops_in(1)_opcode(6: 0)
op_in_b(7:0) op_in_br_target(2:0) op_in_imm ed iate(11:0)
ops_in(1)_b(7: 0) ops_in(1)_br_t arget (2: 0)
op_i n_a(7:0)
func_units[0].spec_cond.spu_exec
rst
op_in_wcode_t
op_in_wcod e_pr
op_in_wcode_pc
op_in_wcode_lr
op_in_wcod e_br
op_in_br
clk
op_in_ucode(1:0)
op_in_target(3: 0)
op_in_opcode(6: 0)
op_in_imm ed iate(11:0)
ops_in(1)_im mediate(11: 0)
ops_in (1)_a(7: 0)
ops_i n(3)_wcode_t
ops_in(3)_wcode_pr
ops_in(3)_wcode_pc
ops_in(3)_wcode_l r
ops_in(3)_wcode_br
ops_in(3)_br
ops_in(3)_ucode(1: 0)
ops_in(3)_t arget (3: 0)
ops_in(3)_opcode(6: 0)
func_units[0].alu_cond.alu_exec
rst
op_in_wcode_pr
rst
op_in_wcode_pc
op_in_wcode_t op_out _wcode_t
op_in_wcod e_pr
ops_in(3)_im mediate(11: 0)
D
cl k op_in_br
op_in_wcode_l r
op_in_wcode_br
rst
op_out _wcode_pr
op_out _wcode_pc
ops_i n(0)_wcode_t
op_in_wcode_lr
ops_in(0)_wcode_l r
ops_in(0)_wcode_pr
op_in_br op_in_wcod e_br
op_in_wcode_pc
ops_in(0)_br ops_in(0)_wcode_br
ops_in(0)_wcode_pc
op_in_t arget (3: 0) op_out _wcode_br
op_in_ucode(1: 0)
op_out _br
op_out _x(7:0)
op_out _target(3:0)
op_out _br_t arget (2: 0)
clk
alu
op_in_target(3: 0) op_in_ucode(1:0)
cl k
op_in_opcode(6: 0)
op_in_br_target(2:0) op_in_imm ed iate(11:0)
ops_in(0)_b(7: 0)
ops_in(0)_t arget (3: 0) ops_in(0)_ucode(1: 0)
ops_in(0)_opcode(6: 0)
ops_in(0)_br_t arget (2: 0) ops_in(0)_im mediate(11: 0)
ops_out(3)_x(7:0)
ops_out(3)_wcode_t
st all_out
ops_out(3)_wcode_pr
ops_out(3)_wcode_pc
ops_out(3)_wcode_lr
ops_out(3)_wcode_br
ops_out(3)_target(3:0)
ops_out(3)_br_t arget (2:0)
ops_out(3)_br
ops_out(2)_x(7:0)
ops_out(2)_wcode_t
ops_out(2)_wcode_pr
ops_out(2)_wcode_pc
ops_out(2)_wcode_lr
ops_out(2)_wcode_br
ops_out(2)_target(3:0)
ops_out(2)_br_t arget (2:0)
ops_out(2)_br
ops_out(1)_x(7:0)
ops_out(1)_wcode_t
ops_out(1)_wcode_pr
ops_out(1)_wcode_pc
ops_out(1)_wcode_lr
ops_out(1)_wcode_br
ops_out(1)_target(3:0)
ops_out(1)_br_t arget (2:0)
ops_out(1)_br
ops_out(0)_x(7:0)
ops_out(0)_wcode_t
ops_out(0)_wcode_pr
ops_out(0)_wcode_pc
ops_out(0)_wcode_lr
ops_out(0)_wcode_br
ops_out(0)_target(3:0)
ops_out(0)_br_t arget (2:0)
ops_out(0)_br
ops_in (0)_br
rst
en
clk
op s_in(3)_x(7: 0)
ops_in(3)_wcode_t
ops_in (3)_wcode_pr
ops_i n(3)_wcode_pc
ops_in(3)_wcode_lr
ops_in (3)_wcode_br
ops_in(3)_t arget (3: 0)
ops_in(3)_b r_t arget (2: 0)
ops_in (3)_br
op s_in(2)_x(7: 0)
ops_in(2)_wcode_t
ops_in (2)_wcode_pr
ops_i n(2)_wcode_pc
ops_in(2)_wcode_lr
ops_in (2)_wcode_br
ops_in(2)_t arget (3: 0)
ops_in(2)_b r_t arget (2: 0)
ops_in (2)_br
op s_in(1)_x(7: 0)
ops_in(1)_wcode_t
ops_in (1)_wcode_pr
ops_i n(1)_wcode_pc
ops_in(1)_wcode_lr
ops_in (1)_wcode_br
ops_in(1)_t arget (3: 0)
ops_in(1)_b r_t arget (2: 0)
ops_in (1)_br
op s_in(0)_x(7: 0)
ops_in(0)_wcode_t
ops_in (0)_wcode_pr
ops_i n(0)_wcode_pc
ops_in(0)_wcode_lr
ops_in (0)_wcode_br
ops_in(0)_t arget (3: 0)
ops_in(0)_b r_t arget (2: 0)
WRITE
write
jum p
rf_wren(3: 0)
rf_dat a(3)(7: 0)
rf_dat a(2)(7: 0)
rf_dat a(1)(7: 0)
rf_dat a(0)(7: 0)
rf_addr(3)(3:0)
rf_addr(2)(3:0)
rf_addr(1)(3:0)
rf_addr(0)(3:0)
pr_data(3)(7:0)
pr_data(2)(7:0)
pr_data(1)(7:0)
pr_data(0)(7:0)
pr_change(3:0)
jum p_ad dr(6: 0)
br_wren(3:0)
br_data(3:0)
br_ad dr(3)(2: 0)
br_ad dr(2)(2: 0)
br_ad dr(1)(2: 0)
br_ad dr(0)(2: 0)
data(3)(7:0)
data(2)(7:0)
data(1)(7:0)
data(0)(7:0)
Obrázek D.6: Pohled na schéma upraveného procesoru.