VHDL

33
VHDL: MODELADO Y SINTESIS DE CIRCUITOS DIGITALES I INTRODUCCION El presente texto tiene por objeto presentar los principios del diseño de sistemas digitales de baja y mediana complejidad por medio del lenguaje de diseño VHDL. Está dirigido a estudiantes y profesionistas de Ingeniería Eléctrica, Electrónica y de Computación, que cuenten con conocimientos básicos de circuitos lógicos, y de programación con algún lenguaje de alto nivel (Java, C, Fortran, etc.). Se pretende que el material permita al estudiante comprender y utilizar el lenguaje como una herramienta, de una manera rápida y eficiente. A diferencia de otros textos sobre el mismo tema, no se presenta una descripción completa del lenguaje, pero se enfatiza la relación entre el código y el circuito sintetizado. Además, se presenta con claridad, desde un principio, la naturaleza concurrente de las instrucciones de VHDL, que la distingue de otros lenguajes de programación. La letra V de VHDL significa en inglés “Very High Speed Integrated Circuit”; HDL significa “Hardware Description Language”. El lenguaje se diseñó justamente para simplificar el diseño de los complejos circuitos que emplean las computadoras, los sistemas de comunicación, los aparatos domésticos inteligentes, etc. Pero puede utilizarse también como un vehículo de estudio de los circuitos lógicos, pues permite modelar y simular el comportamiento de cualquier sistema digital. Es claro así que VHDL se convierte en una herramienta poderosa para apoyar cursos de diseño lógico y arquitectura de computadoras; permite implementar circuitos en un dispositivo programable en un corto período de tiempo, sin tener que alambrar múltiples circuitos integrados en una tablilla. Los ejemplos presentados se han programado utilizando el software Quartus II V7.2 de Altera; sus correspondientes 1

Transcript of VHDL

Page 1: VHDL

VHDL: MODELADO Y SINTESIS DE CIRCUITOS DIGITALES

I INTRODUCCIONEl presente texto tiene por objeto presentar los principios del diseño de sistemas digitales de baja y mediana complejidad por medio del lenguaje de diseño VHDL. Está dirigido a estudiantes y profesionistas de Ingeniería Eléctrica, Electrónica y de Computación, que cuenten con conocimientos básicos de circuitos lógicos, y de programación con algún lenguaje de alto nivel (Java, C, Fortran, etc.).

Se pretende que el material permita al estudiante comprender y utilizar el lenguaje como una herramienta, de una manera rápida y eficiente. A diferencia de otros textos sobre el mismo tema, no se presenta una descripción completa del lenguaje, pero se enfatiza la relación entre el código y el circuito sintetizado. Además, se presenta con claridad, desde un principio, la naturaleza concurrente de las instrucciones de VHDL, que la distingue de otros lenguajes de programación.

La letra V de VHDL significa en inglés “Very High Speed Integrated Circuit”; HDL significa “Hardware Description Language”. El lenguaje se diseñó justamente para simplificar el diseño de los complejos circuitos que emplean las computadoras, los sistemas de comunicación, los aparatos domésticos inteligentes, etc. Pero puede utilizarse también como un vehículo de estudio de los circuitos lógicos, pues permite modelar y simular el comportamiento de cualquier sistema digital. Es claro así que VHDL se convierte en una herramienta poderosa para apoyar cursos de diseño lógico y arquitectura de computadoras; permite implementar circuitos en un dispositivo programable en un corto período de tiempo, sin tener que alambrar múltiples circuitos integrados en una tablilla.

Los ejemplos presentados se han programado utilizando el software Quartus II V7.2 de Altera; sus correspondientes circuitos se han implementado en una tablilla DE2 del mismo fabricante, que incluye un FPGA Cyclone

II. ASPECTOS GENERALES

Antes de proseguir, presentamos algunos aspectos generales e invariantes del lenguaje:

1. Es indistinto usar letras mayúsculas o minúsculas. Por ejemplo, las 2 instrucciones siguientes son semejantes:

Dsal<=A and b; dSAl<= a And B;

dsAl<= a AND B;

1

Page 2: VHDL

2. Los espacios o tabulaciones no alteran el significado. Por ejemplo, la instrucción siguiente es semejante a las 2 anteriores:

D sa L< = a And B ; 3. Es importante comentar el código. Usar 2 guiones sucesivos para indicar que el texto siguiente es un comentario. Ejemplo

S<= A xor B xor Cin; -- suma de A y B

4. Toda instrucción termina con el caracter “;” (punto y coma).

5. Identificadores. Son los nombres que asignamos a señales, variables, funciones, etc. Siguen las reglas siguientes:a) No hay límite para el número de caracteres (aunque no se recomiendan identificadores demasiado largos).b) Los caracteres son letras (A-Z y a-z), dígitos (0-9) y el guión bajo “_”,c) Empiezan con un caracter alfabético, y terminan con un caracter alfabético o numérico.d) No se permiten 2 guiones bajos consecutivos.e) No deben coincidir con palabras reservadas del lenguaje (ver Apéndice A).

6. Instrucciones if, loop, case. Como se verá más adelante, VHDL incluye estas instrucciones, y deben respetarse las reglas siguientes:a) Toda instrucción if debe terminarse con end if.b) Cada instrucción if incluye una componente then.c) Cada instrucción case termina con end case.d) Cada instrucción loop termina con end loop

