viernes, 19 de agosto de 2016

6. Programando un FPGA de Altera

Hasta el momento, solo hemos visto simulaciones de los diseños que estamos realizando. Antes de proseguir con otros sistemas digitales, es necesario aprender a llevar nuestros diseños a un CPLD o FPGA real. Es decir, ha llegado el momento de jugar...

Para la presente entrada, y para las siguientes, a menos que indique otra cosa, los diseños se probarán sobre la tarjeta de desarrollo de Altera Modelo DE1-SoC. Esta tarjeta cuenta como core a un FPGA de la Familia Cyclone V, y además, como si ya no fuera suficiente, cuenta con un Procesador ARM Cortex A9.
Tarjeta de Desarrollo DE1-SoC. (Fuente:Terasic)
Y cómo hago para programar un FPGA? Bueno, el procedimiento es muy sencillo, y si no me creen, esperan a verlo a continuación.

Programando nuestro primer FPGA

La tarjeta de desarrollo DE1-SoC viene con un set de 10 switches (SW), y un set de 10 Leds (LEDR).

La idea del primer programa que elaboraremos será mapear es estado de los 10 switches en los 10 Leds.

Empecemos por el código VHDL para este sencillo ejemplo.
library ieee;

use ieee.std_logic_1164.all;

entity puertos is
port(
sws :in std_logic_vector(9 downto 0);
leds :out std_logic_vector(9 downto 0));
end puertos;

architecture arch of puertos is
begin
leds<=sws;
end;

Como se puede apreciar, el código consiste en asignar al vector leds el estado del vector sws. Al guardar el fichero creado, Quartus II nos consultará si deseamos crear un proyecto con este fichero. En este caso, le diremos que Sí (este es otro modo de iniciar un proyecto en Quartus II).

Esto arrancará el asistente para la creación de un proyecto, que es el mismo que vimos en entradas anteriores. Definimos una ruta donde se guardarán los archivos del proyecto, el Nombre del proyecto y el fichero .vhd de mayor jerarquía. Para nuestro caso, el nombre del proyecto será Test1 y el fichero .vhd de mayor jerarquía el que acabamos de crear, es decir, puertos.vhd.

Iniciando la creación de nuestro proyecto.
Damos click a Next, y seleccionamos Add All, para añadir el fichero que hemos creado al proyecto.

Incorporando nuestro fichero de diseño.
Damos click en Next, para llegar a un punto importante: Family & Device Settings [page 3 of 5]. En esta ventana seleccionaremos a la tarjeta de desarrollo en la que grabaremos nuestro diseño. Para el caso de la tarjeta de desarrollo, el FPGA es un Cyclone V, con Nombre 5CSEMA5F31C6. Este nombre se puede encontrar sobre la superficie del FPGA, justo debajo del logo de Cyclone.

Ubicación del nombre del FPGA.
Aplicando el filtro, seleccionamos del dispositivo disponible.

Seleccionando nuestro FPGA.
Hecho ello, hacemos click en Finish.

Bueno, si bien el paso anterior es importante, en caso de que no se disponga del nombre del FPGA y se defina luego de diseñado y testeado el sistema, siempre es posible realizar la selección del FPGA, acudiendo para ello a la Opción Device del Menú de Assignments, ubicado en la barra de herramientas de Quartus II. Esto es aplicable si se desea grabar un sistema en distintos FPGAs o CPLDs.

Device, para seleccionar el dispositivo embebido que se empleará.

Bueno, al momento ya estamos a mitad de camino. A continuación procederemos a verificar nuestro diseño. Para ello, hacemos click en Start Analysis & Synthesis. Esta opción permitirá verificar si hay algún error en nuestor código. Bueno, en nuestro caso, al final del proceso, obtenemos que no hay ni errores ni advertencias (eso es bueno).

0 errors, 0 warnings. La idea es ver este mensaje cada que acabemos un diseño.

Para esta entrada, nos saltearemos la simulación, pero ustedes son libres de realizarla siguiendo los pasos descritos en la entrada 4. Primer Ejemplo y Simulación empleando archivos .VWF.

El siguiente paso es realizar la asignación de pines. Es decir, decirle a Quartus II qué pines se corresponderán con el vector leds y que otros con el vector sws.

Para ello, ingresaremos a la opción Pin Planner del Menu Assignments de la barra de herramientas.

Seleccionando Pin Planner.
Se abrirá una ventana en la que veremos, en la parte inferior, los puertos definidos por nuestro diseño, es decir, los puertos del vector sws y los puertos del vector leds.

