Arkanoid on Altera DE-1

14

Transcript of Arkanoid on Altera DE-1

Page 1: Arkanoid on Altera DE-1
Page 2: Arkanoid on Altera DE-1

INTRODUZIONE

Arkanoid è un videogioco prodotto dalla software house giapponese Taito nel 1986. Esso è da considerarsi come il rifacimento

dell'arcade Breakout, di casa Atari, risalente agli anni settanta. Sebbene non sia il padre del suo genere, è considerato una pietra

miliare dei videogiochi e negli anni ha ispirato innumerevoli cloni e giochi simili.

Scopo del gioco originale è abbattere un certo numero di mattoncini colorati colpendoli con una sfera. Sul fondo dello schermo

si trova l'astronave "Vaus" che agisce da "base" con cui far rimbalzare la sfera contro i mattoncini; naturalmente bisogna evitare

che la sfera cada nella porzione di schermo sottostante al "Vaus", pena la perdita di una vita.

Per questa tesina si è realizzata una versione semplificata di Arkanoid che ne rispecchia tutte le funzionalità base:

La sfera rimbalza e distrugge i mattoncini;

Esiste il concetto di vita(ci sono 3 vite disponibili per poter tentare di distruggere il muro di mattoncini)

e alcune di quelle avanzate:

Alcuni mattoncini sono indistruttibili;

È possibile variare l’angolo di rimbalzo della sfera imprimendole un effetto tramite il movimento della navicella;

È possibile colpire la sfera con i lati a 45 gradi della navicella facendole invertire il moto mantenendo l’angolo.

Per lo sviluppo si è utilizzata la scheda terasic DE1 che monta FPGA Cyclone II di ALTERA

La scheda DE1 mette a disposizione numerose features per sviluppare variegati progetti:

Altera Cyclone II 2C20 FPGA with 20000 LEs

Altera Serial Configuration deivices (EPCS4) for Cyclone II 2C20

USB Blaster built in on board for programming and user API controlling

JTAG Mode and AS Mode are supported

8Mbyte (1M x 4 x 16) SDRAM

4Mbyte Flash Memory

512Kbyte(256Kx16) SRAM

SD Card Socket

4 Push-button switches

10 DPDT switches

8 Green User LEDs

10 Red User LEDs

4 Seven-segment LED displays

50MHz oscillator ,24MHz oscillator ,27MHz oscillator and external clock sources

Page 3: Arkanoid on Altera DE-1

24-bit CD-Quality Audio CODEC with line-in, line-out, and microphone-in jacks

VGA DAC (4-bit R-2R per channel) with VGA out connector

RS-232 Transceiver and 9-pin connector

PS/2 mouse/keyboard connector

Two 40-pin Expansion Headers

Per la realizzazione di Arkanoid si è scelto di utilizzare:

2 switch ”Push-Button” per comandare il movimento a destra e sinistra della barretta;

2 switch DPDT per controllare reset del gioco e Start/Pause;

I display a 7 segmenti per visualizzare le “vite” rimanenti;

L’uscita VGA per la visualizzazione a schermo.

Inoltre, finita la fase di testing in cui la programmazione del FPGA Cyclone II è stata effettuata con metodologia JTAG, si è anche

sfruttata la eeprom EPCS4 per salvare in maniera permanente il codice.

Il software di sviluppo utilizzato è Quartus II fornito da Altera. Per la finalità didattica di

questo progetto è sufficiente la versione web scaricabile direttamente dal sito Altera.

Tale software rende disponibili tool per realizzare tutte le fasi necessarie per il progetto:

Editor Grafico per gli schematici

Editor testuale per la creazione dei file in linguaggio VHDL

Interfaccia per programmare in modalità JTAG o AS

Page 4: Arkanoid on Altera DE-1

ARCHITETTURA

DIVISORE DI FREQUENZA:

Ha il compito di dividere di un fattore due la frequenza di clock di 50 MHz fornita da uno degli oscillatori montati sulla Board

TERASIC DE1. Tale divisione è necessaria per ottenere la frequenza di 25 MHz richiesta per la modalità di funzionamento della

VGA: 640x480 pixel a 25MHz.