7. El tipo de una señal debe ser igual al de cualquier expresión que se le asigne.

III. ESTRUCTURA DE UN PROGRAMA VHDL Un programa VHDL consta fundamentalmente de 2 partes, o unidades declarativas. La primera representa a cualquier circuito como una “caja negra”, y se denomina como entity (entidad), que posee ciertos puertos de entrada y salida. Se declara como

entity nombre_de_la_entidad is [cláusula de puertos]end nombre_de_la_entidad;

La cláusula de puertos, a su vez , es la lista de puertos de la entidad, que representan señales o interfaces con el mundo externo, separados por punto y coma, de la forma

port ( nombre_de_puerto: modo tipo_de_dato; nombre_de_puerto: modo tipo_de_dato; ………………… …. nombre_de_puerto: modo tipo_de_dato); El modo indica si la señal es de entrada (in), de salida (out) o que actúa como entrada y salida (inout y buffer). Existen diversos tipos de datos, y utilizaremos por ahora los más

2

Page 3: VHDL

comunes: bit y bit_vector. En el caso en el que varios puertos posean el mismo modo y tipo, es posible juntar sus nombres en una sola línea. Cada puerto posee un nombre único. Por ejemplo, la entidad de un circuito sumador que posee 2 entradas A0 y B0, un acarreo de entrada C0, una salida SUM0 y otra salida C1, puede declarase como

entity sumador is port(A0, B0, C0: in bit; SUM0, C1: out bit); end sumador;

entity sumador is

port(A0, B0, C0: in std_logic;

end sumador;La segunda parte de un programa VHDL se denomina architecture, y describe la estructura interna de la entidad; visto desde otro punto de vista, describe lo que hace el circuito. La descripción no suele ser única; pueden existir varias arquitecturas que describen a la misma entidad; además, existen varios estilos para la descripción. Estos se denominan como: flujo de datos, funcional, estructural, o híbrido si se emplean varios de estos estilos.

Como ejemplo de una declaración de arquitectura, describimos ahora la arquitectura de tipo flujo de datos (por ecuaciones booleanas) del circuito correspondiente a la entidad sumador

Architecture uno of sumador isbegin C1<= (A0 and B0) or (A0 and C0) or (B0 and C0); SUM0<= A0 xor B0 xor C0;end uno;

Se observa que la declaración posee un nombre arbitrario (en este caso “uno”); la palabra begin se antepone a las instrucciones que describen la lógica del sumador, y la declaración finaliza con end uno.

3

Page 4: VHDL

El operador “<= “ utilizado en las 2 instrucciones es un operador de asignación para señales. Los operadores xor, and, or son los operadores lógicos conocidos.

IV INSTRUCCIONES CONCURRENTESVHDL difiere significativamente de otros lenguajes de programación, por la razón de que sus instrucciones se ejecutan concurrentemente. En el ejemplo del sumador anterior, no se evalúa primero C1 y después SUM0, sino que se evalúan simultáneamente, cada vez que cambia cualquiera de las señales A0, B0, o C0. Esto es necesario, puesto que dichas señales producen cambios simultáneos en el circuito físico del sumador, y el lenguaje que modela el circuito debe reflejar dicha situación. En la Figura 1 se muestra el circuito, tal como se sintetiza por el programa; en la Figura 2 se muestra su simulación, con diversos valores de las entradas. Nótese como varían las salidas simultáneamente con cada cambio de las entradas A0, B0, C0, establecidas cada intervalo de 10 ns.

Figura 1. Circuito sumador sintetizado por el programa

Figura 2. Simulación del sumador

a) Un primer tipo de instrucción concurrente es el que ya se discutió: La asignación simple, que tiene la forma

señal_destino <= expresión

La expresión incluye otras señales y operadores. Podemos pensar que la señal destino es uno de los puertos de salida, o la salida de un dispositivo interno, como una compuerta OR, por ejemplo. Es obvio que, a diferencia de otros lenguajes de programación, no es posible

4

Page 5: VHDL

incluir 2 o más instrucciones de asignación simple al mismo destino; ello equivaldría a un corto circuito. (Salvo en buses y el uso de la función RESOLVED, que no se trata aquí).

Ejemplo 1: Escribir el código (identidad y arquitectura) para implementar la función expresada en la siguiente tabla de verdad:A B C F0 0 0 00 0 1 10 1 0 00 1 1 11 0 0 01 0 1 01 1 0 11 1 1 1

Solución: De la tabla, tenemos que F=A´∙B´∙C+A´∙B∙C+A∙B∙C´+A∙B∙C=A´∙C+A∙B. Luego

entity ejemplo1 isport(A, B, C: in std_logic; F: out std_logic);end ejemplo1;architecture uno of ejemplo1 isbegin F<= (not A and C) or (A and B);end uno;

b) Un Segundo tipo de instrucción concurrente es la asignación condicional, de la forma

señal_destino<= expresión1 when condición1 [ else expresión2 when condición2 else… expresiónn ];