A mapear se ha dicho.
La idea es hacer click sobre la columan Location, e ir definiendo qué pines del FPGA se asignarán a los puertos de nuestro diseño. Sin embargo, puede resultar tedioso realizar esta asignación de manera manual, además de que la posibilidad de cometer un error es alta (se tendría que buscar el datasheet de la tarjeta DE1-SoC, y ver que puertos del FPGA están conectados con los switches SW y qué puertos están conectados con los leds LEDR, e ir asignando uno a uno).

La alternativa que personalmente empleo para estos casos es realizar la importación de una asignación de pines preestablecida para la tarjeta DE1-SoC. Estos archivos suelen encontrarse en las páginas de recursos de cada tarjeta de desarrollo de Altera. Para nuestro caso, este fichero puede descargarse desde el siguiente link: https://www.altera.com/support/training/university/boards.html#de1-soc (Buscar en Resources la opción Quartus Setting File with Pin Assignments, y descargar el fichero QSF).


Este fichero contiene mapeado todos los pines del FPGA en función de los periféricos conectados a ellos en la tarjeta de desarrollo. Para importar el archivo descargado, nuevamente ingresamos a la opción Assignments, pero esta vez elegiremos Import Assignments.

Importar asignaciones de pines.
En la ventana siguiente seleccionaremos el fichero descargado, e ingresamos Ok.

Seleccionando el fichero qsf.
Si vemos ahora el Pin Planner, encontraremos que casi todos los pines del FPGA están mapeados a un nombre de puerto específico, pero si observamos la columna Direccion, veremos que estos tienen el valor de Unknown, desconocido. Esto, debido a que no existe ninguna entidad en nuestro diseño que emplee puertos con esos nombres.

¿Varios desconocidos?... no se preocupen. Aun vamos por buen camino.
Observando los puertos importados del fichero .qsf descargando, podemos ver que existe un conjunto de rótulos con el nombre de SW[0], SW[1], ... SW[9]. Estos rótulos mapean a los pines del FPGA conectados a los switches SW. Del mismo modo, los rótulos LEDR[0], LEDR[1], ... LEDR[9] mapean los pines del FPGA conectados a los diodos led LEDR. El siguiente paso es modificar nuestro diseño para que los puertos de entrada tengan la etiqueta de vector SW, y el puerto de salida la etiqueda de vector LEDR.

library ieee;

use ieee.std_logic_1164.all;

entity puertos is
port(
SW :in std_logic_vector(9 downto 0);
LEDR :out std_logic_vector(9 downto 0));
end puertos;

architecture arch of puertos is
begin
LEDR<=SW;
end;

Ejecutamos Start Analysis & Synthesis para verificar si el diseño funciona. Hecho esto, volvemos al Pin Planner, pero esta vez veremos que los puertos SW y LEDR ya tienen direcciones. Esto nos indica de que la asignación de los pines del FPGA con los puertos de nuestro diseño se realizó de manera exitosa.

Asignación exitosa.
A continuación, ejecutamos la compilación del proyecto.Al final, Quartus II nos proporcionará el reporte de compilación. Un aspecto importante es verificar el campo Logic Utilization (in ALMs), puesto que este indicador nos dirá cuantos recursos del FPGA requiere nuestro sistema. En este caso, podemos ver que no llega ni a un 1%.

Ni siquiera el 1% de los recursos del FPGA son empleados.

El siguiente paso es programar el FPGA. Para ello, conectamos nuestra tarjeta de desarrollo a un puerto USB de nuestro equipo PC. Si es la primera vez que se conecta una tarjeta de Altera a la PC, necesitaremos instalar los drivers del USB-Blaster. Estos se encuentran en la carpeta de instalación de Quartus II, en la siguiente dirección (que aplica en mi equipo, en la de ustedes puede variar): C:\altera\13.1\quartus\drivers

Una vez cargado los drivers, procederamos a ir al Menú Tools, y elegiremos la opción Programmer.

Arrancando el programador.
En la ventana del programador hacemos click en Hardware Setup. Ello con el objetivo de definir a la Tarjeta DE1-SoC como objetivo para grabar el diseño compilado.

Seleccionando al DE-SoC como hardware.
Nota: Si en este apartado no llegaran a detectar a la tarjeta de desarrollo, deberá revisarse si los drivers del USB-Blaster se instalaron correctamente.

Una vez hecho todo ello, se debe obtener una ventana similar a la mostrada a continuación.