INTERFACCIA VGA:

Come visto nell’introduzione la scheda TERASIC DE1 rende disponibile un’uscita VGA con connettore a 16pin. I segnali di

sincronizzazione vengono gestiti direttamente dallaFPGA Cyclone II ed è presente un convertitore digitale analogico (4bit DAC)

per produrre i segnali analogici R G e B.

Figura 1 - Circuito VGA scheda terasic DE1

La risoluzione supportata è 640x480 a cui corrisponde un dot clock di 25MHz e un refresh di 60Hz, ossia vengono disegnati 60

schermi al secondo. La visualizzazione dell’immagine su monitor avviene tramite righe. Una volta disegnata una riga ed atteso il

tempo di latenza necessario(Front Porch) per passare da una riga all’altra, si passa a disegnare la successiva fino a che si è

disegnato tutto il monitor. A questo punto si ricomincia da capo.

Figura 2 - Diagramma temporale del segnale di sincronismo orizzontale

Figura 3 - Specifiche temporali

Un impulso attivo basso di durata a viene applicato al segnale di sincronismo orizzontale della porta VGA; tale periodo

determina una riga di dati. L’ingresso RGB del monitor dev’essere pilotato a 0 Volt per un periodo di tempo chiamato back porch

dopo l’arrivo dell’impulso hsync. Successivamente nel periodo c viene disegnato sull’uscita ogni singolo pixel relativo a quella

linea. Infine si attende ancora il tempo d, Front Porch, dove il segnale RGB è ancora una volta pilotato a 0 Volt in attesa del

successivo hsync. Il grafico della sincronia verticale è il medesimo solamente che l’arrivo del vsync significa la fine di un frame e

l’inizio del successivo.

Page 5: Arkanoid on Altera DE-1

IMPLEMENTAZIONE: LOGICA DI CONTROLLO

La maggior parte del progetto, sia per quanto riguarda la logica di funzionamento che per la visualizzazione su schermo, è stata

inserita in un unico file VHDL chiamato myArkanoid. Tale modulo si occupa della sincronizzazione VGA, della gestione degli input

switch e dei display a 7 segmenti, del movimento e della resa grafica delle componenti del gioco.

SINCRONIZZAZIONE VGA:

Il segnale viene generato grazie ad un contatore h_cnt che viene incrementato ogni colpo di clock ( 25Mhz ). Il segnale h_sync

viene pilotato in base al valore che assume h_cnt, per poter rispettare i tempi dello standard VGA 640x480.

In particolare h_cnt varia da 0 a 799, un intervallo di valori corrispondente al periodo totale della linea, cioè 31,7 µs (a+b+c+d nel

grafico in Figura 3); il segnale h_sync si mantiene alto per h_cnt che varia tra 0 e 659, viene posto a 0 quando h_cnt assume

valori compresi tra 660 e 755, poi viene nuovamente settato a 1 fino a 799. A differenza del grafico precedentemente illustrato,

questa routine inizializza il contatore a 0 nel momento dell’inizio del Display Interval, spostando Back Porch dopo il Front Porch.

In questo modo il funzionamento del sincronismo non cambia, ma permette di leggere direttamente in h_cnt il valore della

colonna attuale ( column).