En esta forma, las cláusulas incluidas en el paréntesis rectangular […] son optativas. El resultado de la instrucción es que se asigna al destino la expresión j cuya condición j es la verdadera. Usaremos esta instrucción en el ejemplo siguiente, para ilustrar otro estilo de flujo de datos. Ejemplo 2: Escribir el código (identidad y arquitectura) para implementar la función expresada en la tabla del ejemplo1.

Solución: Usamos la misma expresión y entidad, pero determinamos los casos en las que es cierta o falsa:

Architecture dos of ejemplo1 is

5

Page 6: VHDL

begin F<= ‘1’ when (( A=’0’ and C=’1’) or (A=’1’ and B=’1’)) else ‘0’;end dos;

Notar que los valores lógicos se escriben entre comillas simples. Notar, asimismo, que hemos utilizado operadores relacionales (=).

Ilustraremos ahora el uso del tipo bit_vector, que, a diferencia de bit, se refiere a un bus de 2 o más bits, para escribir el código de un multiplexor 4 a 1. En este circuito, un bus SEL de control selecciona cual de las 4 entradas I aparece en la salida Y.

La señal de control SEL incluye 2 señales, SEL(1) y SEL(0). El bus de entrada I incluye las señales I(3), I(2), I(1) e I(0). El símbolo de un MUX se muestra en la Figura 1, junto con la caja negra de la entidad. El bus se resalta con mayor grosor, y se indica el número de señales o alambres que contiene.

Figura 3. Símbolo de un MUX 4 a 1 y de su entidad

entity mux4_1 is port( I: in bit_vector(3 downto 0); SEL: in bit_vector(1 downto 0); Y: out bit);end mux4_1;

architecture uno of mux4_1 isbegin Y<= I(0) when (SEL=”00”) else I(1) when (SEL=”01”) else I(2) when (SEL=”10”) else I(3);end uno;

6

Page 7: VHDL

Nótese que los valores de un vector se encierran entre comillas dobles (“00”, “01”, etc.) a diferencia de señales std_logic. Un vector puede declararse también en otro orden, como SEL: in bit_vector(0 to 1);

c) Un tercer tipo de señal concurrente contiene la cláusula with … select. Tiene la forma

with expresión_de_selección select señal_destino<= expresión1 when selección1, [expresión2 when selección2, … when selecciónn];

Ilustramos el uso de ésta instrucción para expresar otra arquitectura de flujo de datos del mux4_1 anterior:

architecture dos of mux4_1 is begin with SEL select Y<= I(0) when “00”, I(1) when “01, I(2) when “10”, I(3) when others;end dos;

Hemos introducido en el código anterior el valor de selección “others” que equivale al conjunto de valores lógicos no cubiertos por los casos anteriores.

d) Un cuarto tipo de instrucción concurrente, que se refiere al estilo funcional, utiliza un proceso. A diferencia del estilo de flujo de datos, no refleja directamente el modo en el cual se sintetiza el circuito; modela más bien el comportamiento del circuito frente a sus entradas. La sintaxis del proceso es la siguiente:

[etiqueta]: process (lista de sensitividad) begin instrucción secuencial; …………………….. instrucción secuencial; end process [etiqueta];

El uso de una etiqueta para referenciar al proceso es optativo. La lista de sensitividad incluye las señales que, al cambiar, provocan la evaluación de todas las instrucciones secuenciales del proceso.

Las instrucciones incluidas dentro del cuerpo del proceso se ejecutan en forma secuencial, en orden, como en otros lenguajes de programación, cada vez que cambia alguna de las señales de la lista de sensitividad. Sin embargo, el proceso se ejecuta concurrentemente con otras instrucciones concurrentes que se encuentren dentro del bloque de la arquitectura.

Las instrucciones secuenciales que estudiaremos por el momento son: la asignación simple (que ya conocemos), la instrucción condicional if…then…else, y la instrucción case.

7

Page 8: VHDL

La instrucción condicional tiene la forma

If condición1 then conjunto de instrucciones [else conjunto de instrucciones] end if;

En el caso en que no existe la cláusula else, como por ejemplo

if en=’1’ then q<=I;

la señal q toma el valor de I si en=’1’, pero permanece sin cambio en caso en el que en=’0’; equivale a indicar else q<=q. Esto da lugar a un circuito secuencial denominado cerrojo (“latch”), que funciona como una memoria implícita. Ver la Figura 4.

Figura 4. Cerrojo resultante de la instrucción if en=’1’ then q<=I;

Cuando sí existe la cláusula else, en el conjunto de instrucciones siguiente se puede escribir otra instrucción condicional. Para ilustrar esta situación, se muestra a continuación la arquitectura del mux4_1 mediante un proceso:

Architecture tres of mux4_1 isbegin process (I,SEL) begin if SEL=”00” then Y<=I(0); else if SEL=”01” then Y<=I(1); else if SEL=”10” then Y=I(2); else Y=I(3); end if; end if; end if; end process;end tres;

La arquitectura del mux4_1 puede escribirse aún con mayor claridad utilizando la instrucción case. Esta tiene la forma

case expresión is when casos => instrucciones secuenciales;

8

Page 9: VHDL

when casos => instrucciones secuenciales; …………………………………………. when others => instrucciones secuenciales;end case;