Ya casi está...
Para evitar errores, eliminaremos la cadena de programación. Hacemos click sobre el simbolo de chip que representa nuestro FPGA y hacemos click en Delete. Luego, hacemos click en Auto-Detect, y elegimos, en nuestro caso, la opción 5CSEMA5 (las primeras letras del nombre de nuestro FPGA).


Hacemos click en Ok. Ahora, la cadena de programación se mostrará como en la siguiente figura.

Cadena de programación lista.
Vemos que ahora aparecen dos chips en la cadena de programación. No hay por qué alarmarse. Esto es correcto porque nuestra tarjeta de desarrollo tiene un procesador ARM Cortex A9 además del FPGA. En este caso, el procesador es el chip de nombre SOCVHPS y el FPGA es el chip de nombre 5CSEMA5.

Hacemos click sobre el FPGA, luego hacemos click en Change File. A continuación, buscamos en la carpeta output_files, y cargamos el fichero puertos.sof.

Cargando el fichero para la programación.
Finalmente, marcamos con check la casilla Program/Configure, y finalmente hacemos Click en Start. Al cabo de unos segundos nuestro diseño se habrá cargado en el FPGA de manera exitosa.

A un paso de terminar.


Exito!

En unos días colgaré un link de youtube mostrando al FPGA funcionando con el sistema diseñado.

Bueno, ¿qué opinan? No fue complicado, cierto... El truco es intentarlo... Hasta la siguiente entrada.












martes, 28 de enero de 2014

5. Diseño de circuitos MSI: MUX y Codificador


Empezaremos esta entrada con el diseño de algunos circuitos MSI fundamentales para, a través de ellos, estudiar nuevos conceptos en el honorable arte de la codificación VHDL. En primer lugar, un circuito MSI es un sistema digital a mediana escala, de allí las siglas Medium Scale Integration, haciendo referencia a la densidad de compuertas lógicas que integran el sistema. Si bien, esta cantidad de compuertas lógicas aun no alcanza la densidad de componentes que encontraríamos en un microprocesador, los MSI presentan suficiente complejidad como para distraer nuestra mente por un buen rato, claro que ello depende de que tantos estilos y trucos tengamos bajo la manga. Empezaremos con un multiplexor de 2 bits y dos entradas.

Diseño del MUX2to1


Gracias a nuestros buenos conocimientos en los circuitos digitales, sabemos que un multiplexor es un sistema digital cuya salida es elegida de entre dos o más entradas, de acuerdo a una entrada de selección.

Un multiplexor, nuestro primer MSI.


En este caso, la entrada A sería nuestro vector de bits A1A0, y la entrada B, B1B0. La entrada de selección S será quien decida qué vector de entrada aparecerá en nuestra salida. 

Con no mucho esfuerzo podemos crear el sistema utilizando compuertas básicas, las ya conocidas AND, OR, NOT. El esquema sería, para el caso expuesto, el siguiente:

Lógica cableada para describir un multiplexor, ¿la solución más directa?

A partir de este esquema, podríamos obtener un código en VHDL, describiendo cada compuerta mostrada y sus conexiones. Dicho código lo presento a continuación:
library ieee;

use ieee.std_logic_1164.all;
 
entity MUX2to1 is
port(
A :in std_logic_vector (1 downto 0);
B :in std_logic_vector (1 downto 0);
S :in std_logic;
Z :out std_logic_vector (1 downto 0));
end MUX2to1;

architecture arqMUX21 of MUX2to1 is
begin
Z(1)<=(A(1) and not S)or(B(1) and S);
Z(0)<=(A(0) and not S)or(B(0) and S);
end arqMUX21;
Tras compilar, verificamos si el diseño se comporta como deseamos, utilizando el simulador mediante el waveform file.

Los resultados se muestran en la siguiente imagen, y se puede ver que el diseño cumple su cometido perfectamente.

Diagrama de tiempos resultado de verificar el diseño. Se comporta de acuerdo a lo esperado.

Y con esto, habremos acabado el diseño nuestro primer circuito MSI.

Quizá pensemos que buscar otros medios para implementar este multiplexor no parece justificado, si la implementación directa de las ecuaciones booleanas parece suficiente, como se hizo líneas arriba. El problema radica al incrementar la anchura del bus de los datos de entrada. Para entradas de 4 bits, las ecuaciones obtenidas se complicarían al borde de quedar completamente incomprensibles. Si incrementamos el ancho de entrada a 8 bits, tendríamos 16 bits para los datos de entradas, más un bit se selección, de los cuales nuestra salida dependería de una manera que desanimaría a cualquiera. Y para agregarle una cereza al pastel, imagínese tener 4 entradas en lugar de 2, cada una de 8 bits. Este es el sistema que implementaremos ahora a través del VHDL y los métodos de programación que hasta ahora conocemos, y si no los conocemos, que aprenderemos ahora mismo. 

