miércoles, 30 de octubre de 2013

3. VHDL: Conceptos Básicos

Empecemos de una buena vez con lo que es el VHDL. Como les he venido diciendo, este lenguaje está orientado al desarrollo de diseños mediante la descripción del comportamiento del hardware que deseamos. Pero como todo lenguaje, éste implica el manejo de una sintaxis propia, definiciones y estilos, aspectos que van más allá de qué tan bueno eres diseñando soluciones digitales (si manejan algún otro lenguaje, sea java o C, recuerden lo duro que fue aprenderse como funcionaban las instrucciones if, then... o el polimorfismo en java... bueno, creo que me dejo entender). 
A continuación, unas cuantas definiciones básicas que nos serán muy útiles para empezar a enfrentar al VHDL con optimismo.

Conceptos Básicos


Entidad

Todo diseño en VHDL se centra en la creación de entidades. Una entidad es una representación formal de tu diseño, en el que se especificarán sus parámetros como si se tratase de una caja negra, lo cual implica establecer sus puertos de entrada y salida, sus modos y tipos, así como la determinación de parámetros que le den mayor versatilidad al diseño, denominados genéricos. La sintaxis básica para crear una entidad es la siguiente:

entity nombre_entidad is 
generic  (nombre_parámetro : tipo_genérico :=valor);
port (
nombre_terminal1    : modo   tipo;
nombre_terminal2    : modo   tipo;
...
nombre_terminaln    : modo   tipo -- La última declaración no terminará en ";"
);end  nombre_entidad;

Se puede apreciar la palabra clave entity. Esta le indica al compilador que se está creando una entidad. Recordar que el nombre que le pongan a la entidad debe ser el mismo nombre con el que guarden el archivo .vhd.

VHDL impone las siguientes restricciones en cuanto a como hemos de llamar tanto entidades, puertos, etc.:

  • Debe iniciar con una letra. No es válido: 3contador.
  • No debe contener espacios. No es válido: mi contador.
  • Si quieren usar dos o más palabras, se deberán unir con guión abajo. Sí es válido: mi_contador.
  • No puede usarse una palabra clave.
Ejemplos de identificadores válidos son: mux_4_a_2, mi_microprocesador, ejem245, a23_ft, y similares.

La palabra clave generic es opcional, y sirve para indicar parámetros genéricos. Estos básicamente actuarán como valores constantes para el diseño, pero modificables por quien hace el código. Quizá no se entienda con claridad esta idea, pero cuando veamos lo que son registros universales de n bits me parece que cualquier duda será eliminada. Un ejemplo introductorio: imagínense que quieren definir un sistema que trabaje con 4 bits. Entonces, realizan su diseño y lo compilan exitosamente. Pero luego resulta que desean que el mismo sistema trabaje ahora con 8 bits. En este caso, el parámetro genérico sería la longitud de bits, la cual se ajusta de acuerdo a lo que se quiera. De esa forma, para cambiar la longitud de la palabra de bits solo hará falta cambiar el valor del parámetor genérico. Como les dije, más adelante os explicaré esto con mayor claridad, asi que no se preocupen. Además, como lo dije al principio, esta es opcional.

Mediante port definiremos en si los puertos de entrada y salida de nuestro diseño, como si los viésemos en una caja negra. Cabe resaltar que al finalizar cada declaración se usa punto y coma (;), a excepción de la última declaración. Esta no deberá terminar en (;).

Ahora, cada declaración se lleva a cabo mediante el siguiente formato:

nombre_terminal1    : modo   tipo   :=   valor_inicial;

nombre_terminal1: Nombre con el que referenciaremos al puerto. Deberá cumplir con las restricciones descritas líneas arriba.

modo: Identifica el modo de operación del puerto. VHDL nos ofrece los siguientes modos:

IN: Configura al puerto como puerto de entrada.  El flujo de datos va desde el exterior del sistema hacia dentro del mismo. Sólo se pueden leer los datos de este puerto, y no se puede escribir nada sobre ellos (puerto de sólo lectura).

Modo de operación del puerto: IN

OUT: Configura al puerto como puerto de salida. El flujo de datos va desde el interior del sistema hacia el exterior. Sólo se puede escribir sobre este puerto, y no se puede leer el valor que tenga (puerto de sólo escritura).

Modo de operación del puerto: OUT
BUFFER: Configura al puerto como puerto de salida. El flujo de datos va desde el interior del sistema hacia el exterior, pero además se puede leer el valor de dicho puerto, por lo que es un puerto de lectura - escritura.


Modo de operación del puerto: BUFFER
 INOUT: Comfigura al puerto como puerto bidireccional. El flujo de datos va en ambas direcciones, lo cual indica que el puerto puede actuar tanto como entrada (IN) o como salida (OUT)... (De ahí el nombre de INOUT).

Modo de operación del puerto: INOUT
tipo: Indica el tipo de puerto. Los tipos básicos de VHDL son:

bit: Define al puerto como un solo bit.
Ejemplo, la entrada de un inversor.