Consideremos que el MUX posee una entrada de habilitación en, tal que si en=’0’, la salida Y es cero. De lo contrario, opera normalmente. Su modelo VHDL resulta entonces:entity mux4_1 is port( I: in std_logic_vector(3 downto 0); SEL: in std_logic_vector(1 downto 0); en: in std_logic; Y: out std_logic);end mux4_1;

architecture cuatro of mux4_1 isbegin process(en, I, SEL) begin if en=’1’ then case SEL is when “00” => Y<=I(0); when “01” => Y<=I(1); when “10” => Y<=I(2); when others => Y<= I(3); end case; else Y<=’0’; end if; end process;end cuatro;

En general, un proceso se utiliza para la simulación y síntesis de circuitos secuenciales, que se estudian más adelante, y no se estila para circuitos combinacionales.

V. ESTILO ESTRUCTURAL. COMPONENTESHemos visto ya los estilos de flujo de datos y funcional. El estilo estructural hace uso de circuitos ya compilados (componentes) para estructurar un nuevo circuito. Un ejemplo clásico es el de un sumador de 2 bits, formado por 2 sumadores de 1 bit como el de la Figura 1, como se muestra en la Figura 5:

Figura 5. Circuito sumador de 2 bits

9

Page 10: VHDL

En el circuito, el bit de acarreo C1 de salida del primer sumador equivale al bit de acarreo de entrada del segundo. C1 es una señal intermedia, pues no forma parte de la entidad, y debe declararse, por tanto en la arquitectura. Cada sumador es una componente, que se declara también en la arquitectura, y se dice que se “instancia” al utilizarla. La componente debe estar previamente compilada, y añadida al archivo del proyecto que la utiliza. Mostramos el código de la entidad y la arquitectura a continuación:

entity sumador2bits is Port(A0,B0,C0,A1,B1: in bit; S0,S1,C2: out bit);end sumador2bits;

architecture struct of sumador2bits issignal C1,cero: std_logic; -- señal intermedia component sumador is -- componente ya compilada port(A0,B0,C0: in std_logic; SUM0,C1: out std_logic);end component;begin cero<=’0’; -- valor para asignar a C0. paso0: sumador port map ( A0, B0, cero, S0, C1); -- “instanciación” del primer sumador paso1: sumador port map ( A1, B1, C1, S1, C2); -- “instanciación” del segundo sumadorend struct;

Cuando se usa una componente, se dice que se “instancia” dicha componente. La “instanciación” se ha efectuado en el programa sustituyendo las señales “reales” por las “formales” en el orden de la declaración de la componente. Otro método para “mapear” las señales se muestra a continuación; en éste no importa el orden, sino la correspondencia. paso0: sumador port map(A0=>A0, B0=>B0, C0=> cero, SUM=>C0, C1=>C1);

VI. PAQUETES, OPERADORES Y TIPOS ARITMETICOS. OTROS TIPOS

Una vez que hemos estudiado los distintos estilos de arquitecturas, y que hemos utilizado los tipos bit y bit_vector, así como operadores lógicos y relacionales, enfocamos nuestra atención a otros tipos de señales y operadores.

Además de los tipos bit y bit_vector, VHDL provee también los tipos integer, natural, positive, boolean, string, character, real, time. Además, pueden definirse tipos adicionales (tipos enumerados); estos se definen en la arquitectura, junto con señales de dicho tipo Por ejemplo

10

Page 11: VHDL

type estado is (S1,S2,S3);signal edo: estado;

type dia_habil is (lunes, martes, miércoles, jueves, viernes);signal cita: dia_habil;

Los operadores soportados por VHDL, en orden inverso de precedencia, se muestran en la tabla de la Figura 6. Logico And Or Nand Nor Xor Xnor Not Relacional = /= < <= > >=Desplazamiento sll srl sla sra rol rorSuma + -Unario + -MultiplicaciónY División

* / Mod rem

Otros ** abs & Figura 6. Operadores VHDLLos operadores que requieren una explicación son los siguientes: los de desplazamiento son de 3 tipos: lógicos, aritméticos y rotaciones. Funcionan como sigue:

En los lógicos, se introduce ‘0’ por la derecha o la izquierda al vector. En los aritméticos también, salvo que no se altera el bit de signo, y éste se replica al desplazar a la derecha. Las rotaciones introducen el bit de un extremo al otro (Figura 7).

Figura 7. Desplazamientos

El operador & indica concatenación. Por ejemplo, si a y b son vectores de 5 bits, y c es un vector de 10 bits, se puede escribir c<=a&b. En este caso el vector a incluye los 5 bits más significativos de c.

Con el operador & es posible también efectuar desplazamientos. Por ejemplo, si a es un vector declarado como std_logic_vector(15 downto 0), la instrucción

y<=”00”&a(15 downto 2)

produce un desplazamiento lógico de 2 bits a la derecha.

Los operadores de tipo aritmético (grupos 4,5 6, y ABS, ** del 7) se utilizan de forma normal con tipos INTEGER Y REAL. Para demostrar el uso de operadores para tipos BIT y BOOLEAN, supongamos que A=”001”, B=”110”, C=”01110”, D=”11001”; las instrucciones siguientes resultan entonces como lo indican los comentarios.