En este caso, plantear el diseño de la manera en que se hizo inicialmente nos llevaría a escribir sentencias de asignación muy largas para cada bit de salida de Z (en este caso, Z tiene 4 bits). Una manera más sencilla es manejando las entradas como lo que son, como vectores de bits, y utilizando otro tipo de sentencia. A continuación les muestro el código, y tras él, explicaré algunos puntos que quizá puedan confundirlos. 
library ieee;

use ieee.std_logic_1164.all;

entity MUX4to1 is
port(
A :in std_logic_vector(3 downto 0);
B :in std_logic_vector(3 downto 0);
C :in std_logic_vector(3 downto 0);
D :in std_logic_vector(3 downto 0);
S :in std_logic_vector(1 downto 0);
Z :out std_logic_vector(3 downto 0));
end MUX4to1;

architecture arqMUX41 of MUX4to1 is
begin
with S select
 Z <= A when "00",
 B when "01",
 C when "10",
 D when "11",
 null when others;
end architecture;
Tras simular, obtenemos las siguientes formas de onda, que verifican nuestro diseño. 


Resultados de simulación de nuestro diseño del MUX4to1.

Aclaraciones respecto al código, me parece que no hay muchas por hacer. Se manejaron todos los datos como vectores de bits, lo cual permitió reducir la codificación al manejarlos por grupos. La estructura de selección usada en la arquitectura es la siguiente:


with entrada_selectora  select


            salida    <=    seleccion1 when valor_1,


                            seleccion2 when valor_2,


                           


                            seleccionn when valor_n;


Esta estructura permite asignar salidas de acuerdo a una entrada de selección, justo como lo hace un multiplexor. En este caso se asignaron las entradas A, B, C o D a la salida Z de acuerdo al valor binario de A. Notar que solo la última asignación irá con punto y coma al final, mientras que las primeras solo serán finalizadas por comas. Un punto interesante en este caso sería la última asignación. La palabra reservada others se utiliza para referenciar a cualquier valor no utilizado en la asignación, y en este caso, si no se utiliza, nos lanzaría errores. Y ello ¿por qué? Para que funcione correctamente esta estructura de selección, deberán escogerse entre todos los posibles valores de la entrada entrada_selectora. Pero, ¿acaso no se mencionaron todos los posibles valores de la entrada S en el ejemplo? Veamos, con dos bits, usted dirá confiadamente que todos los posibles valores son “00”, “01”, “10” y “11”. Quizá para cualquier estudiante de circuitos digitales esa respuesta sea correcta, pero en estos mares de diseño, usted está equivocado. ¿Y qué valores nos faltan? Pues tendríamos “ZZ”, “XX”, “UU”, entre otros. Estos valores son todos los otros valores soportados por el tipo de entrada std_logic (eso ya lo vimos anteriormente). Como muchos de estos valores no tienen un significado físico exacto y solo son tipos de apoyo para la simulación, no es necesario recordarlos y referenciarlos todos. Imagínese tener que referenciar todas las posibles combinaciones entre ‘1’, ‘0’,  ‘x’, ’U’, ’Z’, etc. Para dos bits tendríamos más de 10 combinaciones posibles.  Ahora, si fueran 4 bits, si fueran 8. Se ve la necesidad de referenciar todos estos valores que no se utilizan directamente en el problema, y para ello se utiliza la palabra reservada others. Así, para asignaciones, basta con referenciar los valores de selección que queramos acorde a nuestro problema, y el resto, referenciarlos con others. En este caso, asignando a la salida null para los valores others, hacemos que el compilador ignore estos valores y solo tome en cuenta los valores referenciados directamente.

Así, hasta, podríamos decir que tenemos las herramientas para diseñar los multiplexores que queramos, sea cual sea la cantidad de entradas y su ancho de bits, de acuerdo a la FPGA o CPLD que tengamos disponible. El siguiente sistema MSI a diseñar será el famoso codificador.


Diseño del Code8to3



