UNIVERSIDAD MAYOR DE SAN ANDRES FACULTAD DE INGENIERIA CARRERA INGENIERIA ELECTRONICA
VIDEOJUEGO PONG EN VHDL UTILIZANDO TARJETA XILINX SPARTAN-3AN MATERIA : PROYECTO II
DOCENTE: ING. ROBERTO OROPEZA CRESPO
ESTUDIANTE: Américo Álvarez Surci
GESTION 2011 LA PAZ - BOLIVIA
A mis Padres que nunca dejaron de apoyarme y Ana una persona muy especial.
II
Resumen “PONG”, un video juego sencillo, es la representación del deporte de TENIS, que consiste en hacer botar el balón en una paleta a otra sin equivocarse. En el presente proyecto describimos el movimiento del balón y de la paleta en un campo diseñado para tal cometido. Analizamos el monitor VGA para poder describir un controlador del mismo, utilizando la descripcion de hardware que nos brinda el VHDL. Por ultimo mostraremos como el monitor brinda el campo de juego y también hara de interfas entre el juego y el jugador.
Abstract "PONG", a simple video game, is the representation of the sport of tennis, which consists of bouncing the ball on a pallet to another without making a mistake. In this project, we describe the movement of the ball and paddle in a field designed for that purpose. We analyze the VGA monitor to describe the same controller, using the hardware description that gives us the VHDL. Finally we show how the monitor provides the playing field and will make the interface between the game and the player.
III
LISTA DE FIGURAS Figura 1: Consola ATARI-PONG Figura 2: Videojuego original pong. Museo Vienna-1998 Figura 3: Recursos de descripción VHDL Figura 4: Analogía de entidad y arquitectura Figura 5: Compuerta and de 2 entradas Figura 6: Ejemplo de descripción en VHDL Figura 7: Modelo Esquemático Figura 8: Diagrama conceptual de un monitor TRC Figura 9: Trazado del Haz de electrones, en un monitor TRC Figura 10: Diagrama de bloques de un simple controlador VGA Figura 11: Diagrama de tiempos de un barrido horizontal Figura12: Diagrama de tiempos de un barrido vertical Figura13: Tarjeta XILINX Spartan-3AN Figura 14: Monitor TRC Figura 15: Conexión VGA, con salidas del FPGA Figura 16: Flujo grama, Videojuego PONG Figura 17: diagrama de bloques conceptual, de un trazador de objetos Figura 18: Elementos primitivos del videojuego PONG Figura 19: Mapeo de bits de un Redondo IV
Figura 20: Movimiento del balón en Rebotes de Esquina
LISTA DE TABLAS Tabla 1 : Combinación a 3 bits, RGB Tabla 2: Modulo de Sincronia en VHDL Tabla 3: Modulo Generador de pixeles, 3 objetos cuadrados. Tabla 4: Modulo Prueba 1. Tabla 5: Generador de Pixeles, con animación Tabla 6: Modulo Final, videojuego PONG en VHDL
V
INDICE GENERAL Resumen/Abstract
III
Lista de Figuras
IV
Lita de Tablas
V
Capitulo 1: ANTECEDENTES Y PROPOSITOS 1.1 Introducción
1
1.2. Objetivos
2 1.2.1 Objetivo principal 1.2.2 Objetivos secundarios
Capitulo 2: MARCO TEORICO 2.1. Antecedentes: Descripción de Hardware
3
2.1.1. Lenguajes Precursores: CDL, DDL, AHPL, ISPS, TI-HDL 2.1.2. Lenguajes Actuales: VERILOG, UDL/I, VHDL 2.2 VHDL
6 2.2.1. Entidad y Arquitectura 2.2.2 Unidad de Diseño y Librerías 2.2.3. Modelo de Diseño Básico
2.3 VGA.
10 2.3.1 Señales de Sincronización. Hsync y Vsync 2.3.2 Timing de Señales de Sincronización VGA VI
2.3.3 Generador de Pixeles Capitulo 3: DESARROLLO DEL PROYECTO 3.1 Hardware empleado:
17
XILINX Spartan 3an, Monitor VGA 3.2 Descripción del Sistema: Ping Pong
19
3.2.1 Diagrama de Flujo 3.3 Elaboración y Ejecución del Videojuego “PONG”
20
3.3.1 Señales de sincronismo: VGA SYNC 3.3.2 Generador de pixeles: PONG_GRAPH
Objetos Rectangulares
3.3.3 Objetos no Rectangulares 3.3.4 Animacion: PONG_GRAPH_ANIMADO 3.4 Programa Principal: JUEGO_PONG
36
Capitulo 4: CONCLUSIONES Y MEJORAS 4.1 Conclusiones
38
4. 2 Mejoras
39
5. BIBLIOGRAFÍA
39
6. ANEXOS
40
VII
Capitulo 1 ANTECEDENTES Y PROPOSITOS
1.1 INTRODUCCION Pong fue el primer juego desarrollado por Atari Inc. Originalmente la placa de “Pong” utilizaba transistores, condensadores, resistencias, etc. ocupando un espacio de unos cuantos metros cuadrados. Aunque ya existía el primer microprocesador, hecho por Intel en 1970, no fue utilizado aún, por su reciente lanzamiento al mercado, alto coste y ausencia de documentación. Atari Pong fue una consola creada por Atari en 1975, es la segunda videoconsola de la historia. No llevaba cartuchos, sino que tenía sólo un juego, el PONG.
Figura1: Consola ATARI-PONG
El jugador controla en el juego una paleta que tiene un movimiento rectilíneo, haciendo las veces de paleta para que la pelota rebote en esta y en las paredes del espacio de juego. El jugador pierde en cuanto más falle en hacer botar el balón en la paleta. Se utilizara un lenguaje de descripción de hardware para el cometido de reproducir el juego propuesto.
1
1.2. OBJETIVOS Con el presente trabajo se trata de enfocar los siguientes objetivos: 1.3.1 Objetivo Principal
Mediante lenguaje de descripción de hardware, implementar el video juego PONG en la tarjeta fpga XILINX.
1.3.2 Objetivos Secundarios
Conocer y definir un circuito controlador básico de sincronismo.
Conocer y definir un circuito generador de pixeles. A base de contadores.
Utilizar el monitor VGA, y dar pautas para futuros diseños.
Figura 2: Videojuego original pong. Museo Vienna-1998
2
Capitulo 2 MARCO TEORICO
Con la creciente complejidad de los diseños digitales ha aparecido una necesidad de describir un circuito de la forma más eficiente y práctica posible. Un lenguaje de programación ofrece la posibilidad de un alto nivel de abstracción y es la solución adecuada para dicha tarea.
2.1 ANTECEDENTES: DESCRIPCIÓN DE HARDWARE Los lenguajes de descripción hardware son lenguajes de alto nivel con una sintaxis similar a los de programación (C, ADA, Pascal, Modula, etc.) y una semántica que permite el modelado y simulación de los dispositivos hardware a diferentes niveles de abstracción. Los primeros lenguajes de este tipo sólo pretendían servir de vehículo de comunicación del diseño. Los actuales lenguajes han adquirido un alto grado de estandarización y han adoptado los nuevos conceptos de la ingeniería de software, permitiendo la verificación de la especificación del diseño mediante simulación. 2.1.1 Lenguajes Precursores: CDL, DDL, AHPL, ISPS, TI-HDL
CDL (Computer Design Language) Fue desarrollado por Yaohan Chu a comienzo de los años 60 bajo el principio de separación de la componente lógica y electrónica de un computador digital. CDL refleja directamente el hardware y sus operaciones, es decir, existe una correspondencia uno-auno entre los objetos y operaciones hardware (registros, RAMs, relojes, suma, cuenta, etc.) y las construcciones del lenguaje. La primera versión del simulador CDL para IBM 7090 estuvo disponible en 1968, y la versión tercera para Univac pocos años más tarde. Se utilizó en universidades y en la industria del radar y aeronáutica.
3
DDL (Digital systems Design Language) Se desarrolló a mediados de los 60 en la Universidad de Wisconsin con varios objetivos: precisión y concisión para facilitar la especificación de los diseños, potencia suficiente para modelar sistemas complejos, independencia respecto a cualquier tecnología o procedimiento de diseño, capacidad de especificación a diferentes niveles de abstracción y, finalmente, una sintaxis y una semántica que permitieran la documentación jerárquica del diseño.
AHPL (A Hardware Programming Language) Fue propuesto por F.J. Hill y G.R. Peterson unos meses más tarde que el CDL y DDL, y apareció publicado por primera vez en 1973 en la edición original de Digital Systems: Hardware Organization and Design. Los autores concibieron AHPL como un lenguaje de síntesis: todo dispositivo síncrono que pudiese ser implementado en hardware debía ser expresable en AHPL de manera tal que se pudiese traducir a una realización física siguiendo un conjunto simple de reglas. En opinión de uno de sus autores, F.J. Hill, que participó como miembro del grupo de trabajo que formuló las especificaciones originales para VHDL, la existencia de AHPL favoreció la incorporación de mecanismos para permitir el proceso de síntesis en VHDL.
ISPS (Instruction Set Processor Specifications) Con este lenguaje se dio un paso importante hacia la formalización del proceso de diseño a niveles de comportamiento. Además de la simulación y la síntesis, ISPS se utilizó en la generación de software, la verificación de programas y la evaluación de arquitecturas. ISPS favoreció los aspectos de comportamiento sobre los estructurales pero sin eliminarlos completamente.
TI-HDL (Texas Instruments-HDL) Es un lenguaje de descripción jerárquica del diseño, estructurado en bloques y basado en texto, que se ha utilizado principalmente en el diseño de circuitos integrados. Procede de 4
antiguos lenguajes usados allá por 1968, concretamente el TIBSD (TI Boolean System Description), desarrollado como lenguaje de entrada de datos para un sistema CAD de circuitos impresos, y Fusim (Functional Simulator), utilizado para describir modelos de alto nivel de microprocesadores y para generar prototipos de patrones de tests. 2.1.2 Lenguajes Actuales: VERILOG, UDL/I, VHDL
Verilog Es un lenguaje de descripción hardware diseñado por la compañía Cadence Design Systems Inc., que se ha venido utilizando como lenguaje del simulador digital Cadence. El uso de Verilog está promovido por la Open Verilog International (OVI), que publicó en octubre del 91 la primera versión del Hardware Description Language Reference Manual. En Verilog la unidad de diseño fundamental es el módulo, que describe un componente hardware con su interfaz y contenido. Desde un punto de vista funcional, un módulo Verilog contiene la información de una entidad y su correspondiente arquitectura VHDL. Verilog no proporciona compilación independiente de módulos: todos los módulos relacionados con el mismo diseño y simulación deben estar en el mismo archivo.
UDL/I (Unified Design Language for Integrated circuits) Es un lenguaje de descripción hardware que se viene desarrollando desde 1989 por la Japan Electronic Industry Development Association, dependiente de importantes compañías japonesas tales como NTT. Una de las características de UDL/I es que pretende ser específico para modelar circuitos integrados. La única unidad de diseño existente en UDL/I es la descripción de diseño, que comprende varias sub-entidades denominadas descripciones de módulos. Una descripción de diseño contiene el modelo de un circuito integrado que consta de varias subunidades o módulos, cada uno de los cuales está especificado por una descripción de módulo.
VHDL (VHSIC Hardware Description Language) Se aborda en el siguiente subtitulo. 5
2.2 VHDL Very High Speed Integrated Circuit Hardware Description Language (VHSIC HDL). Desarrollado por el Departamento de Defensa de los Estados Unidos a finales de la década de los 70's. El propósito era hacer un estándar para diseñar, modelar, y documentar circuitos complejos de manera que un diseño desarrollado por una empresa pudiera ser entendido por otra, además, que pudiera ser procesado por software para propósitos de simulación. En diciembre de 1987 VHDL se estableció como el estándar IEEE-1076. En 1993 el estándar IEEE-1076 se actualizó y un estándar adicional, el IEEE-1164, fue adoptado. En 1996, el estándar IEEE-1076.3 se convirtió en un estándar de VHDL para síntesis siendo este el que se utiliza en el diseño de sistemas digitales. Lo que ha hecho que VHDL sea en un tiempo tan corto el lenguaje de descripción de hardware más utilizado por la industria electrónica, es su independencia con la metodología de diseño utilizada por cada programador, su capacidad de descripción a diferentes niveles de abstracción (Fig.3), y en definitiva la posibilidad de poder reutilizar en diferentes módulos en cualquier aplicación futura.
Figura 3: Recursos de descripción VHDL
6
Si queremos definir cualquier elemento digital tenemos que enfocarnos en 2 bloques de descripción: 1. Entidad, 2 Arquitectura; que conforman una unidad mínima de diseño. Además tenemos la posibilidad de utilizar librerías definidas por usuario.
2.2.1 Entidad y Arquitectura La realización del modelo hardware de un dispositivo en VHDL consiste en la elaboración de dos unidades de código VHDL: una Declaración de Entidad y un Cuerpo de Arquitectura. La Declaración de Entidad es la unidad de diseño VHDL que sirve para especificar el interfaz de los dispositivos. Cumple, por tanto, funciones equivalentes a las de los símbolos en las representaciones gráficas.
Figura 4: Analogía de entidad y arquitectura
El Cuerpo de Arquitectura es la unidad de diseño VHDL que sirve para especificar el funcionamiento de un dispositivo identificado por una determinada Declaración de Entidad, por lo que se puede considerar el equivalente a las tablas de verdad o a los cronogramas.
7
En la Declaración de Entidad se define el nombre del dispositivo y sus puertos; en el Cuerpo de Arquitectura su funcionamiento, en la fig.5 el uso mediante una sentencia que utiliza un operador lógico AND de 2 entradas.
Figura 5: Compuerta and de 2 entradas
La construcción del modelo de un dispositivo en un entorno VHDL finaliza, en principio, en el momento en que las unidades VHDL que lo describen quedan almacenadas en una librería VHDL. Para ello hay que editar las unidades y compilarlas. En el proceso de compilación se comprueba que no se incumplen una serie de reglas sintácticas y semánticas.
2.2.2 Unidades de Diseño y librerías Un modelo VHDL se compone de un conjunto de unidades de diseño. Una unidad de diseño es la mínima sección de código compilable separadamente. Las unidades de diseño VHDL se construyen combinando construcciones del lenguaje (sentencias, declaraciones, etc.). Estas unidades son descritas con la declaración Entidad y el cuerpo de Arquitectura (descritos en el anterior apartado). Las librerías de vhdl, representa la unión de varias unidades de diseño mínimas, para describir un sistema más complejo. Cuando se utilizan varias unidades de diseño se
8
deberá tener un modulo principal que haga el llamado a cualquier modulo según sea el caso. Hay que mencionar además que hay que incluir las librerías, en la declaración de paquetes a utilizar en nuestra descripción en VHDL.
2.2.3. Modelo de Diseño Básico A continuación se describirá un programa básico en vhdl.
Figura 6: Ejemplo de descripción en VHDL
En la fig. 6 observamos la descripción de un pequeño programa vhdl, que describe el circuito de la fig. 7, para este descripción se utilizo una entidad, una arquitectura, además de una librería creada por usuario. Cabe mencionar que los componentes de “milibreria” 9
no se los aprecia, pero son descripciones básicas de una compuerta and de 3 entradas y una or de n entradas (utilizados en líneas 24,27-30 respectivamente).
Figura 7: Modelo Esquemático
2.3 VGA El término Video Graphics Array (VGA) Sistema gráfico de pantallas para PC que se comercializó por primera vez en 1988 por IBM.
Figura 8: Diagrama conceptual de un monitor TRC
En un monitor monocromo (Fig.8), el generador de electrones (cátodo), genera un haz de electrones enfocado que atraviesa un tubo al vacio, y finalmente llega a una pantalla
10
fluorescente. La pantalla se ilumina en el instante en que el rayo de electrones incide con los puntos de fosforo que tiene la pantalla. Una bobina de deflexión vertical y otra vertical, generan campos magnéticos variables que controlan la dirección del haz de electrones, y así poder realizar el trazado de toda la pantalla. Ósea que siguen un patrón fijo de izquierda a derecha de arriba abajo como se muestra en la Fig.9.
Figura 9: Trazado del Haz de electrones, en un monitor TRC
Osciladores internos del monitor generan formas de onda diente de sierra para controlar las dos bobinas de deflexión. Por ejemplo, si el haz de electrones se mueve desde el borde izquierdo al borde derecho, la tensión aplicada a la bobina de deflexión horizontal se incrementa gradualmente. Después de alcanzar el borde derecho, el haz vuelve rápidamente a la orilla izquierda. El funcionamiento básico de un monitor RGB (a color) es muy similar al visto, exceptuando que tiene 3 haces de electrones que inciden en los puntos de fósforo rojo, verde y azul en la pantalla. Los tres puntos se combinan para formar un píxel. Podemos ajustar los niveles de tensión de la entrada de video de tres señales para obtener el color del pixel deseado.
11
Tabla 1: Combinación a 3 bits, RGB
El puerto VGA tiene cinco señales activas: señales de sincronización horizontal y vertical (hsync y vsync), y tres señales de vídeo para el rojo, verde y azul. Un controlador de vídeo genera todas estas señales. Un diagrama de bloques simplificado de un controlador de VGA se muestra en la Fig.10. Contiene un circuito de la sincronización, con el nombre vga_sync, y un circuito de generación de píxel.
Figura 10: Diagrama de bloques de un simple controlador VGA
2.3.1. Señales de Sincronización: Hsync y Vsync El circuito de la sincronización de vídeo genera la señal hsync, que especifica el tiempo necesario para el recorrido (exploración) una fila, y la señal vsync, que especifica el
12
tiempo necesario para el recorrido transversal (scan) toda la pantalla. Las discusiones posteriores se basan en una pantalla VGA 640 por 480, con una tasa de píxel de 25 MHz, lo que significa que 25 millones de píxeles son procesadas en un segundo. SEÑAL DE SINCRONIA HORIZONTAL: HSYNC
Figura 11: Diagrama de tiempos de un barrido horizontal
En la figura anterior se observa que se trabaja con 800 pixeles, esto se debe a que usualmente los monitores CRT tienen un borde negro, conocidos como Backporch y frontporch, que son el borde izquierdo y derecho respectivamente.
13
Display: Región en que los pixeles son observables. Longitud de 640 pix.
Retrace: Región en la que los haces de electrones regresan a la izquierda. La señal de video debe de estar desactivada. Longitud 96 pix.
Right Border: Borde derecho. La señal de video desactivada. Longitud de 48px
Left Border: Borde izquierdo. La señal de video desactivada. Longitud de 96 px.
La señal de Hsync se puede obtener con un contador modulo 800. El backporch y el frontporch pueden variar entre marcas de monitores. SEÑAL DE SINCRONIA VERTICAL: VSYNC
Figura12: Diagrama de tiempos de un barrido vertical
Durante la exploración vertical, el haz de electrones se mueven gradualmente de arriba hacia abajo y luego vuelve arriba. Esto traza toda la pantalla fila a fila. Hay mucho parecido a la señal HSYNC. La unidad de tiempo del movimiento está representada en términos
de
líneas
de
exploración
horizontal.
Un
período
de
de la señal vsync es de 525 pixeles.
Display: Región en la que son observables las líneas horizontales. Longitud de 480pix.
14
Retrace: Región en la que los haces de electrones regresan a la parte superior de la pantalla. La señal de video debe de estar desactivada. Longitud 2 pix.
Bottom Border: Borde inferior. La señal de video desactivada. Longitud de 10px
Top Border: Borde Superior. La señal de video desactivada. Longitud de 33 px.
La señal de Vsync se puede obtener con un contador modulo 525. El Bottom y top border puede variar entre marcas de monitores. 2.3.2. Timing de Señales de Sincronización VGA Como se mencionó anteriormente, se supone que la tasa de píxel es de 25 MHz. Está determinada por tres parámetros: p: El número de píxeles de una línea de exploración horizontal.
pixels line
p 800
L: El número de líneas de trazado de una pantalla
L 525
lines screen
s: El número de pantallas (imágenes) por segundo. Tasa de refresco.
s 60
screens seconds
El parámetro “s” especifica la rapidez con la pantalla debe ser restaurada. Para un ojo humano, la frecuencia de actualización debe ser al menos 30 pantallas por segundo para un movimiento continuo debe de ser 60 pantallas por segundo. El cálculo del timing para las señales de sincronización se lo realiza de la siguiente manera:
pixel _ rate p L s 25M
15
pixels second
2.3.3. Generador de Pixeles El circuito de generación de de píxeles genera la señal RGB de 3 bits para el puerto VGA. Es necesario especificar el contenido de la pantalla a través de los pixel_x y pixel_y. Las señales de sincronización
VGA (vga_sync), nos proporcionar las coordenadas
actuales de cada pixel. Existen tres grandes grupos en los que podemos dividir los generadores de pixeles:
Esquemas de mapa de bits: Se traza gráficos desde una memoria, pixel a pixel. Esto consume mucha memoria, ya que cada grafico debe de estar guardado en mapa de bits
Esquema modular: Se agrupan conjunto de bits para formar un mosaico, y utilizar varios de estos para formar imágenes.
Esquema trazador de objetos: Se crea elementos (figuras), por separado siguiendo un régimen para mostrar objeto a objeto utilizando un multiplexor para este cometido. Este sistema es el que utilizaremos de aquí en adelante para describir el video juego planteado.
16
Capitulo 3 DESARROLLO DEL PROYECTO 3.1 HARDWARE EMPLEADO: XILINX SPARTAN-3AN Se emplea como elemento base un kit de desarrollo FPGA comercial referencia UG334 familia Spartan® 3A/3AN de XILINX.
Figura13: Tarjeta XILINX Spartan-3AN
Este kit de desarrollo posee: o
1 FPGA XC3S700A de la familia Spartan3 de XILINX.
o
Puerto USB para programar FPGA
o
4-Mbyte SRAM
o
64-Mbyte ddr2 SDRAM.
o
8-Mbyte Flash memory
o
4 MByte Flash paralelo
o
4 pushbutton, posicionados a manera de arriba, abajo, derecha, izquierda. 17
o
4 switches deslizables
o
8 salidas en LEDs verdes
o
1 pantalla Lcd de 16x2
o
1 Osciladores de 50-MHz.
o
1 SMA para oscilador externo
o
1 puerto VGA.
o
10/100 controlador de red con su conector
o
USB Host/Slave Controller.
o
Conector I/O para audio externo.
o
2 puertos de 9 pines para comunicación RS-232
o
Connector PS/2 mouse/keyboard
o
1 core microblaze
o
4 salidas de DAC
o
2 entradas a ADC
o
2 expansiones de salidas de 40 pines
Además se utilizo el software para la utilización de esta tarjeta de estudio el software empleado fue el ISE WEB PACK de Xilinx.
Monitor VGA
Figura 14: Monitor TRC
18
Fig.15: Conexión VGA, con salidas del FPGA
3.2 DESCRIPCIÓN DEL SISTEMA: PING PONG El sistema emulara el juego de pong para un jugador, los push-button de la tarjeta a manera de mando (control 1 player), que manejara la paleta donde botara la pelota del juego, además todo esto se observara en un monitor (vga). 3.2.1 Diagrama de Flujo Inicio
Enviar
Reset Movimiento balon
Boton Arriba
Mueve Paddle Arriba
Mueve paddle Abajo
Actualiza Datos VGA
Figura16: Flujo grama, Videojuego PONG
19
3.3 ELABORACION Y EJECUCION DEL VIDEOJUEGO PONG Por lo descrito anteriormente se procederá a elaborar el controlador del vga, modulo a modulo, siguiendo el esquema de la Figura 10. Luego se procederá a la mejora del código y posteriormente a la animación. 3.3.1. Señales de sincronismo: VGA SYNC El circuito de sincronía VGA descrito en la sección 2.3.1. Trabaja con una frecuencia de 25Mhz. El circuito esta implementado con 2 contadores: uno de modulo 800 para el escaneo horizontal, y otro de modulo 525 para el escaneo vertical. La tarjeta Xilinx Spartar-3an tiene un oscilador interno de 50Mhz. Por lo que se procederá a generar una oscilación de 25Mhz, utilizando un contador modulo 2 (signal: mod2); se definen además 2 contadores para el mapeo horizontal y vertical (signal: h_count y v_count); además incluimos 2 señales de finalización h_end y v_end que indican la finalización de cada etapa. Los valores constantes de cada región mostrada en las figuras 11 y 12 (márgenes, display, top, bottom, etc) son indicados y pueden ser cambiados fácilmente si se requiere. Para evitar fallas potenciales, se incluye buffers a la salida de las señales hsync y vsync, esto nos llevara a un retraso de ciclo de reloj, que se corregirá posteriormente añadiendo otro buffer a la salida vga, que observaremos en el generador de pixeles más adelante. VHDL del modulo de sincronización: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
------------------------------------------------------------------ Modulo_ señales de sincronía monitor VGA -- Américo Alvarez Surci ----------------------------------------------------------------library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity vga_sync is port( clk,reset: in std_logic; hsync,vsync: out std_logic; video_on, p_tick: out std_logic; pixel_x,pixel_y: out std_logic_vector(9 downto 0) );
20
--señales de syncronismo --contador de pixels 800x525
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
end vga_sync; architecture rtl of vga_sync is --vga 640 por 480, parametros de sirncronia constant HD: integer:=640; --trazado horizontal constant HR:integer:=96; --recarga horizontal constant HF:integer:=16; --margen derecho constant HB:integer:=48; --margen izquierdo constant VD:integer:=480; --trazado vertical constant VR:integer:=2; --recarga vertical constant VF:integer:=10; --margen inferior constant VB:integer:=33; --margen superior --Contador modulo 2 signal mod2_reg, mod2_next:std_logic; --señal de contadores para la sincronia vertical y horizontal signal v_count_reg, v_count_next: unsigned(9 downto 0); signal h_count_reg, h_count_next: unsigned(9 downto 0); --salidas buffer signal v_sync_reg, v_sync_next: std_logic; signal h_sync_reg, h_sync_next: std_logic; --señales de estados signal h_end, v_end, pixel_tick: std_logic; begin --registros process(clk,reset) begin if(reset='1') then mod2_reg <= '0'; v_count_reg <= (others => '0'); h_count_reg <= (others => '0'); v_sync_reg <= '0'; h_sync_reg <= '0'; elsif(clk'event and clk='1') then mod2_reg <= mod2_next; v_count_reg <= v_count_next; h_count_reg <= h_count_next; v_sync_reg <= v_sync_next; h_sync_reg <= h_sync_next; end if; end process; -- generador de 25Mhz con enable tick mod2_next <= not mod2_reg; -- 25Mhz con pixel tick pixel_tick <= '1' when mod2_reg='1' else '0'; h_end <= '1' when h_count_reg=(HD+HR+HF+HB-1) else '0'; --finaliza contador horizontal(799) y pone en cero v_end <= '1' when v_count_reg=(VD+VR+VF+VB-1) else '0'; --finaliza contador vertical(524) y pone en cero --contador modulo 800, para sincronismo horizontal process(h_count_reg, h_end, pixel_tick) begin if(pixel_tick='1') then if(h_end='1') then h_count_next <= (others=>'0'); else h_count_next <= h_count_reg+1; end if; else h_count_next <= h_count_reg; end if; end process; --contador modulo 525, para sincronismo vertical
21
--flancos subida
-- stado
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
process(v_count_reg, h_end, v_end, pixel_tick) begin if(pixel_tick='1'and h_end='1') then if(v_end='1') then v_count_next <= (others=>'0'); else v_count_next <= v_count_reg + 1; end if; else v_count_next <= v_count_reg; end if; end process; --tope horizontal y verticcal, para evitar fallos h_sync_next <= '1' when (h_count_reg>=(HD+HF)) and (h_count_reg<=(HD+HR+HF-1)) else '0'; v_sync_next <= '1' when (v_count_reg>=(VD+VF)) and (v_count_reg<=(VD+VR+VF-1)) else '0';
--656 --751 --690 --491
--VIDEO ON/OFF video_on <= '1' when ((h_count_reg < HD) and (v_count_reg < VD)) else '0'; --Señales de Salida hsync <= h_sync_reg; vsync <= v_sync_reg; pixel_x <= std_logic_vector(h_count_reg); pixel_y <= std_logic_vector(v_count_reg); p_tick <= pixel_tick; end rtl;
Tabla 2: Modulo de Sincronia en VHDL
3.3.2. Generador de Pixeles: PONGH_GRAPH Utilizando el Esquema Trazador de Objetos, visto en la sección 2.3.3, describiremos en primera instancia los elementos a mostrar en la pantalla objeto a objeto.
Figura17: diagrama de bloques conceptual, de un trazador de objetos
22
Definimos 3 objetos: WALL: pared de rebote, PADDLE: paleta de juego, BALL: balón de juego. OBJETOS RECTANGULARES
Figura 18: Elementos primitivos del videojuego PONG
Un objeto rectangular puede ser definido por sus límites en coordenadas de pantalla yaxis y x-axis(ejes Y y X respectivamente). El juego Básico cuenta con tres objetos como muestra la figura 19, la pared (wall), el balón (ball), y la paleta (paddle).
WALL: definamos la generación de la pared, definiendo dos constantes WALL_X_L y WALL_X_R tendremos los limites izquierdo como derecho de esta, además incluimos 2 señales de Salida, wall_on que será la señal que habilita el dibujo por pixeles de la pared cuando se va muestreando horizontalmente, y la señal wall_rgb que sera la señal que defina su color en combinatorio de 3 bits (ver Figura 10). constant WALL_X_L: integer:=32; constant WALL_X_R: integer:=35; … -- definiendo pixel a pixel linea vertical izquierda (wall) wall_on <= '1' when (WALL_X_L<=pix_x) and (pix_x<=WALL_X_R) else '0';
23
-- definiendo salida rgb (wall) wall_rgb <= "001";
-- azul
Segun el codigo escrito tenemos un ancho en pixeles de la pared de 4, desde el pixel 32 hasta el 35. Además se define el color de la pared, color azul.
PADDLE: Es una barra vertical (bar) que se define de igual manera que wall, con el agregado que aparte de tener límites horizontales (X), también se tiene límites verticales (Y) superior e inferior, en semejanza se tiene además las señales bar_on y bar_rgb que será el trazador de dibujo y la señal del color respectivamente. -- limites derecho e izquierdo constant BAR_X_L: integer:=600; --left constant BAR_X_R: integer:=603; --rigth -- limites superior e inferior constant BAR_Y_SIZE: integer:=72; --tamaño del paddle constant BAR_Y_T: integer:=MAX_Y/2-BAR_Y_SIZE/2; --204 constant BAR_Y_B: integer:=BAR_Y_T+BAR_Y_SIZE-1; --204 (variable) … -- definiendo pixel a pixel linea vertical bar (paddle) bar_on <= '1' when (BAR_X_L<=pix_x) and (pix_x<=BAR_X_R) and (BAR_Y_T<=pix_y) and (pix_y<=BAR_Y_B) else '0'; -- definiendo salida rgb (bar) bar_rgb <= "010"; --verde
Cabe mencionar que el código muestra una paleta de 72 pixeles verticales por 4 horizontales, las constantes BAR_Y_T y BAR_Y_B, son los limites superior e inferior. Podríamos haber definido como un valor integer solamente, pero se opto con elementos a manera de variables, por que el paddle se moverá de arriba y abajo, así que no tenemos limites superior (top) e inferior (bottom) fijos.
BALL: De manera similar al paddle(bar), se define el balón. constant BALL_SIZE: integer:=8; -- limites derecho e izquierdo constant BALL_X_L: integer:=580; constant BALL_X_R: integer:=BALL_X_L+BALL_SIZE-1; --587 -- limites superior e inferior constant BALL_Y_T: integer:=238; constant BALL_Y_B: integer:=ball_Y_T+BALL_SIZE-1; --594 (variable) … -- definiendo pixel a pixel el balon cuadrado (sq_ball) sq_ball_on <= '1' when (BALL_X_L<=pix_x) and (pix_x<=BALL_X_R) and (BALL_Y_T<=pix_y) and (pix_y<=BALL_Y_B) else '0'; -- definiendo salida rgb (sq_ball) ball_rgb <= "100";
24
Como los limites vertical y horizontal cambiaran en el movimiento del balon, se utiliza variables para definirlos.
El circuito de selección y la multiplexación, que examina las señales de los tres objetos y rutas de la señal RGB (rgb_mux: figura 18), y que traza los 3 objetos es: process(video_on, wall_on, wall_rgb, bar_on, bar_rgb, sq_ball_on, ball_rgb) begin if (video_on='0') then graph_rgb<="000"; else if (wall_on='1') then graph_rgb<=wall_rgb; elsif (bar_on='1') then graph_rgb<=bar_rgb; elsif (sq_ball_on='1') then graph_rgb<=ball_rgb; else graph_rgb<="110"; --fondo amarillo end if; end if; end process;
En los casos en los que no este trazando ningún objeto, el fondo se mostrara color amarillo, teniendo de esta manera un background de nuestra pantalla.
Entonces el Código en Vhdl para definir nuestro generador de pixeles será el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
------------------------------------------------------------------ Modulo_generador de pixeles --figuras cuadradas -- Americo Alvarez Surci ----------------------------------------------------------------library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity pong_graph is port( video_on: in std_logic; pixel_x, pixel_y: in std_logic_vector(9 downto 0); graph_rgb: out std_logic_vector (2 downto 0) ); end pong_graph; architecture rtl of pong_graph is -- coordenadas de X y Y (0,0) a (639,479), para resolucion 640x480 signal pix_x, pix_y: unsigned (9 downto 0); constant MAX_X: integer:=640; constant MAX_Y: integer:=480; -- variables para linea vertical (wall) -- limites derecho e izquierdo constant WALL_X_L: integer:=32;
25
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
constant WALL_X_R: integer:=35; -- variables para linea vertical (paddle) -- limites derecho e izquierdo constant BAR_X_L: integer:=600; constant BAR_X_R: integer:=603; -- limites superior e inferior constant BAR_Y_SIZE: integer:=72; constant BAR_Y_T: integer:=MAX_Y/2-BAR_Y_SIZE/2; constant BAR_Y_B: integer:=BAR_Y_T+BAR_Y_SIZE-1; -- variables para ball cuadrado constant BALL_SIZE: integer:=8; -- limites derecho e izquierdo constant BALL_X_L: integer:=580; constant BALL_X_R: integer:=BALL_X_L+BALL_SIZE-1; -- limites superior e inferior constant BALL_Y_T: integer:=238; constant BALL_Y_B: integer:=ball_Y_T+BALL_SIZE-1;
--left --rigth --tamaño del paddle --204 --204 (variable)
--587 --594 (variable)
-- otras señales signal wall_on, bar_on, sq_ball_on:std_logic; signal wall_rgb, bar_rgb, ball_rgb: std_logic_vector(2 downto 0); begin pix_x <= unsigned(pixel_x); pix_y <= unsigned(pixel_y); -- definiendo pixel a pixel linea vertical izquierda (wall) wall_on <= '1' when (WALL_X_L<=pix_x) and (pix_x<=WALL_X_R) else '0'; -- definiendo salida rgb (wall) wall_rgb <= "001"; azul -- definiendo pixel a pixel linea vertical bar (paddle) bar_on <= '1' when (BAR_X_L<=pix_x) and (pix_x<=BAR_X_R) and (BAR_Y_T<=pix_y) and (pix_y<=BAR_Y_B) else '0'; -- definiendo salida rgb (bar) bar_rgb <= "010"; --verde -- definiendo pixel a pixel el balon cuadrado (sq_ball) sq_ball_on <= '1' when (BALL_X_L<=pix_x) and (pix_x<=BALL_X_R) and (BALL_Y_T<=pix_y) and (pix_y<=BALL_Y_B) else '0'; -- definiendo salida rgb (sq_ball) ball_rgb <= "100"; --rojo --rgb mux (multiplexor para elegir rgb) process(video_on, wall_on, wall_rgb, bar_on, bar_rgb, sq_ball_on, ball_rgb) begin if (video_on='0') then graph_rgb<="000"; else if (wall_on='1') then graph_rgb<=wall_rgb; elsif (bar_on='1') then graph_rgb<=bar_rgb; elsif (sq_ball_on='1') then graph_rgb<=ball_rgb; else graph_rgb<="110"; --fondo amarillo end if; end if; end process; end rtl;
Tabla 3: Modulo Generador de pixeles, 3 objetos cuadrados.
26
--
Realizado nuestro generador de pixeles, definimos el circuito completo uniendo los elementos de vga_sync y pong_graph. Se define una señal pixel_tick, que cuando está habilitada carga la señal rgb al buffer definido para la salida al monitor, esta señal sirve para sincronizar la salida RGB con las señales vsync y hsync.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
------------------------------------------------------------------ Modulo_PRUEBA… --Observando Figuras Cuadradas -- Americo Alvarez Surci ----------------------------------------------------------------library ieee; use ieee.std_logic_1164.all; entity pong_esquema is port( clk, reset: in std_logic; hsync, vsync: out std_logic; rgb: out std_logic_vector(2 downto 0) ); end pong_esquema; architecture rtl of pong_esquema is signal pixel_x, pixel_y: std_logic_vector(9 downto 0); signal video_on, pixel_tick: std_logic; signal rgb_reg, rgb_next: std_logic_vector(2 downto 0); begin -- modulo vga_sync vga_sync_unidad: entity work.vga_sync port map(clk=>clk, reset=>reset, hsync=>hsync, vsync=>vsync, video_on=>video_on, p_tick=>pixel_tick, pixel_x=>pixel_x, pixel_y=>pixel_y); --modulo pong_graph (pixel generator) pong_graph_unidad: entity work.pong_graph port map(video_on=>video_on, pixel_x=>pixel_x, pixel_y=>pixel_y, graph_rgb=>rgb_next); --buffer de salida RGB process (clk) begin if (clk'event and clk='1') then if (pixel_tick='1') then rgb_reg <= rgb_next; end if; end if; end process; rgb <= rgb_reg; end rtl;
Tabla 4: Modulo Prueba 1.
Los códigos de la tabla 2 y tabla 3 son el modulo de sincronía y el generador de pixeles para objetos cuadrados, ambos códigos no se pueden observar físicamente en su
27
funcionamiento cuando se los carga por separado en el kit de desarrollo xilinx, porque solo son contadores (modulo de sincronía), y habilitador de pixeles cuando está en cierta cuenta (generador de pixeles). Por lo que para realizar nuestra primera prueba y observar que estas descripciones son correctas se procedió a escribir el código presentado en la tabla 4 que será implementada en la tarjeta. Observando que nuestros códigos anteriores no tiene errores continuamos con el desarrollo de nuestro proyecto.
3.3.3. Objetos no rectangulares El diseño directo de un objeto no rectangular utilizando limites en coordenadas es complicado, una alternativa es especificar el modelo en mapa de bits.
Si queremos
obtener un circulo utilizaremos un modelo de 8 por 8 pixels (figura 20). Para la generación del balón redondo seguimos el siguiente procedimiento:
En la exploración, comprobamos si se traza el balón rectángulo de 8x8 pix.
Si esta en el caso de trazar un cuadrado (visto en anterior inciso), se obtiene el mapa de bits para generar el balón redondo.
Usando la activación del círculo, obtenemos una señal para habilitar el rgb para colorear el círculo.
Figura 19: Mapeo de bits de un Redondo
De igual manera que hicimos en la sección 2.3.2, se definen todos los parámetros con las modificaciones siguientes: Para implementar el sistema con balón redondo, tenemos que
28
incluir un ROM para almacenar el patrón del mapa de bits de nuestra forma. El BALL_ROM tiene por longitud de palabra 8 bits en 8 direcciones. Traza el círculo dentro del cuadrado cuando se activa el rd_ball_on. constant BALL_SIZE: integer:=8; -- limites derecho e izquierdo signal ball_x_l, ball_x_r: unsigned(9 downto 0); -- limites superior e inferior signal ball_y_t, ball_y_b: unsigned(9 downto 0); --Imagen de BALL redondo utilizando ROM type rom_type is array (0 to 7) of std_logic_vector(0 to 7); --definiendo nuestra rom para la forma de balon constant BALL_ROM: rom_type:= ( "00111100", -- **** "01111110", -- ****** "11111111", -- ******** "11111111", -- ******** "11111111", -- ******** "11111111", -- ******** "01111110", -- ****** "00111100" -- **** ); signal rom_addr, rom_col: unsigned (2 downto 0); signal rom_data: std_logic_vector(7 downto 0); signal rom_bit: std_logic; -- señal que indica el mapeo del redondo dentro del cuadrado 8x8 signal rd_ball_on: std_logic;
Además tendremos que modificar la parte de generación de pixeles para obtener nuestro circulo: -- definiendo pixel a pixel el balón (habilitando señal de mapeo) sq_ball_on <= '1' when (ball_x_l<=pix_x) and (pix_x<=ball_x_r) and (ball_y_t<=pix_y) and (pix_y<=ball_y_b) else '0'; -- mapeando el balon segun la ROM (redondo) rom_addr <= pix_y(2 downto 0) - ball_y_t(2 downto 0); rom_col <= pix_x(2 downto 0) - ball_x_l(2 downto 0); rom_data <= BALL_ROM(to_integer(rom_addr)); rom_bit <= rom_data(to_integer(rom_col)); -- habilitando el balon redondo rd_ball_on <= '1' when (sq_ball_on='1') and (rom_bit='1') else '0'; -- definiendo salida rgb del balon ball_rgb <= "100"; --rojo
29
Si sq_ball esta en uno se empieza el mapeo de la región cuadrada 8x8 en la pantalla, dentro de esta región mediante la ROM se obtiene las coordenadas de exploración. Restando los tres LSB desde el límite superior (es decir, de ball_y_t) proporciona la fila correspondiente del ROM (es decir, rom_addr), y restando los tres LSB desde el limite izquierdo (es decir, ball_x_l), establece la columna ROM (es decir, rom_col). Finalmente tenemos que hacer modificaciones al circuito de multiplexacion rgb process(video_on, wa… begin … elsif (rd_ball_on='1') then graph_rgb<=ball_rgb; … end process;
Todas las modificaciones al código de las tablas 2 y 3 descritas en este subtitulo, se las incluirá en la siguiente subseccion.
3.3.4. Animación: PONG_GRAPH_ANIMADO Cuando un objeto cambia su ubicación “poco a poco” en cada exploración en pantalla, se crea la ilusión de movimiento (se anima). Para ello, podemos usar registros para almacenar los límites de un objeto y la actualización de su valor en cada ciclo reloj. En el juego
de
ping
pong,
la
paleta
es
controlada
por
dos
pulsadores y se puede mover arriba y abajo, y la pelota puede moverse y rebotar en todas las direcciones. Estos 2 elementos son los que presentaran animación. Si bien el controlador VGA es controlado por una tasa de píxel de 25 MHz, la pantalla del monitor VGA sólo se actualiza 60 veces por segundo. Por tal razón los registros se deben actualizar a este ritmo. Definimos una señal refr_tick que afirma 60Hz en cada segundo. Examinemos en primer lugar nuestro generador de pixeles para el paddle, debemos que cambiar las constantes bar_y_t y bar_y_b por señales (límite superior e inferior de la paleta), y además crear un registro bar_y_reg para almacenar el valor actual del límite superior y su actualización. Si uno de los botones del control del juego es presionado
30
bar_y_reg aumenta o disminuye, en un valor constante BAR_V que representa la velocidad de movimiento de la paleta. Si btn(1) y btn(0) hacen que la paleta se desplace arriba y abajo, el segmento de código para la actualización del bar_y_reg es: -- nueva posicion en Y del paddle. process (bar_y_reg, bar_y_t, refr_tick, btn) begin bar_y_next <= bar_y_reg; --no se mueve if (refr_tick='1') then if (btn(1)='1') and (bar_y_b<(MAX_Y-BAR_V-1)) then bar_y_next <= bar_y_reg + BAR_V; --movimiento hacia abajo elsif (btn(0)='1') and (bar_y_t>BAR_V) then bar_y_next <=bar_y_reg - BAR_V; --movimiento hacia arriba end if; end if; end process;
Ahora enfoquemos el problema del balón, tenemos que cambiar las cuatro constantes limite por cuatro señales y crear 2 registros ball_x_reg y ball_y_reg para obtener los movimientos horizontales y verticales del balón, actualizando valores de posición del balón. El balón se mueve a velocidad constante en una misma dirección, la dirección puede cambiar cuando bota en las paredes o en el paddle pero no cambia la velocidad. La velocidad la descomponemos en componentes X y componentes Y, cuyos valores de velocidad pueden ser positivos BALL_V_P, o negativos BALL_V_N. Los valores actuales de las componentes las guardamos en registros x_delta_reg y y_delta_reg; el segmento de código para la actualización de ball_x_reg y ball_y_reg es: -- posicionando el balon en pantalla ball_x_next <= ball_x_reg + x_delta_reg when refr_tick='1' else ball_x_reg; ball_y_next <= ball_y_reg + y_delta_reg when refr_tick='1' else ball_y_reg;
y el segmento de código para la actualización, teniendo en cuenta la velocidad del balón guardado en registros delta: -- posicionando por velocidad process(x_delta_reg, y_delta_reg, ball_y_t, ball_x_l, ball_x_r, ball_y_b, bar_y_t, bar_y_b) begin x_delta_next <= x_delta_reg;
31
y_delta_next <= y_delta_reg; if (ball_y_t < 1) then y_delta_next <= BALL_V_P; elsif (ball_y_b > (MAX_Y-1)) then y_delta_next <= BALL_V_N; elsif (ball_x_l <= WALL_X_R) then x_delta_next <= BALL_V_P; elsif (BAR_X_L<=ball_x_r) and (ball_x_r<=BAR_X_R) then if (bar_y_t<=ball_y_b) and (ball_y_t<=bar_y_b) then x_delta_next <= BALL_V_N; end if; end if; end process;
-- alcance superior -- alcance inferior -- alcance del wall
-- registro de mov.
Cabe mencionar que igual que el juego, cuando fallamos en acertarle al balón con la paleta esta seguirá su curso perdiéndose posteriormente. A continuación se presenta el código del generador de pixeles, con animación: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
------------------------------------------------------------------ Modulo_ Generador de pixeles --Genera 2 objetos rectangulare (wall, paddle) además de --un objeto redondo. INCLUYE ANIMACION -- Americo Alvarez Surci ----------------------------------------------------------------library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity pong_graph_animado is port( clk, reset: std_logic; btn: std_logic_vector(1 downto 0); video_on:in std_logic; pixel_x, pixel_y: in std_logic_vector(9 downto 0); graph_rgb: out std_logic_vector(2 downto 0) ); end pong_graph_animado; architecture rtl of pong_graph_animado is signal refr_tick: std_logic; -- coordenadas de X y Y (0,0) a (639,479), para resolucion 640x480 signal pix_x, pix_y: unsigned (9 downto 0); constant MAX_X: integer:=640; constant MAX_Y: integer:=480; -- variables para linea vertical (wall) -- limites derecho e izquierdo constant WALL_X_L: integer:=32; constant WALL_X_R: integer:=40; -- variables para linea vertical (paddle) -- limites derecho e izquierdo constant BAR_X_L: integer:=600; constant BAR_X_R: integer:=608; -- limites superior e inferior signal bar_y_t, bar_y_b: unsigned (9 downto 0); --xxxxxxxxxxxxxxxxxxx
32
--left --rigth
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
constant BAR_Y_SIZE: integer:=72; -- registro mov. limite superior (posicion x fija) signal bar_y_reg, bar_y_next: unsigned (9 downto 0); -- movimiento del paddle cuando se preciona button (velocidad) constant BAR_V: integer:=4; -- variables para ball cuadrado (movimientos basicos no visual) constant BALL_SIZE: integer:=8; -- limites derecho e izquierdo signal ball_x_l, ball_x_r: unsigned(9 downto 0); -- limites superior e inferior signal ball_y_t, ball_y_b: unsigned(9 downto 0); -- registro mov. izquierda + limite superior signal ball_x_reg, ball_x_next: unsigned (9 downto 0); signal ball_y_reg, ball_y_next: unsigned (9 downto 0); -- registro mov. de la velocidad del ball signal x_delta_reg, x_delta_next: unsigned (9 downto 0); signal y_delta_reg, y_delta_next: unsigned (9 downto 0); -- velocidad de ball segun la posicion o registro constant BALL_V_P: unsigned (9 downto 0):=to_unsigned(2,48); constant BALL_V_N: unsigned (9 downto 0):=unsigned(to_signed(-2,48)); --Imagen de BALL redondo utilizando ROM type rom_type is array (0 to 7) of std_logic_vector(0 to 7); --definiendo nuestra rom para la forma de balon constant BALL_ROM: rom_type:= ( "00111100", -- **** "01111110", -- ****** "11111111", -- ******** "11111111", -- ******** "11111111", -- ******** "11111111", -- ******** "01111110", -- ****** "00111100" -- **** ); signal rom_addr, rom_col: unsigned (2 downto 0); signal rom_data: std_logic_vector(7 downto 0); signal rom_bit: std_logic; -- otras señales signal wall_on, bar_on, sq_ball_on, rd_ball_on: std_logic; signal wall_rgb, bar_rgb, ball_rgb: std_logic_vector(2 downto 0); begin -- registros process(clk, reset) begin if (reset='1') then bar_y_reg <= (others=>'0'); ball_x_reg <= (others=>'0'); ball_y_reg <= (others=>'0'); x_delta_reg <= ("0000000100"); y_delta_reg <= ("0000000100"); elsif(clk'event and clk='1') then bar_y_reg <= bar_y_next; ball_x_reg <= ball_x_next; ball_y_reg <= ball_y_next; x_delta_reg <= x_delta_next; y_delta_reg <= y_delta_next; end if; end process; pix_x <= unsigned (pixel_x); pix_y <= unsigned (pixel_y);
33
--tamaño del paddle
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
refr_tick <= '1' when (pix_y=481) and (pix_x=0) else '0'; -- definiendo pixel a pixel linea vertical izquierda (wall) wall_on <= '1' when (WALL_X_L<=pix_x) and (pix_x<=WALL_X_R) else '0'; -- definiendo salida rgb (wall) wall_rgb <= "001"; --azul -- definiendo el bar (paddle) -- limites bar_y_t <= bar_y_reg; bar_y_b <= bar_y_t+BAR_Y_SIZE-1; -- habilitando el paddle bar_on <= '1' when (BAR_X_L<=pix_x) and (pix_x<=BAR_X_R) and (bar_y_t<=pix_y) and (pix_y<=bar_y_b) else '0'; -- definiendo salida rgb (bar) bar_rgb <= "010"; --verde -- nueva posicion en Y del paddle. process (bar_y_reg, bar_y_t, bar_y_b, refr_tick, btn) begin bar_y_next <= bar_y_reg; --no se mueve if (refr_tick='1') then if (btn(1)='1') and (bar_y_b<(MAX_Y-BAR_V-1)) then bar_y_next <= bar_y_reg + BAR_V; --movimiento hacia abajo elsif (btn(0)='1') and (bar_y_t>BAR_V) then bar_y_next <=bar_y_reg - BAR_V; --movimiento hacia arriba end if; end if; end process; -- definiendo balon -- limites ball_x_l <= ball_x_reg; ball_y_t <= ball_y_reg; ball_x_r <= ball_x_l + BALL_SIZE - 1; ball_y_b <= ball_y_t + BALL_SIZE - 1; -- definiendo pixel a pixel el balon (habilitando señal de mapeo) sq_ball_on <= '1' when (ball_x_l<=pix_x) and (pix_x<=ball_x_r) and (ball_y_t<=pix_y) and (pix_y<=ball_y_b) else '0'; -- mapeando el balon segun la ROM (redondo) rom_addr <= pix_y(2 downto 0) - ball_y_t(2 downto 0); rom_col <= pix_x(2 downto 0) - ball_x_l(2 downto 0); rom_data <= BALL_ROM(to_integer(rom_addr)); rom_bit <= rom_data(to_integer(rom_col)); -- habilitando el balon redondo rd_ball_on <= '1' when (sq_ball_on='1') and (rom_bit='1') else '0'; -- definiendo salida rgb del balon ball_rgb <= "100"; --rojo -- posicionando el balon en pantalla ball_x_next <= ball_x_reg + x_delta_reg when refr_tick='1' else ball_x_reg; ball_y_next <= ball_y_reg + y_delta_reg when refr_tick='1' else ball_y_reg; -- posicionando por velocidad process(x_delta_reg, y_delta_reg, ball_y_t, ball_x_l, ball_x_r, ball_y_b, bar_y_t, bar_y_b) begin x_delta_next <= x_delta_reg; y_delta_next <= y_delta_reg; if (ball_y_t < 1) then -- alcance superior y_delta_next <= BALL_V_P; elsif (ball_y_b > (MAX_Y-1)) then -- alcance inferior y_delta_next <= BALL_V_N; elsif (ball_x_l <= WALL_X_R) then -- alcance del wall x_delta_next <= BALL_V_P; elsif (BAR_X_L<=ball_x_r) and (ball_x_r<=BAR_X_R) then
34
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
if (bar_y_t<=ball_y_b) and (ball_y_t<=bar_y_b) then x_delta_next <= BALL_V_N; end if;
--MOV. registro
end if; end process; --rgb mux (multiplexor para elegir rgb) process(video_on, wall_on, bar_on, rd_ball_on, wall_rgb, bar_rgb, ball_rgb) begin if (video_on='0') then graph_rgb<="000"; --negro else if (wall_on='1') then graph_rgb<=wall_rgb; elsif (bar_on='1') then graph_rgb<=bar_rgb; elsif (rd_ball_on='1') then graph_rgb<=ball_rgb; else graph_rgb<="011"; --fondo amarillo end if; end if; end process; end rtl;
Tabla 5: Generador de Pixeles, con animación
Para lo rebotes del balón en las paredes horizontales y verticales, se utilizo los registros: x_delta_reg, x_delta_next, y_delta_reg, y_delta_next; que son registros que actualizaran los
de posición de balón ball_x_reg y ball_y_reg.
Figura 20: Movimiento del balón en Rebotes de Esquina
35
3.4 PROGRAMA PRINCIPAL: JUEGO_PONG En la anterior sección se mostro todo el procedimiento que se siguió en la elaboración de este videojuego, se presento en primera instancia el diseño de un circuito de sincronización VGA_SYNC, luego se procedió a definir un generador de pixeles básico para este juego, que mostraba en pantalla 3 elementos rectangulares PONG_GRAPH además en este mismo apartado se mostro la descripción completa de este modulo pong_graph con el modulo vga_sync a manera de depurar posibles errores cometidos con la elaboración de alguna de estas descripciones, PONG_ESQUEMA fue el modulo con el que
verificamos esto, además nos dio un avance de cómo sería nuestro programa
principal utilizando 2 módulos. Luego se procedió a mejorar el aspecto visual, y realizar un balón redondo, utilizando como base de referencia nuestro pong_graph. Una vez definido como realizaremos el balón redondo se procedió a realizar nuestro generador de pixeles con un agregado importante “la animación”, PONG_GRAPH_ANIMADO nos da la base principal de un sistema de juego planteado en nuestro problema. A continuación se presenta programa principal (modulo juego_pong), que será la culminación del presente trabajo, incluye los módulos vga_sync y el pong_graph_animado: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
library ieee; use ieee.std_logic_1164.all; entity juego_pong is port( clk, reset:in std_logic; btn:in std_logic_vector(1 downto 0); hsync, vsync: out std_logic; rgb: out std_logic_vector(2 downto 0) ); end juego_pong; architecture rtl of juego_pong is signal pixel_x, pixel_y: std_logic_vector(9 downto 0); signal video_on, pixel_tick: std_logic; signal rgb_reg, rgb_next: std_logic_vector(2 downto 0); begin unidad_sincronia_vga: entity work.vga_sync port map(clk=>clk, reset=>reset, video_on=>video_on, p_tick=>pixel_tick, hsync=>hsync, vsync=>vsync, pixel_x=>pixel_x, pixel_y=>pixel_y); unidad_pong_graph_animado: entity work.pong_graph_animado port map(clk=>clk, reset=>reset, btn=>btn, video_on=>video_on, pixel_x=>pixel_x, pixel_y=>pixel_y,
36
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
graph_rgb=>rgb_next); -- definiendo salida rgb en flancos subida de periodo de clock process (clk) begin if (clk'event and clk='1') then if (pixel_tick='1') then rgb_reg <= rgb_next; end if; end if; end process; rgb <= rgb_reg; end rtl;
Tabla 6: Modulo Final, videojuego PONG en VHDL
37
Capitulo 4 CONCLUSIONES Y MEJORAS 4.1 CONCLUSIONES Se describió un monitor CRT para computadora y se trabajo directamente en el puerto de salida VGA de la tarjeta Xilinx. Se definió cada señal del conector vga. La señal de sincronismo se la trato en un principio, y se la definió con contadores distintos. El circuito generador de pixeles se lo trato en diferentes niveles, primeramente generando figuras básicas, luego con figuras definidas en una ROM. Además se vio que para la animación se utilizo registros varios.
Con el avanzar, de prueba y error en código vhdl, se pudo implementar de manera exitosa el videojuego en la tarjeta XILINX spartan-3an.
Se dio bases para futuros experimentos en tarjetas fpga, ya que el código en VHDL planteado se lo escribió de la manera más desglosada y explicando todas las sentencias y valores críticos. Los módulos presentados son aptos para la síntesis, ya que muchas líneas son reducibles y fusionarles.
38
4.1 MEJORAS El sistema descrito en este proyecto, es a manera de introducción a proyectos mas grandes utilizando la tarjeta XILINX, por lo que se puede incluir muchas mejoras a este modulo de juego. 1. Incluir otro jugador, para que se parezca más al juego de PING-PONG de 2 jugadores. 2. Incluir un interfaz de juego. Un teclado ps2 para que 2 jugadores lo utilicen como controles de juego. 3. Incluir texto al esquema generador de pixeles. Además de un texto que indique los puntos de ambos jugadores. 4. Mediante la memoria RAM de la tarjeta, incluir una presentación en imágenes de mapa de bits. A manera de modo de espera. 5. Además mediante la RAM también definir una imagen de fondo, a manera de escenario de juego (background). 6. Incluir sonidos, para el bote del balón en las paletas, además música de fondo en midi. 5. BIBLIOGRAFIA
Digital Logic And Microprocessor Design With VHDL
-
Circuit Design with VHDL (2005)
-
http://members.optusnet.com.au/jekent/FPGA.htm#Section5.1
http://hoper3d.blogspot.com/2008/09/vhdl-pong.html
http://es.scribd.com/doc/55055785/pingpong 39
Hwang
MIT
Press
6. ANEXOS
Implementacion en laboratorio utilizando Tarjeta XILINX Spartan 3AN
Campo de Juego el videojuego implementado
40