11

Page 12: VHDL

E<=’1’&A(1 DOWNTO 0); -- E toma el valor “101” E<=A AND NOT B; --E toma el valor “001”E<=B SRL 1; -- E toma el valor “011”E<= A SLL 2; -- E toma el valor “100”F<=C XOR D ROR 2; -- F toma el valor “10000” ( D ROR 2 =”11110”)C AND NOT D=”00110”-- Esta expresión es cierta, luego posee el valor TRUE

Es claro que E debe estar definido como un vector de 3 bits, y F como un vector de 5 bits.

Bibliotecas y paquetes para VHDL. Las bibliotecas y paquetes se utilizan para extender la funcionalidad de VHDL, definiendo tipos, funciones, componentes y operadores “sobrecargados”. En VHDL estándar no todos los operadores se aplican a todos los tipos (los operadores aritméticos, por ejemplo, no se aplican a tipos lógicos como BIT o BIT_VECTOR). Para extender su rango de aplicación, se crean funciones “sobrecargadas”. El IEEE (Instituto de Ingenieros Eléctricos y Electrónicos) ha desarrollado bibliotecas y paquetes estándar, tales como el IEEE.std_logic_1164 y el IEEE.numeric_bit o IEEE.numeric_std.

Paquete STD_LOGIC_1164 de la biblioteca IEEE. El lenguaje VHDL nativo utiliza lógica de 2 valores (BIT y BIT_VECTOR), pero es también posible utilizar señales STD_LOGIC, que incluye 9 valores posibles; entre otros, el valor ‘Z’ de alta impedancia correspondiente a buffers de 3 estados. Esta es una adición del IEEE para mejorar el lenguaje; su utilización requiere de la declaración inicial de una biblioteca IEEE y un paquete STD_LOGIC_1164, que antecede a la de entidad:

LIBRARY IEEE;USE IEEE_STD_LOGIC_1164.ALL;

Paquetes aritméticos. Los paquetes estándar del IEEE son: IEEE.numeric_bit e IEEE.numeric_std. Estos utilizan vectores tipo signed o unsigned para representar números binarios con y sin signo en vez de bit_vector o std_logic_vector. Una señal del tipo signed o unsigned es de hecho un arreglo de bits, donde el más significativo es un signo en el caso signed (complemento a 2).

Los paquetes incluyen operadores “sobrecargados” del tipo aritmético, relacional, lógico y de desplazamiento. Los pares de operandos para operaciones aritméticas y relacionales pueden ser: unsigned con unsigned, unsigned con natural, signed con signed, signed con integer. Al utilizar operandos de longitud distinta, se añaden ceros a la izquierda del más corto para igualar sus longitudes; el resultado será de la misma longitud (se descarta el acarreo final si existe). Para operaciones lógicas (excepto NOT), ambos operandos deben ser del mismo tipo: signed o unsigned.

Para utilizar operadores sobrecargados en estos paquetes, procedemos a declarar los vectores como signed o unsigned (según sea el caso), o bien aplicamos las funciones de conversión que se enloistan más adelante. Otra alternativa es la de utilizar los paquetes std_logic_signed o std_logic_unsigned, de Synoptics, que utilizan operadores “sobrecargados” para tipos std_logic_vector (no es necesario entonces declarar vectores

12

Page 13: VHDL

signed o unsigned). Otro paquete de Synoptics es el std_logic_arith, que no usaremos en este texto. Ninguno de los paquetes de Synoptics define operaciones lógicas con vectores, por lo que se requiere usar también el paquete std_logic_1164 para las operaciones lógicas.

Presentamos a continuación el código de un sumador de 8 bits, que hace uso del paquete STD_LOGIC_SIGNED. El sumador instanciado incluye acarreo anticipado.

LIBRARY IEEE;USE IEEE.STD_LOGIC_1164.ALL;USE IEEE.STD_LOGIC_SIGNED.ALL,

ENTITY sum8 IS PORT(x,y: IN STD_LOGIC_VECTOR(7 DOWNTO 0); s: OUT STD_LOGIC_VECTOR(7 DOWNTO 0); cout, v: OUT STD_LOGIC);END sum8;

ARCHITECTURE uno OF sum8 ISSIGNAL sum: STD_LOGIC_VECTOR (8 DOWNTO 0);BEGIN sum<=('0'&x) + ('0'&y); -- resultado de 9 bits s<=sum(7 DOWNTO 0); cout<=sum(8); v<= sum(8) XOR x(7) XOR y(7) XOR sum(7); -- desbordamientoEND uno; Figura 8. Código de VHDL para un sumador de 8 bits con operadores aritméticos

Hemos definido en el código una señal sum de 9 bits; incluye la suma s de 8 bits, y el acarreo final cout. Pero si sum se ha definido como de 9 bits, se requiere que, al menos uno de los operandos sea de 9 bits. Por ello, se ha anexado un bit ‘0’ a A y a B, por medio del operador de concatenación &, formando los operandos (‘0’&A) y (‘0’&B). Para determinar si existe o no desbordamiento v, se requiere el valor de C7 (esto es, Cn). El lector deberá comprobar que este valor resulta de la expresión xn yn sum(n).

En la Figura 9. se muestra la simulación, con valores decimales de x,y,s.