bit_vector(Mbit downto Lbit): Define al puerto como un vector de bits de longitud Mbit-Lbit+1, con prioridad descendente.
Ejemplo, un contador cuya salida se define como: Q3 Q2 Q1 Q0 ==> Q (3 downto 0).

bit_vector(Lbit to Mbit): Define al puerto como un vector de bits de longitud Mbit-Lbit+1, con prioridad ascendente.
Ejemplo, un registro cuya entrada se define como: R2 R3 R4 R5 ==> Q (2 to 5).

Debido a que estos tipos solo admiten los dos estados básicos de la lógica digital: '1' y '0'; para darle mayor versatilidad se crearon otros tipos, los cuales se contienen dentro del paquete std_logic_1164 de la librería IEEE. Una librería, al igual que en cualquier otro lenguaje, es un conjunto de códigos hechos y verificados que nos ayudarán a no reescribir lo que amablemente otros ya han hecho. Un paquete a su vez es una subclasificacion de las librerias, que permiten ordenar los códigos según criterios. Tener en cuenta que Ud. también pueden crear sus propias librerias con sus propios paquetes, de manera que sus diseños puedan servirles como base para encarar diseños más desafiantes y complejos. Más adelante les mostraré cómo hacerlo. Para usar un paquete, primero deberán invocar a la librería, y luego al paquete, de la siguiente manera:

library nombre_libreria;
use nombre_libreria.nombre_paquete.all;

Los tipos que nos ofrece el paquete std_logic_1164 de la librería IEEE son:

std_logic: Puerto de un bit.

std_logic_vector(Mbit downto Lbit): Puerto como vector de (Mbit-Lbit+1) bits con prioridad descendente.

std_logic_vector(Lbit to Mbit): Puerto como vector de (Mbit-Lbit+1) bits con prioridad ascendente.

Pero, ¿no son las mismas definiciones? quizá se preguntará. Pues aparentemente sí. En lo que difieren estos tipos es en los posibles valores que pueden tomar. Si para los tipos bit y bit_vector teniamos solo dos posibles estados, para los tipos std_logic y std_logic_vector tendremos los siguientes:

U: Valor sin inicializar.
X: Valor desconocido forzado.
0:  Estado bajo.
1:  Estado alto.
Z: Alta impedancia.
W:Valor desconocido débil.
L: Nivel bajo forzado.
H:Nivel alto forzado.
-  :No importa (el famoso don't care).

¿Y qué eso de forzado? Bueno, esto sólo es interpretado al simular el circuito. Los valores que usaremos en nuestros diseños serán "0", "1", "Z", "-".

valor_inicial: Valor que queremos asignarle al puerto cada vez que el sistema empiece a respirar. No se utiliza mucho para los puertos, pero si para señales dentro de una arquitectura (no se preocupen, la arquitectura es lo que sigue más abajo). Ver que el símbolo de asignación es ":=".

Para finalizar, no olvidar el end al final de la declaración de la entidad.


Arquitectura

La arquitectura es la parte del código en el que se describe el funcionamiento del sistema en función de los parámetros descritos en la entidad. La forma de hacerlo es:

architecture nombre_arq of nombre_entidad is
--Declaración de señales
signal nombre_senial :tipo := valor_inicial;
begin
--codigo
--codigo
...
--codigo
end nombre_arq;

Esta vez la palabra clave es architecture. Notar que también debemos ponerle un nombre a la arquitectura. Este nombre deberá cumplir con las restricciones descritas previamente. Cada arquitectura debe vincularse a una entidad. Es por ello que se usa of nombre_entidad.

Una señal es una variable intermedia, que se usa para facilitar la codificación. Los tipos para las señales son los mismos que los tipos para los puertos, contando además con los tipos genéricos, pero estos los explicaré más adelante. Además, no olvidar que las señales deben declararse antes de que se inicie la descripción del funcionamiento, pero después de la declaración inicial de la arquitectura. En otras palabras, antes de un begin, pero después de un architecture.

Circuito digital simple en el que se simboliza la acción de una señal.

Si no quedó claro. La imagen muestra un circuito digital simple. Las ecuaciones para este sistema serían:

S=A and C

Salida=B or S

Se puede ver que también pudo usarse usa sola ecuación:

Salida= B or (A and C).

Como se ve, la señal S actuó como una variable intermedia, y con ello se pudo dividir el funcionamiento en dos partes. Esa es la misión de las señales: dividir la complejidad de un sistema.

Aclarado esto, vemos que las líneas de código que describirán el funcionamiento están entre el begin y el end nombre_arq.

Bueno, me parece que esto es suficiente para arrancar con el primer ejemplo. ¿Qué les parece si implementamos el sistema de la figura ejemplo? A mí me parece bien... pero ello se verá en la siguiente entrada, en la que además os mostraré como simular un diseño hecho en VHDL utilizando el waveform file (.vwf). Hasta entonces...

No hay comentarios:

Publicar un comentario