ELECTRÓNICA DIGITAL PRÁCTICA 5 SIMULACIÓN EN VHDL CON VERIBEST En esta práctica vamos a ver una breve introducción a la simulación de circuitos electrónicos mediante el uso de Lenguajes de Descripción de Hardware, y en concreto utilizaremos el simulador de VHDL de Veribest. El uso de los comandos e instrucciones más comunes de esta herramienta se ilustra mediante un ejemplo sencillo, y abarca desde la creación de un fichero, la compilación, la visualización de las formas de onda... 0-. Realización de la práctica. Para el desarrollo de la práctica, vamos a realizar la simulación completa de cuantos circuitos electrónicos podamos. Sugerencia: comenzar por los más sencillos (puertas), y luego aumentar la complejidad (mux, sumadores…). 1-. Arranque del programa. Se puede realizar de varias formas, desde el menú de Inicio, bien mediante el icono de acceso directo (si existe), o conociendo la ruta del programa, que debería ser: C:\Programs\Veribest VB99.0\Veribest VHDL simulator \ Veribest VHDL La ventana del simulador se muestra en la siguiente figura: Fig. 1. Ventana inicial del simulador Veribest. 2-. Creación de espacio de trabajo. Antes de empezar a trabajar con esta herramienta, es conveniente crearse lo que se llama un espacio de trabajo (Workspace), donde guardaremos todos nuestros ficheros VHDL. En la barra de herramientas seleccionad Workspace, y dentro de ella New. A continuación se nos pedirá un nombre para nuestro espacio y una ruta. El nombre será Practicas y la ruta c:\Practicas_VHDL. Aparecerá inmediatamente una nueva ventana con el nombre Practicas.vpd, con una serie de botones algunos de los cuales se comentarán más adelante, y una carpeta llamada Practicas source, donde se guardarán nuestros ficheros. 3-. Edición de un fichero. Para crear un fichero nuevo se selecciona FileÎNew, y en la ventana que aparece escogeremos la opción VHDL source file. Como nombre del fichero escogeremos el que más nos interese, guardándolo desde FileÎSave as. En ocasiones perderemos la extensión *.vhd, necesaria para que nuestro fichero sea reconocido. A partir de este momento, podemos introducir nuestro código. Es una buena costumbre que las palabras reservadas del lenguaje las escribamos en mayúsculas y el resto en minúsculas, aunque el compilador no distingue entre ambas. Una vez escrito el código lo guardamos (FileÎSave), y añadimos el fichero creado al espacio de trabajo, presionando en la ventana del Workspace la tecla +, y escogiendo el nombre de nuestro fichero creado. Si no apareciese, hemos perdido la extensión, abriendo todos los archivos (*.*) se soluciona el problema. Aparecerá nuestro archivo añadido, y podemos cerrar la ventana con el código fuente. Como simple ejemplo, empezaremos con un circuito muy sencillo; una puerta OR de dos entradas. El código para esta podría ser: LIBRARY IEEE; USE IEEE.std_logic_1164.all; ENTITY or2 IS PORT (a, b: in bit; q: out bit); END or2; ARCHITECTURE or2_comp OF or2 IS BEGIN q <= a OR b; END or2_comp; 4-. Compilación. En realidad no es un proceso exactamente de compilación el que vamos a realizar, porque no crearemos un archivo ejecutable, sino uno listo para ser simulado. Establecemos las condiciones de compilación: WorkspaceÎSettings En la nueva ventana, aseguraos que la opción Debug está seleccionada, después haced clic en aceptar. A continuación se analiza el fichero: WorkspaceÎCompile <nombre de nuestro fichero> Si el código es correcto, aparecerán mensajes de compilación de las entidades y arquitecturas, componentes y demás que nuestro circuito posea, seguidos del mensaje Done. Si el código no es correcto, aquí se marcará la línea donde se encuentra(n) el error(es) y de que tipo es. En este caso los corregiremos y repetiremos el proceso de compilación. Cuando el proceso no da errores, lo guardamos (Save). La unidad compilada se almacena en la librería WORK. Para ver su ubicación, seleccionad Library en la barra de herramientas, y dentro de ella Library Browser, o pinchad en el icono correspondiente. Aparecen las diferentes librerías, IEEE, STD, VB, y en rojo WORK, WORKLIB. Si pinchamos en esta aparecen, con símbolos de chip, los diferentes circuitos que hayamos compilado; no su código, sino la entidad, arquitectura…etc., que llevan asociadas. Podemos Ver información de fecha y opciones de compilación en la parte derecha de la ventana. (Ver figura). Fig. 2. Aspecto del Veribest con el Library Browser activado. 5-. Preparación del diseño. Por cuestiones de flexibilidad, se aconseja que la entidad y la(s) arquitectura(s) asociadas a un diseño ocupen ficheros separados (pero no es en realidad necesario). Si lo hacemos así, llegados a este punto y dentro de WORK, tendremos el icono de nuestra entidad en blanco. Si el fichero de código lleva asociada una arquitectura, estará en rojo. No se puede simular un circuito si no tiene asociada al menos una arquitectura y un fichero de test. En nuestro caso podríamos haber separado la declaración de entidad de la puerta OR en un fichero (p. ej. OR2_ENT.vhd), y la arquitectura en otro (OR2_ARQ.vhd), respetando en ambos casos el código fuente, y de ser así al compilar la entidad tendríamos el chip en blanco (sin opción a simular), y tras añadir la arquitectura el chip en rojo (solo faltaría el test). El momento más delicado de una simulación es la preparación de este fichero de test. Debe barrer, idealmente, todas las posibles variaciones en las entradas del sistema para que estemos seguros de que las salidas sean las correctas, pero lógicamente no siempre va a poder ser así. En nuestro caso, está bastante claro cuales son las formas de onda que necesitamos para verificar el comportamiento de nuestro sistema. a Fig. 3. Formas de onda que representan la tabla de verdad de una puerta OR de dos entradas. b q 0 5 10 15 20 (tiempo en nanosegundos). El fichero de test necesita una entidad y una arquitectura. Las entidades de ficheros test no tienen puertos. Las arquitecturas son estructurales, incluyendo en su declaración el componente que se va a testear, la arquitectura que se le asocia y la declaración de señales. En nuestro caso: ENTITY or2_test IS END or2_test; ARCHITECTURE test_flujo OF or2_test IS COMPONENT or2 PORT ( a, b: IN BIT; q: OUT BIT); END COMPONENT; FOR I: or2 USE ENTITY WORK.or2 (or2_comp); SIGNAL a, b, q: BIT; BEGIN I: or2 PORT MAP (a, b, q); a <= '0', '1' AFTER 5 ns, '0' AFTER 15 ns; b <= '0', '1' AFTER 10 ns, '0' AFTER 20 ns; END test_flujo; Se compilará el fichero buscando errores, y añadiéndose a la librería WORK como se vio anteriormente. De momento, es recomendable que veamos como se han añadido las nuevas entidad y arquitectura a la librería. 6-. Simulación Lo primero que hay que hacer es establecer las condiciones de simulación, en WorkspaceÎSettings Como en el caso de la compilación, pero ahora se selecciona SIMULATE y se abre la carpeta WORK. Ahí aparece la entidad y la arquitectura de nuestro fichero de test, y por supuesto el resto de las que tengamos. Hay que simular el test, que es donde tenemos configurada la entidad de nuestra puerta OR2, de modo que pinchamos en él. Sobre el espacio que acompaña a la palabra Entity nos colocaremos, activando el botón de Set. A continuación seleccionamos la arquitectura Test_flujo, seleccionándola como arquitectura mediante el mismo procedimiento. Por último, activamos la opción Trace On (que nos permite visualizar las formas de onda resultantes). Esto también se logra desplegando SimulateÎTrace. A continuación activaremos el simulador. Es recomendable tener activada, en la parte inferior de la pantalla, la pestaña GENERAL de la ventana de mensajes. Podemos poner en marcha la simulación desde WorkspaceÎExecute Simulator, y en la pantalla de mensajes se nos avisara, probablemente, de la no existencia de la licencia, y por lo tanto de que el simulador funcionará a baja capacidad. Esto no es importante, aceptaremos el mensaje y continuaremos. En la pestaña SIMULATE Se puede observar que no hay errores, y la simulación está lista para ser comenzada. En la barra de herramientas, presionad el botón de Play, en verde. El tiempo en nanosegundos resaltado debe ser superior al que en nuestro fichero de test hayamos empleado. En nuestro caso lo es. Se nos avisará de que se ha efectuado la simulación del tiempo requerido. Otra opción para activar la simulación es, en la barra de herramientas SimulateÎRun. Se puede detener la simulación mediante la selección de la función Quit, o su símbolo en la barra de herramientas (Stop). Queremos visualizar los resultados de la simulación. Lo lograremos pinchando en el icono de visualización de las formas de onda (en la barra de herramientas aparece muy gráficamente reflejado de cual se trata). Aparece una ventana nueva, Waveform Viewer, en la que debemos seleccionar las señales a visualizar, lo cual haremos una a una mediante la función Add, o todas ellas a la vez con Add All. En cualquier caso, obtenemos lo siguiente: Fig. 4. Waveform Viewer, las formas de onda obtenidas en la simulación. Donde podemos ver que el resultado obtenido es el que cabía esperar. Ahora estamos en disposición de comenzar desde el principio, añadiendo nuevos circuitos a nuestro espacio de trabajo. Salimos del simulador mediante el icono Eject. 7-. VHDL Es de resaltar que, como en todos los lenguajes de programación, la versatilidad del VHDL nos permite realizar el mismo circuito de muchas formas distintas. En un circuito medianamente complicado tendremos probablemente tantas soluciones diferentes como personas aborden el problema. Un último circuito nos permite estudiar algunas de las funciones más destacadas del VHDL. En este ejemplo nos encontramos con uno de los casos en los que el archivo de testeo no puede cubrir todas las posibilidades, o no resulta rentable así hacerlo: Comparador de 4 bits. LIBRARY IEEE; USE IEEE.std_logic_1164.all; ENTITY comp_4bit IS PORT (a, b: IN BIT_VECTOR (3 DOWNTO 0); agtb, altb, aeqtb: OUT BIT); END comp_4bit; ARCHITECTURE comportamental OF comp_4bit IS BEGIN PROCESS (a, b) VARIABLE var_agtb: BIT := '0'; VARIABLE var_altb: BIT := '0'; VARIABLE var_aeqtb: BIT := '0'; BEGIN L1: FOR i IN 3 DOWNTO 0 LOOP IF ((a(i) = '1') and (b(i) = '0')) THEN var_agtb := '1'; ELSE var_agtb := '0'; END IF; IF ((a(i) = '0') and (b(i) = '1')) THEN var_altb := '1'; ELSE var_altb := '0'; END IF; IF (a(i) = b(i)) THEN var_aeqtb := '1'; ELSE var_aeqtb := '0'; END IF; END LOOP L1; agtb <= var_agtb; altb <= var_altb; aeqtb <= var_aeqtb; END PROCESS; END comportamental; Test del comparador. ENTITY comp_test IS END comp_test; ARCHITECTURE test_flujo OF comp_test IS COMPONENT comp_4bit PORT ( a, b: IN BIT_VECTOR (3 DOWNTO 0); agtb, altb, aeqtb: OUT BIT); END COMPONENT; FOR I: comp_4bit USE ENTITY WORK.comp_4bit (comportamental); SIGNAL a, b: BIT_VECTOR (3 DOWNTO 0); SIGNAL agtb, altb, aeqtb: BIT; BEGIN I: comp_4bit PORT MAP (a, b, agtb, altb, aeqtb); a <= "0000", "0001" AFTER 5 ns, "0010" AFTER 10 ns, "0011" AFTER 15 ns, "0100" AFTER 20 ns, "0101" AFTER 25 ns, "0110" AFTER 30 ns, "0111" AFTER 35 ns, "1000" AFTER 40 ns, "1001" AFTER 45 ns, "1010" AFTER 50 ns, "1011" AFTER 55 ns, "1100" AFTER 60 ns, "1101" AFTER 65 ns, "1110" AFTER 70 ns, "1111" AFTER 75 ns, "0000" AFTER 80 ns; b <= "0001", "0000" AFTER 5 ns, "0010" AFTER 10 ns, "0011" AFTER 15 ns, "0001" AFTER 20 ns, "0100" AFTER 25 ns, "0110" AFTER 30 ns, "1111" AFTER 35 ns, "0110" AFTER 40 ns, "1000" AFTER 45 ns, "1011" AFTER 50 ns, "1011" AFTER 55 ns, "1100" AFTER 60 ns, "1101" AFTER 65 ns, "0110" AFTER 70 ns, "1101" AFTER 75 ns, "0000" AFTER 80 ns; END test_flujo; NOTA: En las formas de onda que el simulador nos ofrece podemos observar que, por ejemplo ante las entradas a = “0100”, b = “0001” la salida del sistema es a < b, no a > b. Este error se repite en la misma dirección y a la inversa (a > b cuando debería ser a < b) en diferentes ejemplos de entrada y salida. ¿A que se debe el fallo de comparación? ¿Cómo lo podríamos corregir?