Figura 9.. Simulación del sumador de la Figura 8

De requerirse operaciones numéricas, se recomienda utilizar los paquetes ieee.numeric_std junto con el ieee.std_logic_unsigned o ieee.std_logic_signed. El paquete ieee.numeric incluye un conjunto de operadores como funciones, muy extenso. Soporta, en particular, las funciones shift_left, shift_right, rotate_left, rotate_right, aplicadas a vectores signed

13

Page 14: VHDL

o unsigned, con un argumento natural adicional, que indica el número de desplazamientos a efectuar. Por ejemplo, y<=shift_right(a, 2); donde a se definió como unsigned (0 to 4); el resultado y debe ser del mismo tipo que a.

El paquete numeric incluye también funciones de conversión entre tipos (recordar que el lado derecho y el izquierdo de una asignación simple deben ser del mismo tipo), o soporta conversiones automáticas. Por ejemplo, elementos de signed, unsigned y std_logic_vector son todos del tipo std_logic. Los paquetes permiten las conversiones siguientes:

UNSIGNED(B) – permite que el compilador trate un bit_vector (std_logic_vector) como vector unsigned.BIT_VECTOR(U) --permite que el compilador trate a un vector unsigned como bit_vectorSTD_LOGIC_VECTOR(U) -- permite que el compilador trate a un vector unsigned como std_logic_vector

La conversión entre signed o unsigned a valor integer usa la función to_integer. Por ejemplo, m <= to_integer( E_u); Esta es muy útil, por ejemplo, para obtener el valor entero de la dirección de un decodificador o memoria, para poder utilizarlo como índice. Si partimos de un vector A_slv, requerimos una primera conversión a unsigned, y una segunda a integer:

m <= to_integer( unsigned ( A_slv));

Es posible también convertir un entero a signed o unsigned, mediante las funciones to_unsigned o to_signed. Las funciones incluyen un argumento entero que indica el número de bits. Por ejemplo E_u <= to_unsigned ( m,8). En resumen, tenemos las funciones

TO_INTEGER(U): -- convierte vector unsigned a integerTO_UNSIGNED(I,N): -- convierte un entero a un vector unsigned de longitud N

Mencionamos, por otro lado, que las declaraciones del tipo integer, natural o positive se efectúan normalmente indicando su rango. Por ejemplo q: integer range 0 to 7.

Por último, el tipo std_logic_vector es en realidad un caso particular de un arreglo: array, con elementos de tipo std_logic. En general es posible declarar un banco de 8 registros de 16 bits, como sigue:

type regis is array (0 to 7) of std_logic_vector(15 downto 0);signal banco_reg: regis;

VII . CIRCUITOS SECUENCIALES SINCRONOS I Para escribir código VHDL que represente circuitos secuenciales síncronos como flip-flops, registros, contadores, etc., se requiere especificar la ocurrencia del flanco de un reloj.VHDL nos permite hacerlo, con una cláusula condicional dentro de un proceso, como

14

Page 15: VHDL

if clk=’1’ and clk’event then…. Esta indica el evento en el cual el flanco creciente del reloj ocurre. De igual manera, para el flanco decreciente escribimos clk=‘0’ en vez de ‘1’. Otra cláusula que puede utilizarse es wait until clk=’1’ and clk’event, pero no se establece en el proceso en este caso la lista de sensitividad. Ejemplo 4: Escribir el código de un registro de 8 bits con entradas de reloj clk, de puesta a ceros síncrona rst, de habilitación en, entradas I y salidas Q.Solución:

library ieee;use ieee.std_logic_1164.all;

entity reg8 is port( clk, rst, en: in std_logic; I: in std_logic_vector(7 downto 0); Q: out std_logic_vector(7 downto 0));end reg8;

architecture uno of reg8 isbegin process(clk, rst, I) begin if clk='1'and clk'event then if rst='1' then Q<="00000000"; else if en='1' then Q<=I; end if; end if; end if; end process;end uno;

Hemos utilizado en el código el valor la instrucción Q<=”00000000”. Una manera práctica de asignar ceros a un vector consiste en utilizar others, como sigue:

Q<= (others => ‘0’);

Ejemplo 5 : Escribir el código para modelar un contador mod 16 con carga en paralelo, con señales de control ld y cnt, y reset asíncrono activo en bajo rstn:

library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;

entity pc16 is port( clk, ld, cnt, rstn: std_logic; I: in std_logic_vector(3 downto 0); q: out std_logic_vector(3 downto 0));end pc16;

15

Page 16: VHDL

architecture uno of pc16 issignal y: std_logic_vector(3 downto 0);begin process(clk,rstn,ld,cnt,I) begin if rstn='0' then y<= (others => '0'); else if clk='1' and clk'event then if ld='1' then y<= I; else if cnt='1' then y<=y+1; end if; end if; end if; end if; end process; q<=y;end uno;

La simulación del circuito se muestra en la Figura 10:

Figura 10. Simulación del contador con carga en paralelo

Ejemplo 6: Escribir código para desplazar 4 bits a la izquierda un registro reg a otro q, con el control de una señal shft. El bit que se introduce a la derecha es w. Solución: El programa es como sigue. Su simulación se muestra en la Figura 11.