Otros famosos sistemas MSI son los codificadores. Como su nombre lo indica, estos sistemas generan en su salida un determinado código binario de acuerdo a los pines de entrada que estén activados. Para este caso, diseñaremos un codificador de 8 a 3 con prioridad al bit de mayor peso. Es decir, se tendrán 8 entradas, I7I6I5I4I3I2I1I0, de las cuales, dependiendo quien esté activa, nuestra salida Z generará el número binario del bit activado. Por ejemplo, si nuestra entrada fuese 00010000, entonces el bit activado es I4, y nuestra salida Z deberá tomar el valor de 100 (4 en binario). La condición de prioridad de aplica cuando se tienen más de una entrada activa. En tal caso, solo se codifica el bit activado de mayor peso. Por ejemplo, si nuestra entrada fuese 01001001, nuestra salida Z sería 110. 


El famoso codificador de 8 a 3.

Ahora, el para implementar este sistema en VHDL procederemos a aplicar una nueva estructura de programación denominada proceso. Para explicar qué es un proceso, primero presentamos el código de nuestro codificador, y con él hacer las explicaciones necesarias.
library ieee;

use ieee.std_logic_1164.all;

entity Code8to3 is
port(
data_in : in std_logic_vector(7 downto 0);
z : out std_logic_vector(2 downto 0));
end Code8to3;

architecture arqcod83 of Code8to3 is
begin

process(data_in)
begin
 if data_in(7)='1' then
  z<="111";
 elsif data_in(6)='1' then
  z<="110";
 elsif data_in(5)='1' then
  z<="101";
 elsif data_in(4)='1' then
  z<="100";
 elsif data_in(3)='1' then
  z<="011";
 elsif data_in(2)='1' then
  z<="010";
 elsif data_in(1)='1' then
  z<="001";
 else z<="000";
 end if;
end process;

end arqcod83;

Los resultados de simular nuestro diseño se muestran en la siguiente imagen, en la que se verifica que éste funciona correctamente.


El resultado satisfaze nuestras expectativas, el diseño cumple con lo requerido.

Ahora veamos qué cosa es este proceso. Estas estructuras de programación son las llamadas secuenciales, pues las instrucciones que se encuentren en él se ejecutarán una a continuación de otra, de manera secuencial, como lo haría un programa de C++ o JAVA. El modelo de todo proceso es el siguiente:


process (lista_sensitiva)
begin
instrucción_1;
instrucción_2;
instrucción_n;
end process;


En este proceso, la instrucción 1 se ejecutará primero, luego la instrucción 2, y así, secuencialmente hasta ejecutarse la instrucción n. Otro punto a resaltar es que todo proceso deberá declararse dentro de una arquitectura. Y, ¿qué es la lista sensitiva? Pues la lista sensitiva es el conjunto de señales que activarán al proceso.
Un proceso se activa cuando cualquier señal de su lista sensitiva cambia de estado. En el ejemplo, la lista sensitiva está formada por el vector data_in de 8 bits. De esta forma, cuando cualquier bit de dicha palabra cambie de estado, se activará el proceso con el código descrito. Ahora, el concepto de prioridad es implementado fácilmente usando las famosas condiciones if-then-else. Colocamos en el if más externo la condición de mayor prioridad, de manera que ésta se evalúe primero. 

Antes de dar por terminada esta entrada, mencionarles que las señales y salidas solo actualizarán sus valores cuando el sistema entre en estado inactivo. En el caso del codificador diseñado la salida z se actualizará en el preciso momento en que el proceso quede inactivo, cuando no se detecte cambio en los niveles de entrada de la lista sensitiva.

viernes, 22 de noviembre de 2013

4. Primer ejemplo y Simulación utilizando archivos .VWF


En esta entrada voy a mostrar como simular un diseño en VHDL utilizando el archivo de formas de onda o waveform file .vwf.

Empecemos con el sistema que hemos de programar:

El primer circuito digital que hemos de implementar en este curso... ¡qué emoción!
Quizá digan que el sistema a diseñar ahora es demasiado simple..., pero para empezar está bien. Más adelante nos toparemos con sistemas más desafiantes, y antes de querer correr, deberíamos primero gatear. Así que empecemos...

Llamemos a la salida de esta sistema Z. Entonces, gracias a nuestros conocimientos en circuitos digitales sabemos que Z= (A and C) or B. Esta descripción booleana de nuestro resultado nos ayudará a obtener la tabla de verdad de nuestro circuito. Como todos sabemos, la talba de verdad contiene toda la información que hay que tener sobre un sistema a fin de conocer su funcionamiento. Tras asignar los valores , obtenemos la siguiente tabla de verdad:

Tabla de verdad del circuito.

Con todo lo hallado, tenemos ya una descripción completa del circuito propuesto. Ahora... ¿y en VHDL?

Primera forma para codificarlo


Abrimos el Quartus II, y seleccionamos New => VHDL File. Ello nos proveerá del lienzo en el que hemos de pintar nuestro primer ejemplo.

library ieee;
use ieee.std_logic_1164.all;

entity primerejemplo1 is
port(
a,b,c :in std_logic;
z :out std_logic);
end primerejemplo1;

architecture arqej1 of primerejemplo1 is
begin
z<=(a and c)or b;
end arqej1;

Como se puede apreciar, z recibe exactamente la función booleana obtenida líneas arriba. Esta forma de programar está muy bien, pero complica las cosas cuando hablamos de circuitos más complejos, con más entradas, o circuitos secuenciales.

Al guardar el archivo (recordar hacerlo con el mismo nombre de la entidad), Quartus II nos preguntará si deseamos crear un proyecto con este archivo. Para simular el diseño, primero debemos compilarlo, y toda compilación sólo puede darse en un proyecto. Entonces la damos . Ello lanzará al Porject Wizard descrito en la entrada anterior... Seguir los pasos descritos en dicha entrada para crear el proyecto satisfactoriamente. En este caso, yo llamé al proyecto como primerejemplo, y elegí al archivo que acabamos de crear como archivo top-file. 

Nota: Como quizá lo habrán notado, esta es otra forma de crear proyectos... Esta forma es la más segura para evitar errores de compilación y evitar marearse con todas las entidades que quisieramos añadir al proyecto: Escribimos nuestro código en VHDL, lo guardamos, pero no desmarcamos la opción que sugerí desmarcar en la entrada anterior. Luego, aceptar la petición para crear el proyecto a partir de ese archivo. En la ventana del Project Wizard que les saldrá, tener cuidado al momento de escribir los nombres. Sugiero que no llamen al proyecto con el nombre del archivo .VHD que acaben de crear, debido a que si quieren añadir más archivos al proyecto, el esquema de jerarquias no quedará muy bien que digamos... Guardar con un nombre genérico al proyecto, por ejemplo, si el código que hemos hecho implementa un contador BCD de 4 bits y lo hemos guardado como contadorBDC4bits.vhd; no llamar al proyecto así, sino, por ejemplo, proyecto_contadores. Eso si, en la casilla inferior elegir como archivo top a la entidad que hallamos creado (en este caso sí sería contadorBCD4bits). Si hacen esto, si mañana más tarde desean agregar otro archivo al proyecto e implementar un diseño jerárquico, podrán hacerlo sin el temor de que se asigne mal la jerarquía.

Bueno... sigamos... Luego de esto queda compilar... para ello hacemos click en un botón similar la PLAY, de colo morado, y listo... a esperar...

Iniciando la compilación...

Si se ha hecho bien todo lo mencionado anteriormente, la compilación solo arrojará advertencias, mas no errores. Estas advertencias saltan debido a que muchas de las herramientas especiales de síntesis están deshabilitadas debido a que la versión del Quartus II es la versión gratis y no la de subscripción... Otras advertencias pueden relacionarse con las asignaciones de los pines del diseño. 

NOTA: En caso de que salga algún error, o la compilación tome mucho tiempo, para los fines de la presente entrada bastará con emplear la herramienta Start Analysis and Síntesis. Para ello, hacer click en el botón que está al lado del botón de Compilación. Si aun así se obtienen errores, entonces deberá revisar el fichero .vhd, en búsqueda de algún error.

Iniciar análisis y síntesis.




Mientras no tengamos errores, por ahora, digamos que todo va bien... Al final de la compilación, Quartus II nos proporcionará un reporte con información sobre la síntesis. 


Reporte de compilación para nuestro primer ejemplo.

¿Qué parámetros deberían tomarse en cuenta? Inicialmente, de acuerdo al FPGA o CPLD elegida para la compilación (esto se elige en el Project Wizard o haciendo click después en Assignments=>Device), el reporte nos dice el área ocupada por nuestro circuito. Esta área representa el total de elementos lógicos empleados para sintetizar el sistema, respecto a la cantidad total de elementos lógicos de la FPGA o CPLD. En este caso, vemos que nuestro circuito solo toma un elemento lógico, lo cual representa menos del 1% del total del área del FPGA, en este caso una Cyclone IV GX. Este caso nuestro ejemplo ni siquiera le hizo cosquillas a la FPGA, pero conforme incrementos la complejidas de los diseños, veremos que este parámetro será determinante al elegir entre uno u otro FPGA o CPLD.

