Zadání projektu 2 Verze 2016-1.0 z 30. září 2016 Obsah Zadání projektu 2: znak města až 25 bodů ....................................................................................................................... 1 Souhrn zadání ............................................................................................................................................................... 1 Obvody v návrhu .......................................................................................................................................................... 2 DisplayLogic - Vámi navržený obvod ......................................................................................................................... 2 VGASynchro - VGA Synchonization Generator ........................................................................................................ 3 VGA Register ............................................................................................................................................................. 3 Nutné požadavky na VHDL kód ..................................................................................................................................... 4 Bodování projektu ..................................................................................................................................................... 4 Návaznost na další projekty ...................................................................................................................................... 4 Řešený příklad ................................................................................................................................................................... 5 Krok 1 - základní velikost........................................................................................................................................... 5 Krok 2 - přidání dalších velikostí ................................................................................................................................... 6 Step 3 - použití paměti ROM ......................................................................................................................................... 7 Step 4 - finální kód .................................................................................................................................................... 9
Zadání projektu 2: znak města až 25 bodů Souhrn zadání Vytvořte úsporný obvod DisplayLogic pro zobrazení znaku města na reklamním panelu. Na vstup obvodu přichází pozice pixelu na VGA obrazovce průmyslového standardu 640x480@60Hz. Popis se uvádí například na https://en.wikipedia.org/wiki/Video_Graphics_Array , viz i přednášky. Zobrazovaný znak si sami vyberte na webu předmětu https://dcenet.felk.cvut.cz/fpga/ . Vlajka musí umět čtyři rozměry přepínané dvojicí přepínačů:
Size: 00 - znak o velikosti na celou obrazovku, tj. šířka x výška: 640 x 480 pixelů, úhlopříčka 800 pixelů Size: 01- znak o velikosti 6/8, tj. šířka x výška 480*360 pixelů, úhlopříčka 600 pixelů Size: 10 - znak o velikosti 4/8, tj. šířka x výška: 320 x 240 pixelů, úhlopříčka 400 pixelů Size: 11 - znak o velikosti 2/8, tj. šířka x výška: 160 x 120 pixelů, úhlopříčka 200 pixelů
Z přehledu je patrné, že rozměry jsou volené tak, aby se úhlopříčka obrazu pokaždé zmenšila o 200 pixelů.
1
Obvody v návrhu DisplayLogic - Vámi navržený obvod DisplayLogic představuje kombinační obvod, stejně jako v projektu morseovky. Jeho vstup dostává souřadnice xcolumn a yrow od generátoru VGA synchronizace. Orientace os x a y zde odpovídá klasickému grafickému obrazu. Výstup DisplayLogic dává hodnotu barvy pro daný pixel.
0 0 yrow
xcolumn row
639
479
Tentokrát nebudete navrhovat logické rovnice z Karnaughových map, ty byly by hodně složité, ale přenecháte úkol vývojovému prostředí Quartus tak, jako se to běžně dělá v praxi. Napíšete podklady pro jejich správné sestavení sekvenčními příkazy jazyka VHDL, kde využijete IF THEN ELSIF VHDL příkazy, přímé analogie C konstrukcí if then else {iif then ... else {if then else...... }}. Pro složitější dílčí části lze využít interní paměti ROM.
Vstupy DisplayLogic
Size - 2bitový signál udávající požadovanou velikost 00 maximální až 11 nejmenší xcolumn, yrow : 10bitové signály udávají souřadnice právě zobrazovaného pixelu.
Výstupy DisplayLogic
VGA_R, VGA_G, VGA_B : jedná se o 10bitové hodnoty barev, bílá barva je definována trojicí 0x3FF, 0x3FF, 0x3FF, černá trojicí 0,0,0. Jde o přímý video signál, v něm není alfa kanál s informací o průhlednosti (opacity), barvy jsou vždy neprůhledné.
2
VGASynchro - VGA Synchonization Generator Jde o generátor synchronizace pro VGA rozlišení 640x480@60 Hz. Obvod obsahuje dva čítače zapojené za sebou, za kterými jsou zařazené komparátory. První čítač čítá sloupce od 0 do 799, ale viditelný obraz tvoří jen sloupce od 0 do 639. Druhý navazující čítač čítá řádky od 0 do 524, ale viditelný obraz leží na řádkách 0 až 479.
Obvod bude probraný na přednáškách a nemusíte ho dělat, stačí ho jen použít. Není na něm ani co vymýšlet, tady existuje skoro jen jediné správné řešení. Vstup obvodu VGASynchro:
CLK_25MHz175 – vstup frekvence pixelů 25.175 MHz, avšak dnešní LCD monitory mají poměrně dobré synchronizační vlastnosti, a tak můžete použít i 25 MHz, vstup CLOCK_50 dělený 2. Monitor se na něj zpravidla slušně synchronizuje. Jinou lepší možnost si časem ukážeme na pozdějších přednáškách.
Výstupy obvodu VGASynchro VGA_CLK – pouhá kopie vstupní frekvence přivedená na výstup jen kvůli tomu, aby se nezapomněla připojit na stejnojmenný pin desky DE2 – bez VGA_CLK by monitor ukazoval jen černou plochu; VGA_BLANK - je v '0', pokud je neviditelná část obrazu, tj. zatemňovací pulzy, jinak zůstává v ‘1‘; VGA_HS - je v '0' jen po dobu horizontálního synchronizačního pulzu, tj. jen po dobu 3.81 µs. Objevuje se každých 31.77 µs, tj. s frekvencí 31.47 kHz (údaje předpokládají, že vstup CLK_25MHz175 má frekvenci 25.175 MHz); VGA_VS - je v '0' jen po dobu vertikálního synchronizačního pulzu, tj. po dobu 63,6 µs. Objevuje se každých 16,68 ms, tj. s frekvencí 60 Hz; VGA_SYNC: je v '0', pokud je v nule horizontální nebo vertikálního synchronizační pulz, je jejich prostým logickým součinem - vyžaduje ho obvod VGA budiče; yrow: unsigned(9 downto 0); - 10bitový signál udávající číslo řádku, tedy souřadnici y, pohybující se od 0 do 524. Číslování řádků běží od shora dolů. Řádek nahoře má číslo 0, poslední viditelný řádek dole má číslo 479. Hodnoty 480 a vyšší leží již ve vertikálním zatemňovacím pulzu. xcolumn :unsigned(9 downto 0); - 10bitový signál pohybující se v rozsahu 0 až 799, který udává číslo sloupce, tedy souřadnici x. Hodnota 0 je vlevo a maximální hodnota vpravo. Ještě viditelnému obrazu odpovídá 639. Hodnoty 640 a vyšší leží již v horizontálním zatemňovacím pulzu. Upozornění: Standardu VGA 640x480@60Hz je sice zpravidla podporovaný všemi LCD monitory, avšak není jejich nativním rozlišením. Může se stát, že některý LCD monitor nezobrazí poslední dva sloupce, tedy sloupce 639 či i 638 budou mimo viditelný obraz. VGA Register Do obvodu přicházejí všechny výstupy z VGASynchro a DisplayLogic a jejich uložené hodnoty tvoří i výstupy obvodu VGARegister. VGARegister zaznamená hodnoty svých vstupů při každé náběžné hraně signálu VGA_CLK, takže se vše mění synchronně s VGA_CLK, což zamezí nechtěným zkreslením obrazu různými zpožděními v kombinačních obvodech DisplayLogic. Registr se současně využívá i pro případnou redukci z 10bitových barev (ty mají desky DE2) na 8bitové barvy, které mají desky DE2-115, s nimiž můžete v pozdějších cvičeních také někteří pracovat.
3
Nutné požadavky na VHDL kód Pokud nebudou splněné, celý projekt se může považovat za nevyhovující a bude dán k přepracování. 1. Pro aritmetické operace se musí výhradně používat knihovna ieee.numeric_std. Nelze používat starší, dnes nedoporučované knihovny use ieee.std_logic_arith, ieee.std_logic_unsigned; ieee.std_logic_signed; - ty najdete v mnohých ukázkách na webech, na ně pozor, používají jiné operace a nejsou přenositelné. 2. Musíte dodržet omezené použití vestavěné paměti, projekt může použít maximálně 216 ´= 65536 bitů vestavěné paměti na obvodu, což odpovídá zhruba 13.5 % paměti na FPGA obvodu na desce DE2. Je to víc než dost. Limit je dán hlavně kvůli tomu, abyste se snažili paměť optimálně využít, a případná opakování znaků uložili jen jednou. Navíc paměť bude třeba i v dalších úlohách. 3. Projekt nesmí používat sdílené proměnné, tzv. shared variables. 4. Čísla integer se povolují jedině pro vnitřní proměnné. 5. Použitá čísla integer musí mít vždy omezení délky pomocí definice range. Pokud ta chyběla, může totiž dojít k nadbytečnému, neoptimálnímu převodu na logické operace. Zde rady a) při vývoji rozhodně nechte všechna integer čísla dočasně bez omezení, teprve poté, až se Vám zobrazení začne fungovat, přidejte jim rozsahy range. b) lepší je vnitřně nepoužívat typy unsigned, ale převést x, y pozice na integer. Unsigned čísla mohou dávat chyby při porovnání a odčítání. 6. V projektu nesmíte u proměnných používat dělení čísly různými od mocnin 2 nebo jakoukoliv operaci modulo (zbytek po dělení), protože obě operace jsou v hardwaru hodně náročné. Lze však používat násobení, protože obvod má vestavěné hardwarové násobičky. Dělení a modulo je možné samozřejmě použít u konstant, protože zde se aritmetické operace provedou již při překladu. Jak tedy bez dělení napsat například podmínku: x/3 < y/5 ? Jednoduše: 5*x<3*y. Dělení mocninami 2, tedy čísly 2, 4, 8, 16, 32 atd., lze vložit do kódu, jelikož se realizuje velmi optimálně pomocí posunu bitů. A jak na zbytek po dělení mocninami 2 ? Opět snadno, převede se buď na vymaskování dolních bitů pomocí logické operace AND, nebo na pouhé vybrání potřebných dolních bitů. Bodování projektu Body za správně fungující projekt dostanete podle složitosti vybraného znaku. Bodové hodnocení Vám může být sníženo za nepřehlednost programu, pokud nebudete mít v kódu komentáře k hlavním operacím nebo bude používat příliš čísel i v případech, kdy by se hodilo definovat přehlednější konstanty, či budete používat nevýstižné názvy proměnných. Návaznost na další projekty S vlajkou bude ještě pracovat v posledním nejsložitějším projektu, kde se bude vytvářet pohyb znaku po obrazovce, a tak vřele doporučujeme tedy udělat si pečlivé komentáře. 4
Řešený příklad Vytvořte vlajku se znakem předmětu SPS (nuly a jedničky sevřené zelenou logickou pravdou a modrou nepravdou)
Krok 1 - základní velikost Napřed sestavíme vlajku pro největší rozměr 640x480 bez červených nul a jedniček. 3
1 4 5
2
Blankytná čísla v obrázku se vztahují k podmínkám v dalším textu dole a byly přidané pro orientaci. Vlajku sestavíme z multiplexorů zapojených v hierarchie za sebou, což specifikujeme pomocí podmínek typu IF-THEN ELSEIF... 1/ První podmínka s nejvyšší prioritou ořízne části mimo vlajku na černou barvu. Definujeme si konstanty constant YSIZE : integer := 480; constant XSIZE : integer := 640;
V procesu pak definujeme pomocné proměnné variable x, y : integer; a jim přiřadíme xcolumn a yrow převedení na typy integer x:=to_integer(xcolumn); y:=to_integer(yrow); Poté snadno napíšeme první podmínku if(x<0) or (x>=XSIZE) or (y<0) or (y>=YSIZE) then RGB:=BLACK; 2/ Druhou podmínkou omezíme kruh, k čemuž využijeme klasickou rovnici kružnice, tuhle podmínku navážeme elsif, aby měla nižší prioritu a uplatnilo se pro ni ořezávání dané první podmínkou elsif x*x+(y-YSIZE)*(y-YSIZE) < YSIZE*YSIZE/16 then RGB:=YELLOW; 3/ Třetí podmínkou omezíme zelený prostor pomocí rovnice přímky. Navážeme ji opět pomocí ELSIF, takže se nám automaticky ořízne okraj a kruh, protože podmínky 1 a 2 budou mít vyšší priority. elsif 5*y < 5*YSIZE - 6*x then RGB:=GREEN; -- line equation y = 480-(6/5)*x 4/ Čtvrtá podmínka, vázaná opět elsif, nám ze zbytku ořízne žlutou část pomocí další rovnice přímky elsif 8*y < 8*YSIZE- 3*x then RGB:=YELLOW;
-- line equation y = 480-(3/8)*x
5/ Pátá podmínka definuje pro zbytek modrou barvu a napíšeme ji pomocí ELSE, tedy vše ostatní na modro, a příkaz if ukončíme: else RGB:=BLUE; end if;
5
Celý blok podmínek vypadá takto: begin spsflag : process(xcolumn, yrow) -- output of process depends on xcolumn and yrow variable RGB : RGB_type; -- output colors variable x, y : integer; -- renamed xcolumn and yrow begin x:=to_integer(xcolumn); y:=to_integer(yrow); -- convert to integer if(x<0) or (x>=XSIZE) or (y<0) or (y>=YSIZE) then RGB:=BLACK; --black outside of visible frame elsif x*x+(y-YSIZE)*(y-YSIZE) < YSIZE*YSIZE/16 then RGB:=YELLOW; elsif 5*y < 5*YSIZE - 6*x then -- line equation y = 480-(6/5)*x RGB:=GREEN; elsif 8*y < 8*YSIZE- 3*x then -- line equation y = 480-(3/8)*x RGB:=YELLOW; else RGB:=BLUE; end if; -- Copy results in RGB to outputs of entity VGA_R<=RGB.R; VGA_G<=RGB.G; VGA_B<=RGB.B; ----------------------------------------------------------------------------end process;
Úplný VHDL kód najdete v souboru DisplayLogic_step1_size00_norom.vhd a můžete ho otestovat pomocí zapojení:
Kód lze zkoušet i bez desky pomocí testbench, což bude tématem přednášek a bude ukázané na cvičení. Výstupní obrázky z testbenchů byly použité i v tomto textu.
Krok 2 - přidání dalších velikostí Vzhledem k tomu, že si zobrazená vlajka zachovává poměr stran, je změna jednoduchá; nemusíte přepočítávat směrnice přímek, ty zůstanou stejné. V kódu nahradíme pouze velikosti XSIZE a YSIZE poli, ze kterých vyčítáme hodnoty. Pouze některé operace, které nejsou převoditelné, budou pro každou vlajku zvlášť podle jejího typu, třeba v příkazu case, který odpovídá známému switch z C, a implementuje se jedním multiplexorem. type DIM4I is array(0 to 3) of integer; constant YSIZE : DIM4I := (480,360,240,120); constant XSIZE : DIM4I := (640,480,320,160);
6
Vlastní proces se změní na spsflag : process(xcolumn, yrow) -- output of process depends on xcolumn and yrow variable RGB : RGB_type; -- output colors variable x, y : integer; -- renamed xcolumn and yrow variable ix : integer range 0 to 3; begin ix:= to_integer(unsigned(size)); case size is when "11" | "10" | "01" => x:=to_integer(xcolumn)-(XSIZE(0)-XSIZE(ix))/2; y:=to_integer(yrow)-(YSIZE(0)-YSIZE(ix))/2; when others => x:=to_integer(xcolumn); y:=to_integer(yrow); end case; if(x<0) or (x>=XSIZE(ix)) or (y<0) or (y>=YSIZE(ix)) then RGB:=BLACK; --black outside of visible frame elsif x*x+(y-YSIZE(ix))*(y-YSIZE(ix)) < YSIZE(ix)*YSIZE(ix)/16 then RGB:=YELLOW; elsif 5*y < 5*YSIZE(ix) - 6*x then RGB:=GREEN; -- line equation y = 480-(6/5)*x elsif 8*y < 8*YSIZE(ix) - 3*x then RGB:=YELLOW; -- line equation y = 480-(3/8)*x else RGB:=BLUE; end if; -- Copy results in RGB to outputs of entity VGA_R<=RGB.R; VGA_G<=RGB.G; VGA_B<=RGB.B; end process; Úplný VHDL kód najdete v souboru DisplayLogic_step2_norom.vhd a v laboratoři ho můžete otestovat pomocí zapojení:
Step 3 - použití paměti ROM Přesný postup si ukážeme a vysvětlíme i na přednáškách, a proto s paměti počkejte, až bude provedený výklad. Zde zatím jen orientačně. Napřed si ukážeme doporučené zapojení s pamětí. V DisplayLogic přibyl adresní výstup pro paměť address_next a vstup z paměti memory_in. Všimněte si, že paměť má interně vstupní registr adresy, ten je nezbytný pro její optimální realizaci pomocí vnitřních pamětí obvodu. Hodnotu pro danou adresu dostaneme tedy až se zpožděním, a proto musíme na výstup posílat adresu dalšího bodu v pořadí.
7
Vlastní paměť vytvoříme v Quartusu pomocí MegaWizard Plugin Manager. Pro zadanou vlajku, a mnohé další znaky nám stačí jednobitová ROM. Nakreslíme si příslušné masky bodů pro jednotlivé velikosti; ty uložíme jako bitmapy.
128x128
96x96
64x64
32x32
Poté bitmapy převedeme na inicializační soubor paměti typu *.mif - Memory initialization File, třeba pomocí nástroje Bitmap2MIF, který najdete na stránkách předmětu. Obsahy pro jednotlivé velikosti jsou uložené v jediné paměti za sebou, protože se stejně čte jenom jediný z nich. Bitmap Width x Height
Memory address start
Memory address end
Length in bits
128 x 128
0 [0x0]
16383 [0x3FFF]
16384 [0x4000]
96 x 96
16384 [0x4000]
25599 [0x63FF]
9216 [0x2400]
64 x 64
25600 [0x6400]
29695 [0x73FF]
4096 [0x1000]
32 x 32
29696 [0x7400]
30719 [0x77FF]
1024 [0x400]
Pokud budete potřebovat jiný typ paměti, třeba pro průběhy křivek, můžete si vytvořit paměť i v Quartusu a naplnit si ji svými hodnotami. Sama striktura MIF souborů není složitá, jde o obyčejné textové soubory, takže si je snadno dokážete i vygenerovat i z programu. Do kódu přidáme definice adres pro jednotlivé velikosti constant MEMSTART : DIM4I := (0,16384,25600,29696); constant MEMROWSIZE : DIM4I := (128,96,64,32); a také pozic vloženého obrázku constant EMBORGX : DIM4I := (440,330,220,110); constant EMBORGY : DIM4I := (64,48,32,16); Vložíme poslání následující adresy. Pro její výpočet si definujeme proměnnou variable addr : integer; a do k=odu vložíme příkazy. addr := MEMSTART(ix)+(y-EMBORGY(ix))*MEMROWSIZE(ix) + (x-EMBORGX(ix)+1); address_next <= std_logic_vector(to_unsigned(addr,address_next'length));
8
Mezi podmínky 1 a 2 vložíme podmínku přepnutí na červenou barvu při hodnotě memory_in rovné logické 1 elsif x>=EMBORGX(ix) and x<EMBORGX(ix)+MEMROWSIZE(ix) and y>=EMBORGY(ix) and y<EMBORGY(ix)+MEMROWSIZE(ix) and memory_in='1' then RGB:=RED; Všimněte si, že jsme celou podmínku napsali do jednoho příkazu IF, protože podmínkou ovládáme multiplexor v prioritní kaskádě. Celý kód najdete v DisplayLogic_step3.vhd Nyní můžeme vyzkoušet. Step 4 - finální kód U proměnných typu integer, kde zatím nemáme rozsahy range, doplníme omezení range za současného testování, zda to pořád funguje. Všimněte si, že se po přidání range zmenší počet spotřebovaných logických elementů. U projektu v příkladu klesl počet elementů jen z 364 na 351, což sice není významná úspora, ale učíme se přece psát optimální kód. Celý kód najdete v DisplayLogic_step4.vhd Poté předvedeme učiteli, necháme si zapsat body a hurá na další úlohu:-) ~o~
9