library ieee;use ieee.std_logic_1164.all;entity despl4 is port( clk,shft,w: in std_logic;

reg: in std_logic_vector(3 downto 0); q: out std_logic_vector(3 downto 0));end despl4;architecture uno of despl4 isbegin process (clk,w,reg) begin if clk='1' and clk'event then if shft='1' then

16

Page 17: VHDL

q(3)<=reg(2); q(2)<=reg(1); q(1)<=reg(0); q(0)<=w; end if; end if; end process;end uno;

Figura 11 VIII. CLAUSULA LOOP. VARIABLES Y CONSTANTES. CLAUSULA GENERIC

Cláusula loop. VHDL posee, como otros lenguajes de programación, una instrucción de repetición, utilizando la cláusula loop dentro de un proceso; se utilizan varias formas:for índice in rango loop while condición loop loop[instrucciones secuenciales]; [instrucciones secuenciales]; exit when condición;end loop; end loop; [instrucciones secuenciales];

end loop;

Esta cláusula nos permite escribir en forma compacta el código de un registro de corrimiento grande. Por ejemplo, el desplazamiento a la derecha de un registro de 32 bits puede efectuarse como sigue. La variable i no requiere declararse dentro del proceso.

for i in 0 to 30 loop q(i)<=q(i+1); end loop; q(31)<=w;

Variables y constantes. VHDL tiene 3 tipos de objetos de datos: Señales, variables y constantes. Las variables y constantes equivalen a las utilizadas en otros lenguajes de programación, y se utilizan sólo en procesos. En particular, una variable almacena valores de diversos tipos, pero no representa un cable o conexión en un circuito, a diferencia de una señal. La asignación de valores a una variable o constante utiliza el operador “ := “. Este operador sirve también para asignar valores iniciales a una señal, al declararse. Presentamos a continuación una tabla con diversos ejemplos de declaraciones y valores iniciales.

Objeto de datos Ejemplos de declaraciones Signal Signal indice : integer range 0 to 128:= 0;

Signal bcd_val: unsigned:= “0011”;Variable Variable k: integer:=0;

Variable dato_logico: Boolean:= false;Constant Constant valor_max: integer:= 127;

17

Page 18: VHDL

Constant sel_critica: std_logic_vector(3 downto 0):=”0110”;

Ilustraremos estos objetos de datos con un programa para normalizar la mantisa de 23 bits de un número en punto flotante con el formato IEEE 754. Ejemplo 7: Escribir un programa shftloop para determinar el número n de ceros a la izquierda del primer ‘1’ de un vector de 23 bits, a la vez que se desplaza el vector n posiciones a la izquierda. .

library ieee;use ieee.std_logic_1164.all;use ieee.numeric_std.all;use ieee.std_logic_unsigned.all;

entity shiftloop is port(x: in std_logic_vector(22 downto 0); y: out std_logic_vector(22 downto 0); n: out integer range 0 to 22);end shiftloop;

architecture uno of shiftloop issignal temp: std_logic_vector(22 downto 0);signal y1: unsigned (22 downto 0);signal n1: integer range 0 to 22;begin process (x,temp) variable z: integer :=22; begin temp<=x; loop exit when (z=0 or temp(z)='1'); z:=z-1; end loop; n1<=22-z; end process;y1<=shift_left(unsigned (temp),n1);y<=std_logic_vector(y1);n<=n1;end uno;

Cláusula GENERIC. Esta cláusula permite parametrizar algún dato, como, por ejemplo, el tamaño de un arreglo. Ilustramos su uso con una variante del programa del ejemplo 6 que desplaza a la izquierda un vector de n elementos:

library ieee;use ieee.std_logic_1164.all;

18

Page 19: VHDL

entity despln is GENERIC (n: integer:=32); port( clk,shft,w: in std_logic;

reg: in std_logic_vector(n-1 downto 0); q: out std_logic_vector(n-1 downto 0));end despln;

architecture uno of despln isbegin process (clk,w,reg) begin if clk='1' and clk'event then if shft='1' then for i in n-2 downto 0 loop q(i+1)<=q(i); end loop; q(0)<=w; end if; end if; end process;end uno;

Otras diferencias entre señales y variables. Es sumamente importante entender el concepto de concurrencia para señales, en contraste con la asignación inmediata de valores a variables. En el código que se muestra a continuación, las señales s1, s2 , s3 y s4 cambian simultáneamente cuando y=’1’; sus expresiones toman los valores originales antes del cambio, de manera que s1=2, s2=1+3=4, s3=2, y s4=1+2+3=6.

architecture uno of ej is signal y: std_logic; signal s1: integer :=1; signal s2: integer :=2; signal s3: integer :=3; signal y: integer; begin process (y1) begin if y=’1’ then s1<=s2; s2<=s1+s3; s3<=s2; s4<=s1+s2+s3; end if; end process;end uno; Figura 12. Código con señales

En el código alterno de la Figura 13, en cambio, trabajamos con variables dentro del proceso; las instrucciones secuenciales cambian de inmediato. De manera que v1=2, v2=2+3=5, v3=5, s4=2+5+5=12.

architecture dos of ej is

19

Page 20: VHDL