Hecho esto, queda simular nuestro diseño. Para ello haremos uso de los waveform files o VWF. files. Para crear estos archivos, debemos abrir otra IDE: Altera University Program Simulator. Puede descargar este software gratuitamente desde la página de Altera. Este simulador externo es necesario para todas las versiones recientes del Quartus II, debido a que estas IDE's ya no vienen con el simulador incluido (recuerdo que la versión 9.1 venía con el simulador integrado... pero a partir de la versión 11.0 ya no más). 

Hecho esto, al ejecutar el programa les saldrá una ventana tipo CMD, tras la cual la verdadera IDE simuladora arrancará.

No se asusten si ven esta ventana... a través de esta el simulador arrancará...

Luego verán la siguiente plataforma, que es en la que crearán el archivo waveform para simular su diseño.

Qsim, para simular nuestros diseños en VHDL y ver si hacen lo que supuestamente deberían hacer...

El primer paso es cargar nuestro proyecto. Para ello, hacer click en File => Open Project.

Buscar el proyecto que hemos creado y cargarlo. El programa les avisará mediante:

>> Opened project: primerejemplo (Revision: primerejemplo1)

Luego, hacemos click en New (la hoja en blanco). Con esto, nos saldrá una ventana con un waveform en blanco. 

Ventana de creación de un waveform file.

Un waveform file es básicamente un diagrama de tiempos en el que asignaremos a las señales de entrada de nuestro sistema valores que cambien en el tiempo, para observar como varía la salida. (Recordar los diagramas de tiempo de las memorias RAM, algo parecido...).

Bueno, seguimos... Ahora, hacemos doble click sobre el espacio en blanco rotulado por Name. Ello hará saltar la siguiente ventana:

Ventana para seleccionar los puertos que hemos de usar para simular.

Seleccionar el Node Finder. En Filter, elegir Pins: all, luego hacer click en List. Hecho eso aparecerán todos los puertos creados en el proyecto. Hacer click en >>, luego en OK para finalizar.

Veremos todos los puertos que hemos creado en nuestro proyecto...




Hecho esto, la ventana quedará lista para determinar las formas de onda para simular nuestro proyecto.

Ya estamos listos para crear nuestras señales de prueba...

Notar que nuestra salida Z tiene valor desconocido. Ello debido que aun no se simulado nada. Debemos dejarlo así para evitar que el simulador lo interprete como colisión de señal y nos bote un error.

Por defecto, el simulador elige un tiempo de simulación de 1us. Si quieren ampliar este intervalo, hacer click en Edit => Set End Time. 

Elegir cuanto tiempo quieres simular...


Por ahora lo dejaremos en 1 us. 

Dentro del cuadro rojo, los posibles niveles que le podemos asignar a nuestras entradas... interesante ¿no creen?
Para asignar los niveles a las señales, resaltar el intervalo que deseen aignar, y elegir el nivel deseado en la parte indicada por el recuadro rojo. 

Para este caso, elegí los siguiente para las tres entradas:

Señales con sus valores ya definidos... ahora sí, ¡a simular!

A la entrada a le asigné un reloj de 100ns de periodo, a la entrada b, reloj de 200ns, y a la entrada c, un reloj de 400ns. Hecho esto, guardamos el archivo con el mismo nombre de la entidad que queremos simular y cerramos el editor de vwf.

En la ventana principal del simulador, hacer click Assign => Simulator Settings...

Ajustes de simulación.

En esta ventana, elegir el archivo .vwf que hemos creado. En este caso, primerejemplo1.vwf. La opción de abajo es importante. Existen dos tipos de simulación con archivos .vwf: la simulación funcional y la simulación temporal. La primera (Functional), simulará asumiendo que todos los elementos lógicos son ideales, y que por ende, no tienen retardos de propagación. Este tipo de simulación se usa para verificar si la estructura de tu diseño es correcta o no. La simulación temporal (Timing) en cambio sí toma en cuenta estos retardos. Esta simulación es más realista, y se usa para contrastar si tu diseño se ve afecto o no en su desempeño debido a estos retardos.

En este caso elegiré la simulación temporal. 