Figura 4 - Diagramma temporale del segnale di sincronismo orizzontale (nell'implementazione usata)

In maniera analoga avviene per quanto riguarda la sincronia verticale, in particolare v_cnt varia da 0 a 524, un intervallo di valori

corrispondente al periodo totale della schermata, cioè 16.6 ms; il segnale v_sync si mantiene alto per v_cnt che varia tra 0 e 492,

viene posto a 0 quando v_cnt assume i valori 493 e 494, poi viene nuovamente settato a 1 fino a 524.

Esiste un ulteriore segnale interno, video_en ossia video enabled, il quale è attivo solo se h_cnt è minore di 640 e v_cnt è minore

di 480. Pertanto l’informazione sul colore del pixel viene ricopiata dall’ingresso all’uscita solo quando video_en è settato a uno,

cioè solo se ci si trova all’interno dello schermo attivo.

In appendice A il frammento di codice che realizza quanto visto.

INPUT SWITCH:

Per comandare il movimento della navicella sono stati utilizzati due dei quattro switch Push Button presenti sulla scheda. Tali

switch mantengono lo stato logico fino a che non vengono rilasciati e quindi sono ideali per realizzare quanto necessario.

Altri due Toggle Switch sono stati invece utilizzati per il Reset del gioco e la Pausa.

DECODER DISPLAY A 7 SEGMENTI:

Per pilotare il display a 7 segmenti è stato realizzato un semplice decoder atto a creare una corrispondenza biunivoca tra

simbolo visualizzato sul display e il numero intero corrispondente alle vite rimanenti. Il codice è molto semplice e si riassume con

un banale statement case. In appendice A il frammento di codice che lo realizza.

DIVISORE DI FREQUENZA:

Il modo più semplice per realizzare un divisore di frequenza è quello di realizzare un contatore che fornisce un segnale in uscita

solamente per alcuni valori. In questo caso particolare è sufficiente un contatore a 1 bit: se il contatore conta ad ogni fronte di

salita (o discesa) infatti, otteniamo già il clock a frequenza dimezzata. In appendice A il frammento di codice che lo realizza.

Page 6: Arkanoid on Altera DE-1

IMPLEMENTAZIONE: ELEMENTI GRAFICI E ANIMAZIONI

Ogni elemento grafico è stato realizzato andando ad accendere o meno i pixel che lo compongono in una determinata posizione

dello schermo. Ad esempio per realizzare un mattoncino, è necessario fornirne gli estremi v_cnt e h_cnt:

IF ((v_cnt >=195) AND (v_cnt <= 204) AND (h_cnt >= 61) AND (h_cnt <= 110)) THEN red3_signal <= '0'; red2_signal <= '1'; red1_signal <= '1'; red0_signal <= '0'; green3_signal <= '0'; green2_signal <= '1'; green1_signal <= '1'; green0_signal <= '0'; blue3_signal <= '0'; blue2_signal <= '1'; blue1_signal <= '1'; blue0_signal <= '0';

END IF;

Per capire meglio il significato di v_cnt e h_cnt li si consideri come righe e colonne: se il pixel si trova tra le righe 195 e 204 e tra

le colonne 61 e 110, allora viene acceso secondo la colorazione fornita specificata(grigio scuro).

Appare subito evidente come sia complicata la realizzazione di linee oblique o curve. Nel dettaglio le configurazioni per la resa

grafica degli altri oggetti in scena.

LA SFERA:

Quanto visto in precedenza, come introduzione riguardo alla resa grafica del mattoncino, rappresenta la modalità di disegno di

un oggetto statico. Nel gioco, la sfera deve muoversi nello schermo, rimbalzare sulle pareti, sugli ostacoli e sui mattoncini. Per

rendere possibile il movimento, anziché disegnare la sfera tramite

punti fissi, si utilizzano delle variabili che vengono aggiornate

continuamente in base ad un contatore che divide il clock

affinché il movimento avvenga ad una velocità umanamente

ragionevole. In figura lo schema utilizzato per la resa della sfera:

si notano le variabili ballPositionV e ballPositionH che

rappresentano la posizione della sfera in ogni istante. La sfera ha

due componenti di movimento: orizzontale ballMovementH e

verticale ballMovementV. Tali variabili assumono 3 valori: 0

movimento negativo, verso l’alto nel caso del movimento

verticale e verso sinistra nel caso del movimento orizzontale; 1

movimento positivo, verso il basso e verso destra; 2 situazione di

STOP, prima di partire o alla fine della partita. Tali variabili

servono per discriminare l’aumento o la diminuzione dei valori

caratterizzanti la posizione della sfera negli istanti di cambio

direzione; infatti se ad esempio la sfera si muove dall’alto verso il basso il valore di ballPositionV aumenterà fino al momento in

cui la sfera stessa si scontra con un ostacolo: a tal punto il valore di ballPositionV diminuisce in modo analogo. Similmente

accade per il movimento orizzontale. Per creare il movimento in diagonale, ad ogni refresh della posizione della pallina,

dovranno variare contemporaneamente sia la componente di movimento orizzontale che quella verticale. In particolare per

quanto riguarda il caso in questione si sono adottati diversi angoli di impatto in base all’effetto impresso alla sfera dal

movimento della navicella. È evidente che per variare l’angolo descritto dalla traiettoria della sfera, bisogna variare l’incremento

di una delle due componenti. Si è scelto di utilizzare la variabile angle che varia l’incremento per quanto riguarda la componente

verticale.

Figura 6 - Componenti del movimento della sfera

La figura 7 mostra la variazione delle componenti del movimento della sfera nei vari casi presi in esame. Il valore angle

sopraccitato varia quindi tra 1 e 3 a creare gli angoli di 45°, 60° e 72° circa in modulo. Una variazione di questo tipo comporta

inevitabilmente una variazione anche della velocità di movimento della sfera. Questo perché aumentando il numero di pixel che

vengono saltati da una posizione all’altra, in un istante temporale la sfera si è muove di più. Tale caratteristica(divenuta poi tale

ma nata come un errore) peraltro era presente anche nel videogioco originale. Un metodo semplice per sopperire a questo

Figura 5 - Realizzazione grafica della sfera

Page 7: Arkanoid on Altera DE-1

errore è basato sulla variazione della velocità della sfera a seconda dell’angolo. Come accennato infatti la sfera varia la sua

posizione a intervalli regolari cadenzati da un contatore che suddivide la frequenza di clock. Se si variasse dinamicamente i l

valore a cui si azzera il contatore, variabile ballSpeed nel sorgente, in base all’angolo di impatto della pallina, ne risulterebbe una

velocità adattata al movimento della sfera. Una prova in tal senso è stata fatta e rimane commentata all’interno del codice.

Tuttavia si è notato che rallentando la frequenza di aggiornamento della sfera, il movimento della stessa risulta meno fluido e di

conseguenza la resa finale è meno piacevole alla vista.

Il codice che realizza quanto descritto è abbastanza complesso e si rimanda al sorgente completo per un’analisi più specifica.

Volendo semplificarlo al massimo al caso di una sfera che rimbalza, sempre mantenendo il medesimo angolo e a velocità

costante, a contatto con i bordi dello schermo, eccone il sorgente:

IF(ballMovementV = 1) THEN IF (ballPositionV = 450) --Pallina Colpisce fondo schermo

THEN ballMovementV:=0; ballPositionV:= ballPositionV-1; ELSE ballPositionV:= ballPositionV+1; END IF; END IF;

IF(ballMovementV = 0) THEN IF (ballPositionV =30) --Pallina Colpisce bordo alto dello schermo

THEN ballMovementV:=1; ballPositionV:= ballPositionV+1; ELSE ballPositionV:= ballPositionV-1; END IF; END IF;

Analogamente per quanto riguarda il movimento orizzontale.

LA NAVICELLA:

Considerazioni simili a quelle del movimento della sfera, con la differenza che la navicella ha solamente la componente

orizzontale del movimento. Ecco come è stata realizzata la navicella:

Figura 7 - Realizzazione grafica della navicella

Come accennato, in base al movimento della navicella all’istante dell’impatto con la sfera, a quest’ultima viene impresso un

effetto che ne varia la traiettoria. Inoltre i lati della navicella sono posti a 45 gradi e nel caso in cui la sfera impattasse questa

parte di navicella, il movimento verrebbe completamente invertito facendole ripercorrere la stessa traiettoria al contrario. La

figura 9 mostra alcuni degli angoli d’impatto.

Figura 8 - Angoli d'impatto

Page 8: Arkanoid on Altera DE-1

BORDI E SCRITTE:

Per rendere più piacevole la grafica del gioco, si sono creati dei bordi per delimitare la zona di movimento della sfera. Inoltre al

di sotto della navicella si è disegnata una grafica che ricorda uno specchio d’acqua ondulato.

Sempre con la stessa finalità si è inserita l’intestazione che contiene l’autore, l’università, il corso per il quale è stata realizzata

questa tesina, il titolo del gioco e la scheda su cui si è lavorato. Per disegnare le lettere si è proceduto come visto in precedenza,

ecco a titolo di esempio come è stata realizzata la “S” e il codice corrispondente:

IF (((v_cnt = 300 OR v_cnt = 305 OR v_cnt = 310)) AND ((h_cnt >=Pause_s_Position+1) AND (h_cnt <= Pause_s_Position+3)))

OR (((v_cnt >= 301 AND v_cnt<=304) OR v_cnt = 309) AND (h_cnt = Pause_s_Position))

OR (((v_cnt >= 306 AND v_cnt<=309) OR v_cnt = 301)AND(h_cnt = Pause_s_Position+4))

THEN red3_signal <= '0';red2_signal <= '0';red1_signal <= '0'; red0_signal <= '0';green3_signal <= '1';green2_signal <= '0'; green1_signal <= '0'; green0_signal <= '0'; blue3_signal <= '1'; blue2_signal <= '0'; blue1_signal <= '0'; blue0_signal <= '0'; END IF;

SFUMATURE:

Poiché sono disponibili 4bit per ogni colore, sono a disposizione 212

colori ossia 4096 colori. La gradazione di colore è data

dall’insieme dei segnali red0, red1, red2, red3 ,green0, green1, green2, green3,blue0, blue1,blue2 e blue3. Ad esempio con la

combinazione:

red3_signal <= '0'; red2_signal <= '1'; red1_signal <= '1'; red0_signal <= '0';

green3_signal <= '0'; green2_signal <= '1'; green1_signal <= '1'; green0_signal <= '0';

blue3_signal <= '1'; blue2_signal <= '1'; blue1_signal <= '1'; blue0_signal <= '0';

si ottiene una gradazione di azzurro.

Sfruttando alcune delle variazioni di colore si sono creati diversi gradienti per simulare i riflessi nei bordi, nel mare sottostante,

nella navicella e nella sfera.

Page 9: Arkanoid on Altera DE-1

SCHEMATICO FINALE E PIN ASSIGNMENT

Figura 9 - schematico finale

Ecco come appare il file di design dello schematico finale. Come già detto, per l’implementazione si è scelto di concentrare la

maggior parte della logica in un unico blocco, myArkanoidVHDL. Nella figura sono evidenziati anche i pin di destinazione nel

FPGA.

Page 10: Arkanoid on Altera DE-1

PROGRAMMAZIONE DELLA BOARD: MODALITÀ AS

Una volta terminato il progetto ed eseguite le fasi di compilazione, analisi e sintesi del circuito e pin assignment, si procede con

la programmazione della scheda. Le modalità utilizzate sono state 2:

JTAG in fase di debug, comodo in quanto il bitstream (.sof) viene scaricato al volo nel FPGA e vi rimane fino allo

spegnimento della board;

AS, Active Serial Programming: in questo caso il file del programma (.pof) viene scritto nella eeprom EPCS4 montata

sulla board e risiede in memoria fino a quando questa non viene riscritta. In questo modo all’avvio della scheda verrà

avviato automaticamente il codice presente sulla EPCS4. Lo schema a blocchi seguente mostra la fase di

Programmazione, si noti lo switch RUN/PROG in posizione PROG, e la successiva fase di caricamento dalla EPCS4

all’FPGA:

Figura 10 - Schema di configurazione Attiva Seriale

Si tralascia la descrizione della modalità JTAG che è trattata in altri tutorial e ci si sofferma sulla programmazione attiva seriale

che richiede anche alcune configurazioni preliminari: infatti il file .pof va preparato in base al tipo di eeprom montata sulla

scheda. In particolare la scheda DE1 monta una eeprom da 512Kbyte, EPCS4.

Procediamo dunque con le impostazioni necessarie. Cliccando su AssignmentsDevice appare la seguente finestra:

Page 11: Arkanoid on Altera DE-1

Cliccando poi su Device and Pin Options e selezionando la tab Configurations appare:

Come indicato in figura, selezionare Active Serial per quanto riguarda il Configuration Scheme e la eeprom EPCS4 come device.

A questo punto ogni volta che il software compilerà e assemblerà il codice, verrà creato un file .pof di 512Kbyte compatibile con

la EPCS4 e quindi non rimane che programmarla. Cliccando su ToolsProgrammer apparirà la seguente:

1. Selezionare la modalità Active Serial Programming.

2. Selezionare il device di destinazione, l’EPCS4 cliccando su Add Device.

3. Selezionare il file .pof sorgente cliccando su Add File.

A questo punto si procede con la programmazione vera e propria cliccando su Start sempre nella finestra del programmatore. È

importante assicurarsi che sia selezionato USB-Blaster come Hardware di Programmazione, in caso contrario va selezionato

tramite l’apposito bottone Hardware setup. Inoltre, dato che stiamo programmando la EEPROM lo switch RUN/PROG va posto

nella posizione PROG. Al termine della programmazione, va spenta la scheda, rimesso lo switch RUN/PROG in posizione RUN e

riaccesa la scheda che avvierà la configurazione appena scritta sulla EPCS4.

Page 12: Arkanoid on Altera DE-1

COMANDI DI GIOCO

Data la semplicità del gioco, i comandi necessari sono pochi:

PUSH BUTTON SWITCH KEY0 : Muove la navicella verso destra;

PUSH BUTTON SWITCH KEY1 : Muove la navicella verso sinistra;

TOGGLE SWITCH SW1 : Mette in pausa il gioco

TOGGLE SWITCH SW2 : Resetta il gioco.

Figura 11 - Comandi di Gioco

Per completare la distruzione del muro di mattoncini il giocatore ha a disposizione 3 vite che corrispondono a 3 navicelle.

Quando una navicella viene distrutta, la sfera viene riposizionata nella posizione iniziale e dopo circa 2 secondi riparte con

direzione casuale; in questo lasso di tempo la navicella non potrà muoversi.

Figura 12 - Screenshot del gioco

Page 13: Arkanoid on Altera DE-1

APPENDICE A: PORZIONI SIGNIFICATIVE DI CODICE SORGENTE

SINCRONIZZAZIONE VGA:

--Horizontal Sync --Reset Horizontal Counter IF (h_cnt = 799) THEN h_cnt <= "0000000000"; ELSE h_cnt <= h_cnt + 1; END IF; --Generate Horizontal Sync IF (h_cnt <= 755) AND (h_cnt >= 659) THEN h_sync <= '0'; ELSE h_sync <= '1'; END IF; --Vertical Sync --Reset Vertical Counter

IF (v_cnt >= 524) AND (h_cnt >= 699) THEN v_cnt <= "0000000000"; ELSIF (h_cnt = 699) THEN v_cnt <= v_cnt + 1; END IF; --Generate Vertical Sync IF (v_cnt <= 494) AND (v_cnt >= 493) THEN v_sync <= '0'; ELSE v_sync <= '1'; END IF; --Generate Horizontal Data IF (h_cnt <= 639) THEN horizontal_en <= '1'; --column <= h_cnt; ELSE horizontal_en <= '0'; END IF; --Generate Vertical Data IF (v_cnt <= 479) THEN

vertical_en <= '1'; --row <= v_cnt; ELSE vertical_en <= '0'; END IF;

DECODER DISPLAY A 7 SEGMENTI:

case vite is when 0 => leds1 <= "0111111"; when 1 => leds1 <="0000110”; when 2 => leds1 <= "1011011”; when 3 => leds1 <="1001111”; when others => NULL;

end case;

DIVISORE DI FREQUENZA:

ENTITY dimezzaClock IS PORT (clock_f : IN STD_LOGIC; clock_f_mezzi : OUT STD_LOGIC); END dimezzaClock; ARCHITECTURE comportamento OF dimezzaClock IS SIGNAL conta : STD_LOGIC_VECTOR(0 DOWNTO 0); BEGIN PROCESS (clock_f) BEGIN IF clock_f = '0' AND clock_f'event THEN conta <= conta + 1; END IF; END PROCESS; clock_f_mezzi <= conta(0); END comportamento;

Page 14: Arkanoid on Altera DE-1

BIBLIOGRAFIA

Sincronizzazione VGA: http://www.pyroelectro.com/tutorials/vhdl_vga/

Arkanoid Game: http://en.wikipedia.org/wiki/Arkanoid

Terasic DE1 Development Board Manuals: http://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&No=83

Altera Web Site: http://www.altera.com/