signal y: std_logic; signal s4: integer;begin process (y1) variable v1: integer :=1; vartiable v2: integer :=2; svariable v3: integer :=3; begin if y=1 then v1:=v2; v2:=v1+v3; v3:=v2; v4<=v1+v2+v3; end if; end process;end dos;Figura 13. Código con variables.

VII . CIRCUITOS SECUENCIALES SINCRONOS II

Tratamos ahora el modelado de autómatas finitos, o máquinas algorítmicas de estados (llamadas FSM o ASM). Un diagrama a bloques de una FSM se muestra en la Figura 14.

Figura 14. Modelo de Autómata Finito

En la Figura, DES significa Decodificador de Estado Siguiente (combinacional), DS significa Decodificador de salidas (combinacional), y REG es el registro de estados. q es el estado presente, y Q el estado futuro. z representa las salidas. Cuando las salidas z dependen de las entradas independientes x, como en la Figura, tenemos un autómata tipo Mealy; en caso contrario, uno tipo Moore.

Proponemos modelar un detector de la secuencia 1011 como una máquina de Mealy; el diagrama de estados con notaciónj tradicional x/z en los arcos se muestra en la Figura 15.

Figura 15. Diagrama de estados del reconocedor de la secuencia 1011

20

Page 21: VHDL

Presentamos 2 formas de modelar al diagrama. En la primera utilizamos un solo proceso, en el cual se especifican las transiciones de estados con el frente anterior del reloj. Usamos una señal rst asíncrona para el estado inicial S0.

library ieee;use ieee.std_logic_1164.all;

entity rec2 is port(rst,clk,x: in std_logic; z: out std_logic);end rec2;architecture uno of rec2 istype estado is (S0,S1,S2,S3);signal edo: estado;begin process(clk, rst,x) begin if rst='1' then edo<= S0; else if clk='1' and clk'event then case edo is when S0 => if x='1' then edo<=S1; else edo<=S0; end if; when S1 => if x='1' then edo<=S1; else edo<=S2; end if; when S2 => if x='1' then edo<=S3; else edo<=S0; end if; when S3 => if x='1' then edo<=S1; else edo<=S2; end if; end case; end if; end if; end process; z<='1' when (edo=S3 and x='1') else '0'; -- instrucción de salida z end uno;

Presentamos ahora la arquitectura de una segunda forma, en la cual utilizamos 2 procesos: en el primero se establecen las transiciones de estados (como un bloque combinacional), y en el segundo la asignación del estado futuro (edof) al presente (edop).

library ieee;use ieee.std_logic_1164.all;

entity rec2 is port(rst,clk,x: in std_logic; z: out std_logic);end rec2;

architecture dos of rec2 istype estado is (S0,S1,S2,S3);signal edo,edof: estado;

21

Page 22: VHDL

begin process(x) begin case edo is when S0 => if x='1' then edof<=S1; else edof<=S0; end if; when S1 => if x='1' then edof<=S1; else edof<=S2; end if; when S2 => if x='1' then edof<=S3; else edof<=S0; end if; when S3 => if x='1' then edof<=S1; else edof<=S2; end if; end case; end process; process(clk,rst) begin if rst='1' then edo<=S0; else if clk='1' and clk'event then edo<=edof; end if; end if; end process; z<='1' when (edo=S3 and x='1') else '0'; -- instrucción de salida z end uno;

La instrucción para la salida z es del tipo concurrente, y se ha incorporado en ambos casos al final de la arquitectura. Esta es la forma recomendable; si se incluye dentro del proceso en el primer caso, se generaría una señal que quedaría encendida permanentemente (a menos que en otro estado se apagara).

En la Figura 14 se muestra la simulación del reconocedor, que incluye información sobre el estado. El programa Quartus II nos permite observar también el diagrama de estados, como se muestra en la Figura 15 si la síntesis se efectúa mediante un flip-flop por estado (one-hot).

Figura 16. Reconocimiento de la secuencia 1101.

22

Page 23: VHDL

Figura 15. Diagrama de estado mostrado por Quartus II

La síntesis de una ASM se efectúa por default con codificación one-hot en un FPGA cuando se usa un tipo enumerado para las variables de estado. El usuario puede utilizar sólo log2n flip-flops con la codificación que desee utilizando un atributo al declarar el tipo, como sigue:

type estado is (S0,S1,S2,S3); attribute enum_encoding: string; attribute enum_encoding of estado is “00,01,10,11);

Para terminar, mencionamos que los estados pueden declararse también como enteros (integer range 0 to 3) en vez de un tipo enumerado. En ninguno de los 2 últimos casos se mostrará el diagrama de estados.

APENDICE A: LISTA DE PALABRAS RESERVADAS

Abs downto library postponed srlAccess else linkage procedure subtypeAlter elsif literal process thenAlias end loop pure toAll entity map range transportAnd exit mod record typeArchitecture file nand register unaffectedArray for new reject unitsAssert function next rem untilAttribute generate nor report useBegin generic not return variableBlock group null rol waitbody guarded of ror when

23

Page 24: VHDL

Buffer if on select whileBus impure open severity withCase in or signal xnorComponent inertial others shared xorConfiguration inout out slaConstant is package sllDisconnect label port sra

24