Hecho esto queda hacer click en la gran flecha azul para inicar la simulación. Lamentablemente os saldrá un error. Ello debido a que la familia Cyclone no es soportada por el simulador. Para corregir el error, simplemente compilar el diseño sobre otro FPGA o CPLD. En este caso, elegiremos un CPLD de la familia MAX3000A. Para hacer el cambio, hacer click en Assignments => Device.

 
Elegiremos a la buena familia de CPLD MAX3000A.

Compilamos nuevamente, y leemos el reporte. Vemos que esta vez se consumió el 3% de área del CPLD. Ello debido a que solo tiene 32 macroceldas (para los CPLD's, ya no son elementos lógicos, sino macroceldas). 

32 macroceldas ofrecidas por la MAX3000A, 1 macrocelda destinada a nuestro proyecto.
Hecho esto, en el simulador recargamos el proyecto, recargamos el archivo .vwf y le damos click a la gran flecha azul. Esta vez la simulación será exitosa. 

Aun se puede ver en rojo el error producido por querer simular en una FPGA de la familia Cyclone.

Es importante lograr el 100% de covertura en la simulación, a fin de que todas las posibilidades sean simuladas. En este caso, para las formas de onda creadas, se obtuvo un 100.00% de coverage. Si no se hubiese logrado, deberíamos cambiar las formas de onda de las entradas, buscando cubrir todas las posibilidades. Bueno, al acabar de simular, no saltará el reporte de simulación con la forma de onda de la salida.

Reporte de simulación... ¡yeah! Resultados esperados... diseño funcionando bien...

Se aprecia en el diagrama obtenido que la salida se comporta según lo esperado, lo cual verifica que el diseño está bien. Acercándonos más en la respuesta, podemos medir los retrasos de propagación de nuestro diseño. Para ello, agregamos punteros. Para ello, hacemos doble click  en el espacio en blanco entre la barra en donde están los tiempos, y el espacio en donde dibujamos nuestras formas de onda. Aparece un puntero en dicha posición. Luego, hacemos click en Edit => Snap to grid para hacer que los punteros puedan posicionarse en cualquier punto  (si Snap to grid está seleccionada, los punteros se posicionarán solo en los ejes marcados por las líneas verticales punteadas (grid)). El segundo puntero marcará la diferencia en tiempo respecto al puntero master (para diferenciarlo, el puntero master tiene un indicar de posición en la parte superior y además, tendrá una línea azul bien definida, mientras que los punteros secundarios serán líneas delgadas azules). 

Añadiendo punteros.

En este caso, el ratardo, medido usando punteros, es de 3.584ns. 

Con esto, la simulación queda finalizada.


Segunda y tercera de codificación para el primer ejemplo


Ahora, utilizaremos una señal para codificar el circuito.
library ieee;

use ieee.std_logic_1164.all;

entity primerejemplo2 is
port(
a,b,c :in std_logic;
z :out std_logic);
end primerejemplo2;

architecture arqej of primerejemplo2 is
signal ac:std_logic;
begin
ac<= a and c;
z<=ac or b;
end arqej;
Creamos la señal ac, y le asignamos el producto lógico de las entrada a y c. Luego, obtenemos nuestra salida z a partir de ella.

Al guardar el diseño, lo hacemos dentro del proyecto primerejemplo, y luego le asignamos la prioridad este archivo (lo seteamos como archivo top-level). Para ello, ver la siguiente imagen:

Set a Top-level Entity...

Compilar y seguir los pasos anteriores para simular. El resultado es el siguiente:

Mismo resultado al anterior... eso implica que los diseños son equivalentes...

La última forma para diseñar el sistema del primer ejemplo es usando la tabla de verdad:
library ieee;
use ieee.std_logic_1164.all;

entity primerejemplo3 is
port(
abc :in std_logic_vector(2 downto 0);
z :out std_logic);
end primerejemplo3;

architecture arqej of primerejemplo3 is
begin

with abc select z<=
'1' when "010",
'1' when "011",
'1' when "101",
'1' when "111",
'0' when others;

end arqej;
Esta vez se definió la entrada como un vector de 3 bits. Vemos que la salida z se asgina según los valores que tome el vector abc de acuerdo a la tabla de verdad hallada. No se preocupen por entender como funciona esa manera de asignación. En las entradas que vienen me enfocaré en sí al diseño en VHDL, explicándoles todas las maneras de asignación posibles, así como estilos de programación y otros asuntos de interés.

Tras compilar y simular, se verifica que este diseño también es equivalente a los dos planteados previamente. 

Bueno, así termina esta entrada. Hasta la próxima oportunidad.