Documentos de Académico
Documentos de Profesional
Documentos de Cultura
(',&,Ð1/$VV
Diseño de Circuitos
con VHDL
Volnei A. Pedroni
MIT Press
Cambridge, Massachusetts
. London, England
2004 Massachusetts lnstitute ofT echno!ogy
1
DISEÑO DE CIRCUITOS
1 INTRODUCCIÓN
1.1 Acerca de VHDL.
VHDL está orientado a la síntesis de circuitos además de la simulación de circuitos. Sin embargo,
a pesar de que VHDL es completamente simulable, no todas las construcciones son
sintetizables. Nosotros haremos énfasis en las que sí lo son.
Una motivación fundamental para usar VHDL (o su competidor, Verilog) es que VHDL es un
lenguaje estandard, independiente de la tecnología/vendedor, y es por tanto portable y reusable.
Las dos aplicaciones principales e inmediatas del VHDL están en el campo de los Dispositivos
Lógicos Programables (incluyendo CPLDs- Complex Programmable Logic Devices y FPGAs-
Field Programmable Gate Array) y en el campo de los ASICs (Application Specific lntegrated
Circuits). Una vez que el código VHDL ha sido escrito, puede ser usado ya sea para implementar
el circuito en un dispositivo programable (Ya sea Altera, Xilinx, Atmel, etc.) o puede ser sometido
a fundición para fabricacion de un chip ASIC. Actualmente, muchos chips comerciales complejos
(microcontroladores, por ejemplo) son diseñados usando esta aproximación.
Una nota final en relación al VHDL es que, contrario a los programas regulares de computadoras
los cuales son secuenciales, sus sentencias son inherentemente concurrentes (en paralelo). Por
esta razón, VHDL es usualmente llamado código en vez de programa. En VHDL, solo las
sentencias colocadas dentro de un PROCESS, FUNCTION, o PROCEDURE se ejecutan
secuencialmente.
Como se mencionó anteriormente, uno de los principales servicios del VHDL es que permite la
síntesis de un circuito o sistema en un dispositivo programable (PLD o FPGA) o en un ASIC. Los
pasos que se siguen durante este proyecto se resumen en la figura 1.1. Empezamos el diseño al
escribir el código VHDL, el cual se guarda en un archivo con la extensión .vhd y el mismo nombre
del nombre de su ENTITY. El primer paso en el proceso de síntesis es la compilación. La
compilacion es la conversión del lenguaje VHDL de alto nivel, el cual describe el circuito al nivel
Register Transfer Level (RTL), en un netlist al nivel de gates. El Segundo paso es la
optimización, la cual se desarrolla en el netlist al nivel de gates para la optimización de la
velocidad 0del área. En esta etapa, el diseño puede ser simulado. Finalmente, un software
place-and-route (ajustador) generará la disposición física para un chip PLD/FPGA o generará la
máscara para un ASIC.
\'HDLcntry
lRTI~ lcvcl)
Compilill ion
Synthcsis l Optirnizatíon 1
Pbc"1&Route
Simulation
Figura 1.1
Resúmen del flujo de diseño VHDL.
Hay varias herramientas EDA (Electronic Design Automation) disponibles para la síntesis,
implementacion, y simulacion de circuitos usando VHDL. Algunas herramientas (place and route,
por ejemplo) se ofrecen como parte de la suite de diseño de un proveedor (e.g., Altera's Quartus
11, el cual permite la síntesis de código VHDL en chips CPLD/FPGA de Altera, o la suite de
Xilinix, para chips CPLD/FPGA de Xilinix). Otras herramientas (sintetizadores, por ejemplo),
además de ser ofrecidas como parte de las suites de diseño, pueden ser también provistas por
compañías especializadas EDA (Mentor Graphics, Synopsis, Synplicity, etc.). Ejemplos de este
último grupo son Leonardo Spectrum (un sintetizador de Mentor Graphics), Synplify (un
sintetizador de Synplicity), y ModelSim (un simulador de Model Technology, una compañía
Mentor Graphics).
En la figura 1.2 se representa un sumador completo. En el, a y b representan los bits de entrada
que se van a sumar, cines el bit del cargo de entrada, ses el bit de suma, y cout el bit del cargo
de salida. Como se muestra en la tabla de verdad, s debe estar en alto siempre que el número
de entradas que están en alto es impar, mientras que cout debe estar en alto cuando dos o más
entradas están en alto.
a-. s
ab cm
00 o
s coui
o o
b-+
Full ol o L o
Adder 1 o o l o
cout
cin _.,. J l o o 1
00 1 1 o
ol l o 1
1 Ü' 1 o 1
1 l l 1 )
Figura 1.2
Diagrama del sumador completo y la tabla de verdad.
En la figura 1.3 se muestra un código VHDL para el sumador completo de la figura 1.2. Como se
puede ver, consiste de una ENTITY, la cual es una descripcion de los pines (PORTS) del
circuito, y de una ARCHITECTURE, la cual describe cómo debe funcionar el circuito. Vemos en
ésta última que el bit suma se calcula comos= a+b+cin, mientras que cout se obtiene de cout =
a.b + a.cin + b.cin.
\,'·· Circuít
AF..CHITECTURE d~t:~fVY.1 CF :'u1 l
·\...
BRGW
~ ~- ~ XOR b XDR =i~; \.,
/
cout, •.- r~ J\liC b ] Of( j;:i l~lD •:::n1 ·~'~-
\
\, ,.•'
(b J'J{D ci::i) ;
...... .. . . """
Figura 1.3
Ejemplo de código VHDL para la unidad del sumador completo de la figura 1.2.
físico, como se indica en el =
A partir del código VHDL mostrado en el lado izquierdo de la figura 1.3, se infiere un circuito
derecho de la figura. Sin embargo, hay varias maneras de
implementar las ecuaciones descritas en la ARCHITECTURE de la figura 1.3, así que el circuito
real dependerá del compilador/optimizador que se esté usando y, más importante aun, de la
tecnología del dispositivo finalmente usado. En la figura 1.4 se presentan unos pocos ejemplos.
Por ejemplo, si nuestro dispositivo final es un dispositivo lógico programable (PLD o FPGA-
apéndice A), entonces se ilustran dos posibles resultados (entre muchos otros) para cout en las
figuras 1.4(b)-(c) (en ambas, por supuesto, cout = a.b + a.cin + b.cin). Por otra parte, si nuestra
tecnología final es un ASIC, entonces una posible implementación CMOS, a nivel de
transisitores, es el de la figura 1.4(d) (la cual hace uso de transistores MOS y lógica con reloj).
Por otra parte, la herramienta de síntesis puede ser ajustada para optimizar el área o la
velocidad, lo que obviamente también afecta el circuito final.
Cualquiera que sea el circuito i~ferido a partir del código, su operacion debe ser siempre
verificada todavía al nivel de diseño (después de la síntesis), como se indica en la figura 1.1. Por
supuesto debe ser también probada al nivel físico, pero los cambios en el diseño podrían ser
demasiado costosos
Cuando se pruebe, el simulador mostrará ondas smilares a las presentadasen la figura 1.5. En
efecto, la figura 1.5 contiene los resultados de la simulacion del circuito sintetizado con el código
VHDL de la figura 1.3, el cual implementa la unidad del sumador completo de la figura 1.2. Como
puede verse, los pines de entrada (caracterizadospor una flecha de entrada con una "I" marcada
dentro) y los pines de salida (caracterizados por una flecha de salida con una "o" marcada
dentro) son los enlistados en la ENTITY de la figura 1.3. Podemos libremente establecer los
valores de las señales de entrada (a, b, y cin en este caso), y el simulador calculará y graficará la
señales de salida (s y cout). Como puede ser observado en la figura 1.5, las salidas se
comportan como se esperaba
(at (b)
a--j a-j
b --j dn-j
L.-~___,...~
(e) (d)
Figura 1.4
Ejemplos de circuitos posibles obtenidos del código del sumador completo de la figura 1.3.
ffiÓrrs 11JJ.Ons 150.[t·,:; :))J.Dns '.L--.:.OJJns 3Jllln'3 ~$J.1Jn~ 4CO.On
•a o r 1
1 l
¡-¡__
--b
rcin o· 1
....@:s o 1 1 r~ _J
-O coul 1 1
Figura 1.5
Resultados de la simulación del diseño VHDL de la figura 1.3.
Por otra parte, algunos diseños adicionales y verificaciones experimentales también se proponen
como ejercicios:
• Adders and subtractors (problemas 3.5, 5.4, 5.5, 6.14, 6.16, 10.2, y 10.3)
• Arithmetic-logic units (problemas 6.13 y 10.1)
• Barrel and vector shifters (problemas 5. 7, 6.12, 9.1, y 12.2)
• Binary-to-Gray code converter (problema 5.6)
• Comparators (problemas 5.8 and 6.15)
• Count ones (problema 6.9)
• Counters (problemas 7.5 y 11.6)
• Data dela y circuit (problema 7 .2)
• Decoders (problemas 4.4 and 7.6)
• DFFs (problemas 6.17, 7.3, 7.4, y 7.7)
• Digital FIR filter (problema 12.4)
• Dividers (problemas 5.3 y 9.2)
• Event counter (problema 6.1)
• Finite-state machine (problema 8.1)
• Frequency divider, generic (problema 6.4)
. -~-
2 ESTRUCTURA DEL CÓDIGO
En este capítulo, se describen las secciones fundamentales que conforman una pieza de VHDL:
Declaraciones de LIBRERIAS, ENTIDAD Y ARQUITECTURA.
Como se muestra en la figura 2.1, una pieza independiente de código VHDL se compone al
menos de tres secciones fundamentales:
o Declaración LIBRARY: Contiene una lista de todas las librerías que se usan en el diseño.
Por ejemplo: ieee, std, work, etc.
o ENTITY: Especifica los pines 1/0 del circuito.
o ARCHITECTURE: Contiene el código apropiado, que describe cómo se debe comportar el
circuito.
Una librería es una colección de piezas de código usadas comúnmente. Al colocar tales piezas
en una librería les permite ser reusadas o compartidas por otros diseños.
La estructura típica de una librería se muestra en la figura 2.2. El código está generalmente
escrito en forma de FUNCTIONS, PROCEDURES, o COMPONENTS los cuales están puestos
en PACKAGES, y luego son compilados en la librería destinación.
Las unidades fundamentales de VHDL (Fig 2:1) se estudiarán en la Parte 1 del libro (hasta el
capítulo 9), mientras que las secciones relacionadas con las librerías (Fig 2:2) se verán en la
Parte 11 (capítulos 10-12).
Para declarar una librería (es decir, para hacerla visible en el diseño) se necesitan dos líneas de
código, una que contiene el name de la librería, y la otra una cláusula use,
LIBRARY library_name;
USE library _ name.package _ name.package _parts;
Al menos tres paquetes de tres librerías diferentes, son generalmente necesarias en un diseño:
Código Básico
ENTITY VHDL
ARCHITECTURE
Figura 2.1
Secciones fundamentales de un código Básico VHDL
UBRARY
PACKAGE
¡
p::=::.i
COMPONENTS
1
j
L_~-=~~ TYPE~~~~=:~--j-=__J
CON:ST ANTS Í
l
Figura 2.2
Partes fundamentales de una librería
LIBRAR Y work;
USE work.all;
1
Las librerías std y work mostradas arriba son visibles por default, así que no hay que declararlas;
sólo la librería ieee debe ser escrita explícitamente. 'sin embargo, esta última sólo es necesaria
cuando se emplean los datos de tipo STD_LOGIC (o STD_ULOGIC) en el diseño (los tipos de
datos se estudiarán en detalle en el siguiente capítulo).
El propósito de los tres paquetes/librerías mencionados arriba es el siguiente: El paquete
std_logic _ 1164 de la librería ieee especifica un sistema lógico multinivel; std es una biblioteca de
recursos (tipos de datos, texto i/o, etc) para el entorno de diseño VHDL; y la librería work es
donde guardamos nuestro diseño (el archivo .vhd, más todos los archivos creados por el
compilador, simulador, etc).
En efecto, la librería ieee contiene varios paquetes, incluyendo los siguientes:
• std_logic_l l64: Especifica los sistemas lógicos multivalor STD_LOGIC (de 8 niveles) y el
STD_ULOGIC (de 9 niveles).
• std_logic_arith: Especifica los tipos de datos SIGNED and UNSIGNED y las operaciones
de comparación y aritméticas relacionadas. También contiene varias funciones de
conversión ~e datos, las cuales permiten que un tipo sea convertido en otro:
conv _integer(p), conv _unsigned(p, b), conv_signed(p, b), conv_std_logic_vector(p, b).
• std_logic_signed:
Contiene funciones que permiten las operaciones con datos
STD_LOGIC_ VECTOR para desarrollarlas como si fueran de tipo SIGNED.
• std_logic _unsigned:
Contiene funciones que permiten operaciones con datos
STD_LOGIC_ VECTOR para desarrollarlas como si fueran de tipo UNSIGNED.
2.3 ENTITY
Una ENTITY es una lista con especificaciones de todos los pines de entrada y salida (PORTS)
del circuito. Su sintaxis se muestra abajo:
El mode de la señal puede ser IN, OUT, INOUT, o BUFFER. Como se muestra en la figura 2.3,
IN y OUT son pines totalmente unidireccionales, mientras que INOUT es bidireccional. BUFFER,
por otra parte, se emplea cuando la señal de salida debe ser usada (leída) internamente.
El type de la señal puede ser BIT, STO_LOGIC, INTEGER, etc. Los tipos de datos se discutirán
en detalle en el capítulo 3.
Finalmente, el name de la entidad puede ser básicamente cualquier nombre, excepto palabras
reservadas VHDL (las reservadas se enlistan en el apéndice E).
Ejemplo: Consideremos la función NANO de la figura 2.4. Su entidad puede especificarse como:
Figura 2.3
Modos de la Señal.
a
b
Figura 2.4
Función NANO.
El significado de esta entidad es el siguiente: El circuito tiene tres pines 110, siendo dos entradas
(a y b, modo IN) y una salida (x, modo OUT). Las tres señales son del tipo BIT. El nombre que
se escogió para la entidad fue nand_gate.
2.3 ARCHITECTURE
El significado de la arquitectura anterior es: El circuito debe desarrollar la operación NANO entre
las dos señales de entrada (a, b)y asignar ("<=") el resultado al pin de salida (x). El nombre
escogido para esta arquitectura fue myarch. En este ejemplo, no hay parte declarativa, y el
código contiene una sola asignación.
2.5 Ejemplosintroductorios
En esta sección presentaremos dos ejemplos de código VHDL. Aunque todavía no hemos
estudiado las construcciones que aparecen en los ejemplos, ayudarán a ilustrar los aspectos
fundamentales con respecto a la estructura general del código. Cada ejemplo es seguido por
comentarios explicativos y los resultados de la simulación.
G ·I,
1
Q1FF --GJ
~-·-
~J
Figura 2.5
OFF con Reset Asíncrono.
1
Ejemplo 2.1: OFF con Reset Asíncrono
¡ La figura 2.5 muestra el diagrama de un Flip Flap tipo O disparado con la transición positiva de la
señal de reloj (clk), Y con una entrada de reset asíncrona (rst). Cuando rst = 'l ', la salida debe
ponerse en nivel bajo independientemente de clk. Por otra parte, la salida debe copiar la entrada
1
1
(es decir, q <= d) en el momento en que clk cambia de 'O' a 'l' (es decir, cuando se produce
una evento hacia arriba en clk).
Hay varias maneras de implementar el OFF de la figura 2.5, uno es la solución presentada abajo.
Una cosa que recordar, sin embargo, es que VHDL es inherentemente concurrente (contrario a
los programas de computadora regulares, los cuales son secuenciales), así que para
implementar un circuito con reloj (flip flops, por ejemplo) debemos "forzar" que VHOL sea
secuencial. Esto puede hacerse usando un PROCESS, como se muestra enseguida.
1 ---------------------------------------
2 LIBRAR Y ieee;
3 USE ieee.std_logic_l 164.all;
4 ---------------------------------------
5 ENTITY dff IS
6 PORT ( d, clk, rst: IN STD_LOGIC;
7 q: OUT STD LOGIC);
8 END dff;
9 ---------------------------------------
10 ARCHITECTURE behavior OF dffIS
11 BEGIN
12 PROCESS (rst, clk)
13 BEGIN
14 IF (rst=' l ') TREN
15 q <='O';
16 ELSIF ( clk'EVENT AND clk=' l ') TREN
17 q <= d;
18 END IF;
19 END PROCESS;
20 END behavior;
21 ---------------------------------------
Comentarios:
Líneas 2-3: Declaración de la librería (name de la librería y cláusula use). Recuerde que
las otras dos librería indispensables (std y work) son hechas visibles por
default.
Líneas 5-8: Entidad dff
Líneas 10-20: Arquitectura behavior.
Línea 6: Puertos de entrada (el modo input sólo puede ser IN). En este ejemplo,
todas las señales de entradas son del tipo STO_LOGIC.
Línea 7: Puerto de salida (El modo output puede ser OUT, INOUT, o BUFFER).
Aquí la salida es también del tipo STO LOGIC. .
Líneas 11-19: Parte del código de la arquitectura (desde la palabra BEGIN en adelante).
Líneas 12-19: Un PROCESS (internamente el código se ejecuta secuencialmente).
Línea 12: El PROCESS se ejecuta cada vez que una señal declarada en su lista
de sensibilidad cambia. En éste ejemplo, cada vez que rst o clk cambian, el
PROCESS se ejecuta.
Líneas 14-15: Cada vez que rst es' l' la salida se limpia, sin importar clk (reset asíncrono).
Líneas16-17: Si rst no se activa, y clk cambia (ocurre un EVENT en clk), y además tal evento
fue una transición positiva (clk = 'l '), entonces la señal de entrada (d) se
almacena en el flip-flop (q <= d).
Líneas 15--17: El operador "<=" se usa para asignar un valor a una SIGNAL. En contraste,
":=" se usaría para una VARIABLE. Todos los puertos en una entidad son
señales por default.
Líineas 1, 4, 9, y 21:
Comentarios (recuerde que " " indica un comentario). Usados solo para
organizar mejor el diseño.
Figura 2.7
OFF con función lógica NANO
El circuito de la figura 2.4 fue puramente combinacional, mientras que el de la figura 2.5, fue
puramente secuencial. El circuito de la figura 2.7 es una mezcla de ambos (sin reset). En la
solución que sigue, hemos introducido a propósito una señal innecesaria (temp), sólo para
ilustrar cómo una señal debería declararse.
1 ---------------------------------------
2 ENTITY example IS
3 PORT (a, b, clk: IN BIT;
4 q: OUT BIT);
5 END example;
6 ---------------------------------------
? . ARCHITECTURE example OF example IS
8 SIGNAL temp : BIT;
9 BEGIN
1O temp <= a NAND b;
11 PROCESS (clk)
12 BEGIN
13 IF ( clk' EVENT AND clk=' l ') THEN q<=temp;
14 END IF;
15 END PROCESS;
16 END example;
17 ---------------------------------------
Comentarios:
Las declaraciones de las librerías no son necesarias en este caso, porque el dato es del tipo BIT,
el cual está especificado en la librería std (recuerde que las librerías std y work son visibles por
default).
l 2.6 Problemas
1
1 a l7:01 sel e
f c(7:0)
00 o
1 b {7:0J ¡..___,,, 01 a
10 b
11 z
1 Sel (1:0)
Figura P2.1
Nota: Se usó un IF en el código, porque es más intuitivo. Sin embargo, como se verá más tarde,
también se pueden usar sentencias como WHEN o CASE para implementar el multiplexor.
1 ---------------------------------------
2 LIBRARY ieee;
3 USE ~----------
4 ---------------------------------------
5 ENTITY mux IS
6 PORT ( _, _: _ STD_LOGIC_ VECTOR (7 DOWNTO O);
7 sel: IN _
8 _:OUT STD_LOGIC_ VECTOR (7 DOWNTO O));
9 END __
10 ---------------------------------------
11 ARCHITECTURE example OF IS
12 BEGIN
13 PROCESS (a, b, _)
14 BEGIN
15 IF (sel = "00") THEN
16 e<= "00000000";
17 ELSIF ( ) THEN
18 e<= a;
19 (sel= "10") THEN
20 e<=_,
21 ELSE
22 e<= (OTHERS => '_');
23 END
24 END ----
25END
--
26 ---------------------------------------------
Figure P2.2
Figura P 2.2
a) Escriba un código VHDL para el circuito de la figura P2.2. Observe que es puramente
combinacional, así que no es necesario un PROCESS. Escriba una expresión para d usando
sólo operadores lógicos (ANO, OR, NANO, NOT, etc.).
b) Sintetice y simule su circuito. Después de asegurarse de que trabaja apropiadamente, abra el
archivo del reporte y cheque la expresión real implementada por el compilador. Compárela
con su expresión.
3 TIPOS DE DATOS
Con el propósito de escribir códigos VHDL apropiadamente, es esencial conocer qué tipos de
datos son permitidos, y cómo se especifican Y se usan. En este capítulo, se describen todos los
tipos de datos fundamentales, con énfasis especial en los que son sintetizables. Se incluyen
discusiones sobre la compatibilidad y la conversión de datos.
VHDL contiene una serie de tipos de datos predefinidos, especificados a través de los
estándares IEEE 1076 e IEEE 1164. Más específicamente, tales definiciones de tipos de datos
pueden ser encontradas en los siguientes paquetes/librerías:
• Paquete standard de la librería std: Define los tipos de datos BIT, BOOLEAN, INTEGER, Y
REAL.
• Paquete std_logic_1164 de la librería ieee: Define los tipos de datos STD_LOGIC and
STD_ULOGIC.
• Paquete std_logic_arith de la librería ieee: Define los tipos de datos SIGNED Y
UNSIGNED, además incluye varias funciones para conversón de datos, como
conv _integer(p), conv _unsigned(p, b), conv _signed(p, b), y conv _std_logic_vector(p, b).
• Paquetes std_logic_signed y std_logic_unsigned de la librería ieee: Contiene funciones
que permiten operaciones con datos STD_LOGIC_VECTOR para ser desarrolladas como si
los datos fueran de tipo SIGNED o UNSIGNED, respectivamente.
Todos los tipos de datos predefinidos (especificados en los paquetes/librerías enlistados arriba,
se describen a continuación.
Examples:
SIGNAL x: BIT;
-- x está declarada como una señal de un dígito de tipo BIT.
SIGNAL y: BIT_ VECTOR (3 DOWNTO O);
__ y es un vector de 4 bits, con el bit más a la izquierda como el bit MSB.
SIGNAL w: BIT_ VECTOR (O TO 7);
__ w es vector de 8 bits, con el bit más a la derecha como el bit MSB.
Basados en las señales anteriores, las siguientes asignaciones serían legales (para asignar un
valor a una señal, se usa el operador"<=''):
X<='}';
¡
. ¡
-- x es una señal de un solo bit (como ya se especificó), cuyo valor es
-- 'l '.Observe que se usan apóstrofes('') para un solo bit.
y<="Olll";
-- y es una señal de 4 bits (como ya se especificó), cuyo valor es"Ol 11"
-- (MSB='O'). Observe que se usan comillas (" ") para vectores.
Ejemplos:
Table 3.1
Sistema Lógico de resolución (STD_LOGIC).
X o 1 z w 1 H -
X X X X X X X X X
o X o X o o o o X
1 X X 1 1 1 1 1 X
z X o 1 z w L H X
w X o 1 w w w w X
L X o 1 L w L w X
H X o 1 H w w H X
- X X X X X X X X
En efecto, el sistema STD_LOGIC descrito arriba es un subtipo del STD_ULOGIC. Este último
incluye un valor lógico extra, 'U', el cual se aplica a los casos no resueltos. Así que, al contrario
del STD_LOGIC, los conflictos de niveles lógicos no se resuelven automáticamente aquí, así que
los alambres nunca deben ser conectados directamente entre sí. Sin embargo, si se supone que
dos alambres de salida nunca se deben conectar juntos, este sistema lógico puede ser usado
para detectar errores de diseño.
Ejemplos:
SIGNAL a: BIT;
SIGNAL b: BIT_ VECTOR (7 DOWNTO O);
SIGNAL e: STD _LOGIC;
SIGNAL d: STD _LOGIC_ VECTOR (7 DOWNTO O);
SIGNAL e: INTEGER RANGE O TO 255;
VHDL también permite que el usuario defina sus propios tipos de datos. Enseguida mostramos
dos tipos de datos definidos por el usuario: integer y enumerated.
3.3 Subtypes
Un SUBTYPE es un TYPE con una restricción. La principal razón para usar un subtipo en vez de
especificar un nuevo tipo es que, aunque las operaciones entre datos de diferentes tipos no son
permitidos. si son permitidos entre un subtipo y su correspondiente tipo base.
Ejemplo: Los subtipos siguientes fueron derivados de los tipos presentados en los previos
ejemplos.
Los arreglos son colecciones de objetos del mismo tipo. Pueden ser de una dimensión (1 O), de
dos dimensiones (20), o de una dimensión por una dimensión (1 Dx1 O). Pueden ser también de
dimensiones más grandes, pero generalmente no son sintetizables.
La figura 3:1 ilustra la construcción de arreglos de datos. Un valor simple (escalar) se muestra en
(a), un vector (array 1 O) en (b), un arreglo de vectores (array 1 Dx1 D) en (e), y un arreglo de
escalares (array 20) en (d).
En efecto, tos tipos de datos predefinidos (vistos en la sección 3.1) incluyen solamente las
categorías de escalares (un solo bit) y de vectores (arrays de bits de una sola dimensión). Los
tipos predefinidos sintetizables en cada una de estas categorías son los siguientes:
Figure 3.1 Ilustración de (a) un escalar, (b) 1 o, (e) 1 Ox1 o, y (d) arreglos de datos 20.
Como se puede ver, no hay datos predefinidos para arreglos 20 o 1 Dx1 D, los cuales, cuando
sea necesario, deben ser especificados por el usuario. Para ello, el nuevo TYPE debe ser
primero definido, luego se puede declarar la nueva SIGNAL, VARIABLE o CONSTANT usando
ese tipo de dato. Se debe usar la siguiente sintaxis.
En esta sintaxis, se declaró una SIGNAL. Sin embargo, también podría ser una CONSTANT o
una VARIABLE. Observe que el valor inicial es opcional (solo para simulación)
Suponga que queremos construir un arreglo que contenga cuatro vectores, cada uno de tamaño
de ocho bits.
Esto es un arreglo 1 Dx1 D (vea la figura 3.1 ). Llamemos a cada vector row, y al arreglo completo
matrix. Adicionalmente, supongamos que queremos que el bit de más a la izquierda de cada
vector sea su MSB (most significant bit), y que queremos que la fila superior sea la fila O.
Entonces la implementación del arreglo sería la siguiente (observe que una SIGNAL, llamada x,
de tipo matrix, se declaró como ejemplo):
Desde el punto de vista de compatibilidad, este último podría ser más ventajoso que el del
ejemplo anterior (vea ejemplo 3.1 ).
Como se muestra en la sintaxis anterior, el valor inicial de una SIGNAL o una VARIABLE es
opcional. Sin embargo, cuando se requiere la inicialización, puede hacerse como en los ejemplos
siguientes.
SIGNAL x: row;
SIGNAL y: array l ;
SIGNAL v: array2;
SIGNAL w: array3;
·--------- ---·--··---
---------------------------
ENTITY mux IS
PORT (inp: IN VECTOR_ARRAY (O TO 3);
... );
END mux;
... '
--------------------------------------------
Como puede verse en el ejemplo anterior, se creó un tipo de dato definido por el usuario, llamado
vector _array, el cual puede contener un número indefinido de vectores de tamaño de ocho bits
cada uno (NATURAL RANGE < > significa que el rango no es fijo, con la única restricción de
que debe estar en el rango NATURAL, el cual va de O to +2, 147,483,647). El tipo de dato se
guardó en un PACKAGE llamado my_data_types, y luego se usó en una ENTITY para
especificar un PORT llamado inp. Observe en el código principal la inclusión de una cláusula
USE adicional para hacer el paquete definido por el usuario my_data_ types visible a el diseño.
Otra opción para el PACKAGE anterior sería el que se muestra enseguida, donde se incluyó una
declaración de una CONSTANT (en el capítulo 1 o se presentará un estudio detallado de
PACKAGES).
PACKAGE my_data_types IS
CONSTANT b: INTEGER := 7;
TYPE vector_array IS ARRA Y (NATURAL RANGE <>) OF
STD _LOGIC _VECTOR(b DOWNTO O);
END my _data_ types;
-------------------------------------------------
3.6 Records
Los Records son similares a los arreglos, con la única diferencia de que ellos contienen objetos
de diferentes tipos.
Ejemplo:
Ejemplo:
SIGNAL x: SIGNED (7 DOWNTO O);
SIGNAL y: UNSIGNED (O TO 3);
Para usar los tipos de datos SIGNED o UNSIGNED, se debe declarar el paquete std_logic_arith,
de la librería ieee, A pesar de su sintaxis, los tipos de datos SIGNED y UNSIGNED están
destinados principalmente para operaciones aritméticas, es decir, contrario a
STD_LOGIC_VECTOR, aceptan operaciones aritméticas. Por otra parte, no se permiten las
operaciones lógicas. Con respecto a las operaciones relacionales (comparación), no hay
restricciones.
LIBRARY ieee;
USE ieee.std_logic_l 164.all;
USE ieee.std _logic _ arith.all; -- paquete extra necesario.
LIBRAR Y iece;
USE ieee.std - logic - 1164.all; -- no se require paquete extra
<=a+ b;
V __ ilegal (operación aritmética no OK)
w <= aAND b; -- legal (operación lógica OK)
A pesar de las restricciones mencionadas arriba, hay una manera simple de permitir que los
datos de tipo STD_LOGIC_ VECTOR participen directamente en operaciones aritméticas. Para
eso, la librería ieee provee dos paquetes, std_logic_signed y std_logic_unsigned, los cuales
permiten operaciones con datos STD_LOGIC_ VECTOR para desarrollarlas como si fueran datos
de tipo SIGNED o UNSIGNED, respectivamente.
LIBRAR Y ieee;
USE ieee.std _logic _ 1164.all;
USE ieee.std _ logic _ unsigned.all; -- paquete extra incluí do
VHDL no permite operaciones directas (arithmetic, logical, etc.) entre datos de diferentes tipos.
Por tanto, a menudo es necesario convertir datos de un tipo a otro.
Esto puede hacerse de dos maneras básicas: podemos escribir una pieza de código VHDL para
eso, o invocamos una FUNCTION de un PACKAGE predefinido el cual es capaz de hacerlo por
nosotros.
Si los datos están muy relacionados (es decir, ambos operandos tienen el mismo tipo base, a
pesar de ser declarados como pertenecientes a dos tipos diferentes), entonces el std _logic _ 1164
de la librería ieee provee funciones de conversión directas (straightforward). En seguida se
muestra un ejemplo.
LIBRAR Y ieee;
USE ieee.std_logic_l 164.all;
USE ieee.std _logic _arith.all;
Las asignaciones legales e ilegales presentadas enseguida están basadas en las siguientes
definiciones de tipos y declaraciones de señales:
Tabla 3~2
FOR i IN O TO 3 LOOP
FORj IN 7 DOWNTO O LOOP
X G) <='O';
y G) <='O'
z G) <='O';
wl (i,j) <='O';
w2 (i) G) <='O';
w3 (i) G) <='O';
ENDLOOP;
ENDLOOP;
---------------------------------------------------------
Este ejemplo ilustra la diferencia entre la asignación de un solo bit y la asignación de un vector
de bits (es decir, BIT vs BIT_ VECTOR, STO_LOGIC vs STO_
LOGIC_VECTOR, o STD_ULOGIC vs STD_ULOGIC_VECTOR).
A continuación se presentan dos códigos VHDL. Ambos desarrollan la operación ANO entre las
señales de entrada y asigna el resultado a la señal de salida. La única diferencia entre ellos es el
número de bits en los puertos de entrada y de salida (un bit en el primero, cuatro bits en el
segundo). Los circuitos inferidos a partir de estos códigos se muestran en la figura 3.2.
a(O)jJ- x(O}
b(O)
a(l)jJ- x(I)
h(I) .
ª(-.'))
b(2)
=fL__ ../
J
.'>
X(-)
.
a(3)jJ r- x(3)
b(J) ___/
Figura 3.2
Circuitos inferidos del código de los códigos del ejemplo 3.2
La figura 3.3 muestra el diagrama top-level de un sumador de 4-bits. El circuito tiene dos
entradas (a, b) y una salida (sum). Se presentan dos soluciones. En la primera, todas las
señales son de tipo SIGNED, mientras que en la segunda la salida es de tipo INTEGER. Note en
la solución 2 que se usó una función de conversión en la línea 13, ya que el tipo de a+b no es
igual al de sum. Observe también la inclusión del paquete std_logic _ aritli (línea 4 de cada
solución), el cual especifica los datos de tipo SIGNED. Recuerde que un valor SIGNED se
presenta como un vector; es decir, similar a STD_LOGIC_VECTOR, no como un entero.
a{3:0)G
b (3:0) -
+ _ surn (4:0)
Figura 3.3
Sumador de 4-bits del ejemplo 3.3
s
o T=c=C~J:---Cl-D 4 X a }i e X o K
~-5Urn __ QQ J:._ _:!ª-___ K.__J.§._~U! __:LJ~ L __g_9 _
K_J);___l._ ~~~--_J,__Q'?_ __
Figura 3.4
Resultados de simulación del ejemplo 3:3
Los resultados de la simulación (para cada solución) se presentan en la figura 3.4. Observe que
los números se representan en forma hexadecimal y complemento a 2. Ya que el rango de
entrada es desde -8 a 7, su representación es 7 -7 7, 6 -7 6, ... , o -7 O, -1 -7 15, -2 -7 14, ... , -8 -7 &.
De la misma forma, el rango de salida es de -16 a 15, así que su representación es 15 -7 15, ... , O
r
7 O, -1731, ... ,-16-716. Por tanto, 2H + 4H = 06H (es decir, 2 + 4 = 6), 4H + BH = 1CH (es
decir, 4 + (-8) = -4), etc., donde H = Hexadecimal.
3.11 Problemas
Los problemas de abajo están basados en las siguientes definiciones TYPE y declaración de
señal SIGNAL:
TYPE arrayl IS ARRA Y (7 DOWNTO O) OF STD_LOGIC;
TYPE array2 IS ARRA Y (3 DOWNTO O, 7 DOWNTO O) OF STD LOGIC·
TYPE array3 IS ARRA Y (3 DOWNTO O) OF arrayl; - '
SIGNAL a: BIT;
SIGNAL b: STD_LOGIC;:
SIGNAL x : arrayl ; .
SIGNAL y: array2;
SIGNAL w: array3;
SIGNAL z: STD_LOGIC_VECTOR (7 DOWNTO O);
Problema 3.1
Determine la dimensionalidad (escalar, 1 O, 20, ar 1 Dx1 O) de las señales dadas. también, escriba
un ejemplo numérico para cada señal. .
Problema 3.2
Determine cuáles de las asignaciones de la tabla P3.2 son legales y cuáles son ilegales.
Brevemente justifique sus respuestas. También, determine la dimensionalidad de cada
asignación (en ambos lados).
a <= x(2);
b <= x(2);
b <= y(3,5);
b <= w(5)(3);
y( 1 )(O) <= z(7);
x(O) <= y(O,O);
X <= "1110000";
a <= "0000000";
y(l) <= x;
w(O) <=y;
w(l) <= (7=>'1 ', OTHERS=>'O');
y(l) <= (O=>'O', OTHERS=>'l');
w(2)(7 DOWNTO O) <= x;
w(0)(7 DOWNTO 6) <= z(5 DOWNTO 4);
x(3) <= x(5 DOWNTO 5);
b <= x(5 DOWNTO 5);
y <= ((OTHERS=>'O'), (OTHERS=>'O'),
(OTHERS=>'O'), "1000000 l ");
z(6) <= x(5);
z(6 DOWNTO 4) <= x(5 DOWNTO 3);
z(6 DOWNTO 4) <= y(5 DOWNTO 3);
y(6 DOWNTO 4) <= z(3 TO 5);
y(O, 7 DOWNTO O) <= z;
w(2,2) <= 'l';
4 OPERADORES Y ATRIBUTOS
El propósito de este capítulo, junto con los capítulos anteriores, es sentar las bases del VHDL, de
tal forma que en el siguiente capítulo podamos empezar a trabajar con el diseño de circuitos
reales. En efecto, es imposible -o poco productivo al menos- escribir cualquier código
eficientemente sin esforzarse por entender los tipos de datos, los operadores y los atributos
también.
Los operadores y los atributos constituyen una lista relativamente larga de construcciones
generales VHDL, que se examinan a menudo escasamente. Los hemos puesto juntos en un
capítulo específico con el propósito de proveer un estudio más completo y consistente.
Al final del capítulo, se presentan unos pocos diseños. Sin embargo, debido al hecho de que este
es un capítulo fundamental, los ejemplos son meramente ilustrativos, como los de los capítulos
anteriores. Como se mencionó antes, empezaremos trabajando con circuitos reales en el capítulo
5.
4.1 Operadores
• Operadores de Asignación
• Operadores Lógicos
• Operadores Aritméticos
• Operadores Relacionales
• Operadores de Desplazamiento
• Operadores de Concatención
Operadores de Asignación
Se usan para asignar valores a las señales, variables y constantes. Ellos son:
Operadores Lógicos
Se usan para desarrollar operaciones lógicas. Los datos deben ser del tipo BIT, STD_LOGIC, o
STD_ULOGIC (u, obviamente, sus respectivas extensiones, BIT_VECTOR,
STD_LOGIC_VECTOR, o STD_ULOGIC_VECTOR).
• NOT
•ANO
• OR
•NANO
• NOR
• XOR
• XNOR
Observe que el operador NOT tiene prioridad sobre los otros. El operador XNOR fue introducido
en VHDL93.
Ejemplo:
Operadores Aritméticos
Se usan para desarrollar operaciones aritméticas. Los datos pueden ser del tipo INTEGER,
SIGNED-, UNSIGNED, o REAL (recuerde que estos últimos no pueden ser sintetizados
directamente). También, si se usa el paquete std_logic _signed o el paquete std _logic _unsigned
de la librería ieee, entonces pueden usarse STD_LOGIC_ VECTOR directamente en operaciones
de adición y substracción (como se vio en la sección 3.6).
+ Adición
Substracción
. Multiplicación
I División
** Exponenciación
MOD Módulo (Modulus)
REM Residuo (Remainder)
ABS Valor Absoluto
Operadores de Comparación
Se usan para hacer comparaciones. Los datos pueden ser cualquiera de los ya mencionados.
Los operadores relacionales (comparación) son:
= Equal to
/= Not equal to
< Less than
> Greater than
<= Less than or equal to
>= Greater than or equal to
Operadores de desplazamiento
se usan para desplazamiento de datos. Fueron introducidos en VHDL93. Su sintaxis es como
sigue: <left operand> <shift operation> <right operand>. El operando de la izquierda debe ser
del tipo BIT_ VECTOR, mientras que el operando de la derecha debe ser INTEGER (+ o - frente
a él se aceptan). Los operadores de desplazamiento son:
• sll Shift left logic - las posiciones a la derecha se llenan con 'O's
• srl Shift right logic - las posiciones a !a izquierda se llenan con 'O's
• sla Shift left arithmetic -- El bit de más a la derecha se replica a la derecha
• sra Shift right aritmetic - El bit de más a la izquierda se replica a la izquierda
• rol Rotate left -- Rotación hacia la izquierda
• ror Rotate right -- Rotación hacia la derecha
Y<= X sfl 2; __ Desplazamiento lógico hacia la izquierda por 2: y<= "00100!11 ° l oc<r>
Y<= x sla 2; Desplazamiento aritmético hacia la izquierda por 2: y ~~ "0"0111"
Y<= x srl 3; Desplazamiento lógico hacia la derecha por 3: y<= "00001"
Y<= x sra 3; Desplazamiento aritmético hacia la derecha por 3: y <= "00001"
Y<= x rol 2; Rotación lógica hacia la izquierda por 2: Y<= "00101"
Y <= x srl -2; -- Igual que sll 2
Operadores de concatenación
&
(, ' ' )
Ejemplos
4.2 Atributos
El propósito de los atributos es dar al VHDL más flexibilidad, y también permitir la construcción de
piezas de código genéricas (código que trabajará para cualquier tamaño de vectores y arrays,
por ejemplo. Tales atributos se dividen en dos grupos:
Atributos de datos: Regresan información (un valor) con respecto a un vector de datos;
Atributos de señales: Sirven para monitorear una señal (regresa TRUE o FALSE)
En cualquiera de los casos, para usar un atributo se debe emplear"" (apóstrofes) Además de una
lista de atributos predefinidos, VHDL también permite atributos definidos por el usuario. Los
primeros se presentarán en esta sección, mientras que los últimos se discutirán en la sección 4.3.
Entonces:
Entonces los cuatro estatutos LOOP que siguen son sintetizables y equivalentes:
Atributos de Señales
Vimos anteriormente atributos del tipo HIGH, RANGE, EVENT, etc. Estos están predefinidos en
el VHDL87. Sin embargo, VHDL también permite la construcción de atributos definidos por el
usuario. Para emplear un atributo definido por el usuario, debe ser declarado y especificado. La
sintaxis es la siguiente:
Declaración de atributo:
donde:
attribute_type: cualquier tipo de dato (BIT, INTEGER, STD_LOGIC_ VECTOR, etc.)
clase: TYPE, SIGNAL, FUNCTION, etc.
va l or.· 'O' , 27 , "00 11 10 01" , etc.
Ejemplo:
Sus estados se codificarán como red = "00", green = "O l ", blue = "10", y white = "11 ".
Enum _ encoding permite que la codificación por default (secuencial) sea cambiada. Por tanto el
siguiente esquema de codificación podría ser empleado, por ejemplo:
Un atributo definido por el usuario puede ser declarado en cualquier parte, excepto en un
PACKAGE BODY. Cuando no se reconoce por una herramienta de síntesis, simplemente es
ignorado, o se emite una advertencia.
Acabamos de ver que los atributos pueden ser definidos por el usuario. Lo mismo es cierto para
los operadores. Por ejemplo, consideremos los operadores aritméticos predefinidos vistos en la
sección 4.1 (+, -, *,/,etc.). Estos especifican las operaciones aritméticas eritre datos de ciertos
tipos (INTEGER, por ejemplo). Por ejemplo, el operador predefinido "+" no permite la suma
entre datos del tipo BIT.
Nosotros podemos definir nuestros propios operadores, usando el mismo nombre que los
predefinidos. Por ejemplo, podríamos usar ''+'' para indicar una nueva clase de suma, esta vez
entre los valores del tipo BIT_VECTOR. Esta técnica se llama sobrecarga del operador.
Ejemplo: Considere que queremos sumar un entero con un número binario de 1 bit. Entonces
podríamos usar la siguiente FUNCTION (los detalles sobre cómo construir y usar una
FUNCTION se verá en el capítulo 11 ):
--------------------------------------
FUNCTION "+" (a: INTEGER, b: BIT) RETURN INTEGER IS
BEGIN
IF (b=' l ') THEN RETURN a+ 1 ;
ELSE RETURN a;
END IF;
END"+";
--------------------------------------
Un llamado a la función podría ser el siguiente:
4.5 GENERIC
Como su nombre lo sugiere, GENERIC es una manera de especificar un parámetro genérico (es
decir, un parámetro estático que puede ser fácilmente modificado y adaptado a diferentes
aplicaciones). El propósito es conferir al código más flexibilidad and reutilización.
Una sentencia GENERIC, cuando se emplea, debe ser declarada en la ENTITY. El parámetro
especificado será entonces verdaderamente global (es decir, visible al diseño total, incluyendo la
ENTITY misma). Su sintaxis es como sigue.
ENTITY my _ entity IS
GENERIC (n: INTEGER := 8);
PORT ( ... );
END my _ entity;
ARCHITECTURE my _architecture OF my_ entity IS
END mv architecture:
.;_
Mostramos ahora unos pocos ejemplos de diseño completos, con el propósito de ilustrar el uso
de los operadores, atributos y GENERIC. Recuerde, sin embargo, que hasta aquí hemos
trabajado en establecer los fundamentos del VHDL, con la discusión formal de las técnicas de
codificación empezando en el siguiente capítulo (capítulo 5). Por lo tanto, el estudiante novato
en VHDL no se debe desanimar si los ejemplos le parecen poco familiares. En vez de eso, eche
una mirada a los siguientes ejemplos, y luego, después de estudiar los capítulos del 5 al 7,
regrese y vuélvalos a examinar.
Figure 4.1
Decodificador del ejemplo 4.1.
Observe en el código siguiente el uso de los operadores "+" (línea 22), "*" (líneas 22 y 24),
":=" (líneas 17, 18, 22, 24, y 27), "<=" (línea 29), and "=>" (línea 17). Observe también el
uso de los siguientes atributos: HIGH (lineas 14-15) y RANGE (línea 20).
1 ---------------------------------------------
2 LIBRARY ieee;
3 USE ieee.std_logic_l 164.all;
4 ---------------------------------------------
5 ENTITY decoder IS
6 PORT ( ena : IN STD_LOGIC;
7 sel: IN STD_LOGIC_ VECTOR (2 DOWNTO O);
8 x: OUT STD_LOGIC_ VECTOR (7 DOWNTO O));
9 END decoder;
10 ---------------------------------------------
11 ARCHITECTURE generic _ decoder OF decoder IS
12 BEGIN
13 PROCESS ( ena, sel)
14 VARIABLE templ: STD_LOGIC_ VECTOR (x'HIGH DOWNTO O);
15 VARIABLE temp2 : INTEGER RANGE O TO x'HIGH;
16 BEGIN
17 templ := (OTHERS => '1');
18 temp2 :=O;
19 IF ( ena=' 1 ') THEN
20 FOR i IN sel'RANGE LOOP -- sel range is 2 downto O
21 IF (sel(i)='l ') THEN -- Bin-to-Integer conversion
22 temp2:=2*temp2+ 1;
23 ELSE
24 temp2 := 2*temp2;
25 END IF;
26 ENDLOOP;
27 templ(temp2):='0';
28 END IF;
29 x <= templ;
30 END PROCESS;
31 END generic _ decoder;
32 ---------------------------------------------
í rn Dns 3Cl[UJns
,_ .4Cü On s 5'JO On!
~,~;il
DIJ ·-· ---··¡i·----
·--·· ·-).~- -·----· ~J· ·-· · ü· ·-· · · --~:c
---··:T-·-· ··---x· ·· . ,:.: -· 2 . ' :(··-"'·"·~~- ··-. ·-}(. 11 > ·;s- --- ·~r-~ -5·- ·-~l--·-7· . --)~
Figure 4.2
Resultado de la Simulación del ejemplo 4.1.
La funcionalidad del decodificador anterior se puede verificar en los resultados de la simulación
de la figure 4.2. Como puede verse, todas las salidas están en nivel alto, es decir, x =
'' 11111111 '' (decimal 255), cuando ena = 'O'· Después que ena ha sido puesta en 'l', solo un
bit de salida (el seleccionado por sel) se pone en nivel bajo. Por ejemplo, cuando sel = "000"
(decimal O), x = "1111111 O" (decimal 254); cuando sel = "00 l" (decimal 1 ), x = "11111101"
(decimal 253); cuando sel = "O 10" (decimal 2), x = "11111O11" (decimal 251 ); etcétera.
La figura 4.3 muestra el diagrama top-level de un detector de paridad. El circuito debe proveer
una salida = 'O' cuando el número de '1 'sen el vector de entrada es par, o una salida = '1' en otro
caso. Observe en el código VHDL siguiente que la ENTITY contiene una sentencia GENERIC
(línea 3), la cual define n como 7. Este código trabajará para cualquier otro tamaño de vector,
siendo sólo necesario cambiar el valor den en esa línea. Se le invita a destacar los operadores Y
atributos que aparecen en este diseño.
Ejemplo
11101110 rango= 7 - O
O xor 1 xor 1 xor 1 xor O xor 1 xor 1 xor 1 xor O= O hay 6 1 's (par)
11101100 rango 7 - O
O xor 1 xor 1 xor 1 xor O xor 1 xor 1 xor O xor O= 1 hay 5 1 's (impar)
PARITY
input (n:O) DETECTOR
output
Figura 4.3
Detector de paridad Genérico del ejemplo 4.2.
Figura 4.4
Resultado de la Simulación del ejemplo 4.2.
--------------------------------------------
2 ENTITY parity_det IS
3 GENERIC (n : INTEGER := 7);
4 PORT (input: IN BIT_ VECTOR (n DOWNTO O);
5 output: OUT BIT);
6 END parity _ det;
7 --------------------------------------------
8 ARCHITECTURE parity OF parity _ det IS
9 BEGIN
10 PROCESS (input)
11 VARIABLE temp: BIT;
12 BEGIN
13 temp :='O';
14 FOR i IN input'RANGE LOOP
15 temp := temp XOR input(i);
16 ENDLOOP;
17 output <= temp;
18 END PROCESS;
19 END parity;
20 --------------------------------------------
La simulación que resulta del circuito sintetizado con el código mostrado arriba se muestra en la
figura 4.4. Observe que cuando input = "00000000" (decimal O), la salida es 'O', porque el
número de '1 's es par; Cuando input = "00000001" (decimal 1 ), la salida es 'l ', porque el
número de '1 'ses impar; y etc.
El circuito de la figura 4.5 debe sumar un bit al vector de entrada (a su izquierda). Tal bit debe ser
un 'O' si el número de '1 's en el vector de entrada es par, o un 'l' si es impar, tal que el vector
resultante siempre contendrá un número par de 'l 's (paridad par). Un código VHDL para el
generador de paridad se muestra enseguida. Una vez más se le invita, a destacar los
operadores y atributos usados en el diseño.
Figure 4.5
Resultado de la Simulación del ejemplo 4.3.
1 -----------------------------------------------
2 ENTITY parity _gen 1 S
3 GENERIC (n : INTEGER := 7);
4 PORT (input: IN BIT_ VECTOR (n-1 DOWNTO O);
5 output: OUT BIT_VECTOR (n DOWNTO O));
6 END parity _gen;
7 -----------------------------------------------
8 ARCHITECTURE parity OF parity _gen IS
9 BEGIN
10 PROCESS (input)
11 VARIABLE templ: BIT;
12 VARIABLE temp2: BIT_ VECTOR (output'RANGE);
13 BE GIN
14 templ :='O';
15 FOR i IN input'RANGE LOOP
16 temp 1 := temp 1 XOR input(i);
17 temp2(i) := input(i);
18 END LOOP;
19 temp2( output'HIGH) := temp 1;
20 output <= temp2;
21 END PROCESS;
22 END parity;
23 -----------------------------------------------
La simulación que resulta se muestra en la figure 4.6. Como se puede ver, cuando input =
"0000000" (decimal O, con siete bits), output = "00000000" (decimal O, con ocho bits);
cuando input= "0000001" (decimal 1, con siete bits), output = "10000001" (decimal 129, con
ocho bits); y así sucesivamente.
g;: input
~ output
Figura 4.6
Resultado de la simulación del ejemplo 4.3.
4.7 Resumen
En las tablas 4.1 y 4.2 se presenta un resumen de operadores y atributos VHDL,
respectivamente. Las partes que no son sintetizables (o que tienen poco soporte de síntesis) se
marcan con el simbolo ">'.
Tabla 4.1
Operadores.
Tipo de operador Operadores Tipos de <Datos
Tabla 4.2
Attributes.
c'LOW ->
d'HIGH ->
c'LEFT ->
d'RIGHT ->
c'RANGE ->
d'LENGTH ->
c'REVERSE _RANGE ->
verifique si cada una de las siguientes operaciones es legal o ilegal. Brevemente justifique sus
respuestas
b(O) AND a
a+ d(7)
NOTbXNORc
c+d
e-f
IF (b<c)
IF (b>=a) .
IF (f/=e) .
IF (e>d)
b sra 1
e srl -2
fror 3
e*3
5**5
f/4
e/3
d<=c
d(6 DOWNTO 3) := b
e<=d
f := 100
Las siguientes preguntas están relacionadas con el circuito decodificador diseñado en el ejemplo
4.1
(a) Con el propósito de que ese diseño opere con otro tamaño de vector, se debe cambiar dos
valores: el rango de sel (línea 7) y el rango de x (line 8). Ahora queremos transformar ese diseño
en uno verdaderamente genérico. Con éste propósito, introduzcamos una sentencia GENERIC
en la ENTITY, especificando el número de bits de sel (digamos, n = 3), luego reemplace los
límites superiores de los rangos de sel y de x por un atributo el cual es una función den.
Problema 4.5
Enliste todos los operadores, atributos y genéricos empleados en los ejemplos 4.2 y 4.3.
5 CÓDIGO CONCURRENTE
Habiendo terminado de exponer los fundamentos del VHDL (capítulos 1 to 4), podemos ahora
concentrarnos en el diseño mismo(código).
El código VHDL puede ser concurrente (paralelo) o secuencial. Estudiaremos el primero en este
capítulo, mientras que el último se estudiará en el capítulo 6. Esta división es muy importante, ya
que permite un mejo entendimiento del propósito de cada estatuto para cada clase de código,
además de las consecuencias de usar uno o el otro.
Los estatutos concurrentes en VHDL son WHEN y GENERATE. Además de ellos, las
Asignaciones usando solo operadores (ANO, NOT, +, *, sll, etc.) pueden ser usados también para
construir código concurrente. Finalmente, una clase especial de asignación, llamado BLOCK,
puede ser empleada en esta clase de código.
Por definición, lógica combinaconal es aquella en la que la salida del circuito depende solamente
de las entradas presentes (figura 5.1 (a)). Es claro entonces que, en principio, el sistema no
requiere memoria y puede ser implementado usando funciones lógicas convencionales.
En contraste, la lógica secuencial está definida como aquella en la que la salida sí depende de
entradas previas (figure 5.1 (b)). Por lo tanto, se requieren elementos de almacenamiento, los
cuales están conectados al block de lógica combinacional por medio de un lazo de
retroalimentación, tal que ahora los estados almacenados (creados por entradas previas)
también afectarán la salida del circuito.
Un error común es pensar que cualquier circuito que posee elementos de almacenamiento (flip-
flops) es secuencial. Una RAM (Random Access Memory) es un ejemplo. Una RAM puede ser
modelada como en la figura 5.2. Observe que los elementos de almacenamiento aparecen en
forward path y no en un feedback de retroalimentación. Las operaciones de lectura dependen
solamente de el vector de dirección actual aplicado a la entrada de la RAM, y .el valor obtenido
de la lectura no tiene nada que ver con los accesos de memoria previos.
present next
state Storage state
Elements
(a) {b)
Figure 5.1
Lógica (a) Combinacional versus (b) Lógica secuencial.
Storage
Elerncnts
Figure 5.2
Modelo RAM
Por ejemplo, consideremos un código con tres estatutos concurrentes (stat1, stat2, stat3).
Entonces, cualquiera de las siguientes alternativas dará el mismo circuito:
Es entonces claro que, ya que el orden no importa, el código puramente concurrente no puede
ser usado para implementar circuitos síncronos (la única excepción es cuando se usa un
GUARDED BLOCK). En otras palabras, en general podemos construir sólo circuitos lógicos
combinacionales con el código concurrente. Para obtener circuitos lógicos secuenciales, se debe
usar código secuencial (capítulo 6). En efecto, con estos últimos podemos implementar ambos,
circuitos secuenciales así como circuitos combinacionales.
En este capítulo, discutiremos código concurrente, es decir, estudiaremos las sentencias que
sólo pueden ser usadas fuera de PROCESSES, FUNCTIONS, o PROCEDURES. Ellas son la
sentencia WHEN y la sentencia GENERATE. Además de ellas, las asignaciones que usan solo
operadores (lógicos, aritméticos, etc) pueden obviamente también ser usadas para crear circuitos
combinacionales. Finalmente, una clase especial de sentencia¡ llamada BLOCK, puede también
emplearse
• Operadores;
• La sentencia WHEN (WHEN/ELSE o WITH/SELECT/WHEN);
• La sentencia GENERATE;
• La sentencia BLOCK.
Operadores.
Operator type Operators Data types
Figura 5.3 si so
Multiplexor del ejemplo 5.1.
La figura 5.3 muestra un multiplexor de 4 entradas y un bit por entrada. La salida debe ser igual a
la entrada seleccionada por los bits de selección, sl-sO. Su implementación, usando operadores
lógicos, puede hacerse como sigue
1 ---------------------------------------
2 LIBRARY ieee;
3 USE ieee.std_logic_l 164.all;
4 ---------------------------------------
5 ENTITY mux IS
6 PORT (a, b, e, d, sO, sl: IN STD_LOGIC;
7 y: OUT STD_LOGIC);
8 END mux;
9 ---------------------------------------
1 O ARCHITECTURE pure _logic OF mux IS
11 BEGIN
12 y<= (a AND NOT sl AND NOT sO) OR
13 (b AND NOT sl AND sO) OR
14 (e AND sl ANDNOT sO) OR
15 (d AND sl AND sO);
16 END pure _logic;
17 ---------------------------------------
Los resultados de la simulación, que confirman la funcionalidad del circuito, se muestran en la
figura 5.4.
~so 1
~s1 l
~;
l_~t
l 1
o
o
-:iifit- '~ 1
o
o
1
1
,~¡j
~ "J o
1
Figura 5.4
Resultados de la simulación del ejemplo 5.1.
5.3 WHEN (Simple y Selected)
Como se mencionó antes, WHEN es uno de los estatutos concurrentes fundamentales (junto con
los operadores y GENERATE). Aparece en dos formas: WHEN / ELSE (simple WHEN) y WITH /
SELECT J WHEN (selected WHEN). Su sintaxis es la siguiente.
WHEN I ELSE:
Siempre que se use WITH I SELECT / WHEN, se deben probar todas las permutaciones, por lo
que la palabra reservada OTHERS es a menudo útil. Otra palabra importante reservada es
UNAFFECTED, la cual debe usares cuando ninguna acción tiene l~gar.
Ejemplo:
a
b
MUX y
e
d
sel (1 :O)
Figura 5.5
Multiplexor del ejemplo 5.2.
En las soluciones anteriores, sel pudo haber sido declarada como un INTEGER, en cuyo caso el
código sería el siguiente:
1 ----------------------------------------------
2 LIBRARY ieee;
3 USE ieee.std_logic_l 164.all;
4 ----------------------------------------------
5 ENTITY mux IS
6 PORT (a, b, e, d: IN STD_LOGIC;
7 sel: IN INTEGER RANGE O TO 3;
8 y: OUT STD_LOGIC);
9 END mux;
1 O ---- Solution 1: with WHEN/ELSE ---------------
11 ARCHITECTURE muxl OF mux IS
12 BEGIN
13 y <= a WHEN sel=O ELSE
14 b VlHEN sel= 1 ELSE
15 e WHEN sel=2 ELSE
16 d;
17 END muxl;
18 -- Solution 2: with WlTH/SELECT/WHEN -----··--
19 ARCHITECTURE mux2 OF mux IS
20 BEGIN
21 WITH sel SELECT
22 y<=a WHENO,
23 b WHEN 1,
24 e WHEN2,
25 d WHEN 3; -- aquí, 3 u OTHERS son equivalents,
26 END mux2; -- ya que todas las opciones se prueban de cualquier manera
27 --~--------------------------------------------
Nota: Sólo una ARCHITECTURE puede ser sintetizada a la vez. Por lo tanto, siempre que
presentamos más de una solución dentro del mismo código global (como el anterior), es implícito
que todas las soluciones deben ser comentadas (con "- -"), excepto una, o una secuencia de
comandos de síntesis debe ser utilizada, con el objeto de sintetizar la solución restante. En
simulaciones, la sentencia CONFIGURATION ouede usarse oara seleccionar una arquitectura
específica.
ena
Figura 5.6
Buffer de Tres estados del ejemplo 5.3.
Este es otro ejemplo que ilustra el uso de WHEN. El buffer de tres estados de la figura 5.6 debe
proveer una salida = input cuando ena (enable) está en nivel bajo, o output = "ZZZZZZZZ"
(alta impedancia) en otro caso.
1 LIBRARY ieee;
2 USE ieee.std_logic_l 164.all;
3 ----------------------------------------------
4 ENTITY tri_state IS
5 PORT ( ena: IN STD _LOGIC;
6 input: IN STD _LOGIC _VECTOR (7 DOWNTO O);
7 output: OUT STD_LOGIC_ VECTOR (7 DOWNTO O));
8 END tri_ state;
9 ----------------------------------------------
10 ARCHITECTURE tri_state OF tri_state IS
11 BEGIN
12 output <=input WHEN (ena='O') ELSE
13 (OTHERS => 'Z');
14 END tri_ state;
15 ----------------------------------------------
Los resultados de la simulación del circuito sintetizado con el código anterior se muestran en la
figure 5.7. Como se esperaba, la salida permanece en el estado de alta impedancia cuando ena
está en nivel alto, siendo una copia de la entrada cuando ena está en nivel bajo.
100.0ns
1
~üO.Ons
1
30J.Ons
1
40J.One 1
toO.Ons
1
GLD.Ons
1
70J.Dns 1
8JO.Ons
1
900.( 1
!iP- ene
~input DO o -r-1--·-·r 1
-
2
.
- -y 3 -r -- ·-·
.4
=rv::
5
- --~(
=: ~r:--
J. ·'I ¡, ¡\
@ou1pu1 DZ z I 2 ~x 3 4 5 --~
Figura 5.7
Resultados de la simulación del ejemplo 5.3.
Ejemplo5.4: Codificador
El diagrama top-level de un codificador n-by-m se muestra en la 5.8. Suponemos que n .es una
potencia de 2, tal que m = log2n. Una y solo una de las entradas bit se espera que esté en nivel
alto a la vez, cuya dirección debe ser codificada en la salida. Se presentan dos soluciones, una
usando WHEN I ELSE, y la otra con WITH I SELECT / WHEN.
X(n-1) ___..
x(n-2) ___.,
nxm (rn-l :O)
ENCODER
X(I) ___.,
x(O)
Observe que el código anterior tiene una larga lista de prueba (líneas 12-20 en la solución 1,
líneas 13- 21 en la solución 2). La solución se hace aun más incómoda cuando el número de bits
de selección crece. En tal caso, la sentencia GENERATE (sección 5.4) o le sentencia LOOP
(sección 6.6) puede usarse.
Los resultados de la simulación (de cada solución) se muestran en la figura 5.9.
100.ons 2JO.Dns ~OJ.Ons 400.0ns
i)i}: DO o 2 ~ ~
l 8 ,( 16 A 32 [lCV. )1
123
2 V 3 l{ \
~·¡
.1 DZ l jl J
4 ~
5 ~
6 I 7
Figura 5.9
Resultados de la simulación del ejemplo 5.4.
,/ .. -- --- ---- --·--------~
~-------------- --------
Un ALU (Arithmetic Logic Unit) se muestra en la figura 5.1 O. Como el nombre lo dice, es un
circuito capaz de ejecutar ambas clases de operaciones, aritméticas así como lógicas logical. Su
operación se describe en la table de verdad de la figura 5.1 O. La salida (aritmética o lógica) se
selecciona por el MSB of sel, mientras que la operación especifica se selecciona por los otros
tres bits de sel.
!!ir' a o 25)
~b DO
:U- cin o
ir sel DO º r~===:
2 Y..-- 4 =» ---
: :. :' '':-:-===;-;::::=::'."'
6-
~X
1====:-;::::::'.
~')' )ps1 X
D 2ffi l---_2s_o_____,KLS2 ~3 Á{,,___.i~:-~....., 6_·X s g
Figure 5.11
Resultados de la simulación del ejemplo 5.5. ·
a (7:0) Logic
b {7:0) Unlt
Arithrnertc
---
Unit sel (3)
sel Operation Function Unit
0000 y<=a Transfer a
OOOi y<= a+1 Incrementa
0010 y<= a-1 Decrementa
0011 y<=b Transfer b Arithmetic
0100 y<==b+l lncrement b
0101 y<= b-1 Decrernent b
01 IO y<=a+b Add a and b
o 1J1 y <=a+b+dn Add a and h with carrv
1000 y<=NOTa Complement a
1001 y<=NOTb Comp1ement b
IOIO y<= a A1\1D b ANO
1011 y <=aOR b OR Logic
llOO y <=aNAND b NANO
1101 y<=aNORb NOR
11 JO y<=aXORb XOR
1111 v <=a XNOR b XNOR
Figura 5.10
ALU del ejemplo 5.5.
La solución que se presenta, además de usar solo código concurrente, también ilustra el uso de
el mismo tipo de datos para desarrollar ambas operaciones, aritméticas y lógicas. Esto es posible
debido a la presencia del paquete std_logic_unsigned de la librería ieee (discutida en la sección
3.6). Se usan dos señales arith y logic, para mantener los resultados de las unidades aritmética
y lógica, respectivamente, siendo pasado el valor seleccionado por el multiplexor. Los resultados
de la simulación se muestran en la figura 5.11.
1 ----------------------------------------------
.2 LIBRARY ieee;
3 USE ieee.std_Iogic_l 164.all;
4 USE ieee.std_logic_unsigned.all;
5 ----------------------------------------------
6 ENTITY ALU IS
7 PORT (a, b: IN STD_LOGIC_VECTOR (7 DOWNTO O);
8 sel: IN STD_LOGIC_ VECTOR (3 DOWNTO O);
9 cin: IN STD_LOGIC;
10 y: OUT STD_LOGIC_ VECTOR (7 DOWNTO O));
11 END ALU;
12 ----------------------------------------------
13 ARCHITECTURE dataflow OF ALU IS
14 SIGNAL arith, logic: STD_LOGIC _VECTOR (7 DOWNTO O);
15 BEGIN
16 ----- Unidad Aritmética: ------
17 WITH sel(2 DOWNTO O) SELECT
18 arith <= a WHEN "000",
19 a+l WHEN "001",
20 a-1 WHEN "010",
21 b WHEN "011",
22 b+l WHEN "100",
23 b-1 WHEN "101 ",
24 a+b WHEN "110",
25 a+b+cin WHEN OTHERS;
26 ----- Unidad Lógica: -----------
27 WITH sel(2 DOWNTO O) SELECT
28 logic <= NOT a WHEN "000"
'
29 NOTb WHEN "001",
30 aAND b WHEN "010",
31 a OR b WHEN "011'\
32 a NAND b WHEN "100"
'
33 a NOR b WHEN "101"
'
34 a XOR b WHEN "110"
'
35 NOT (a XOR b) WHEN OTHERS;
36 -------- Mux: ---------------
37 WITH sel (3) SELECT
38 . y <= arith WHEN 'O',
39 logic WHEN OTHERS;
40 END dataflow;
41 ----------------------------------------------
5.4 GENERATE
GENERATE es otra sentencia concurrente (junto con los operadores y WHEN). Es equivalente al
estatuto secuencial LOOP (capítulo 6) en el sentido de que permite que una
sección de código sea repetida un número de veces, creando así varios casos de las mismas
asignaciones. Su forma general es FOR 1 GENERATE, con la sintaxis mostrada abajo. Observe
que GENERATE debe ser etiquetada.
FOR / GENERATE:
Ejemplo:
Una nota importante acerca de GENERATE (y lo mismo es cierto para LOOP, la cual se verá en
el capítulo 6 es que ambos límites del rango deben ser estáticos. Como ejemplo consideremos,
el siguiente código, donde choice es un parámetro de entrada (no-estático). Esta clase de código
generalmente no es sintetizable.
También debemos tener cuidado de las señales multiply-driven (no resueltas). Por ejemplo,
Es correcto. Sin embargo, el compilador indicará que accum es multiply driven (y detiene la
compilación) en cualquiera de los dos casos siguientes
---------------------------1111!!11""!!1"!!"'!""_.. . . ~-,.- . --·· -· ·=---~--~--
Ejemplo5.6: VectorShifter
Este ejemplo ilustra el uso de GENERATE. En el vector de salida debe ser una versión
desplazada del vector de entrada, con el doble de su ancho y una cantidad de desplazamientos
especificados por otra entrada. Por ejemplo, si el bus de entrada es de 4 bits, y el valor presente
es "1111 ", entonces la salida debe ser una de las líneas de la siguiente matriz (el vector original
está subrayado):
row(O): O O O O 1 1 1 1
row( 1 ) : O O O 1 1 1 1 O
row(2): O O 1 1 1 1 O O
row(3): O 1 1 1 1 O O O
row( 4): 1 1 1 1 O O O O
La primera fila corresponde a la misma entrada, sin desplazamiento y los bits más significativos
llenados con 'O's. Cada fila sucesiva es igual a la fila previa desplazada una posición a la
izquierda. La siguiente solución tiene la entrada inp, la salida outp, y la selección de
desplazamiento sel. Cada fila del array anterior (llamada matrix, línea 14) está definida como un
vector subtype (fine 12).
1 ------------------------------------------------
2 LIBRARY ieee;
3 USE ieee.std_logic_l 164.all;
4 ---------------------------------------------~--
5 ENTITY shifter IS
6 PORT ( inp: IN STD_LOGIC_VECTOR (3 DOWNTO O);
7 sel: IN INTEGER RANGE O TO 4;
8 outp: OUT STD_LOGIC_ VECTOR (7 DOWNTO O));
9 END shifter;
10 ------------------------------------------------
11 ARCHITECTURE shifter OF shifter IS
12 SUBTYPE vector IS STD_LOGIC_VECTOR (7 DOWNTO O);
13 TYPE matrix IS ARRA Y ( 4 DOWNTO O) OF vector;
14 SIGNAL row: matrix;
15 BEGIN
16 row(O) <= "0000" & inp;
17 Gl: POR i IN 1TO4 GENERATE
18 row(i) <= row(i-1)(6 DOWNTO O) & 'O';
19 END GENERATE;
20 outp <= row(sel);
21 END shifter:
'
22 ------------------------------------------------
Los resultados de la simulación se presentan en la figura 5.12. Como puede verse, inp =
"0011" (decimal 3) se aplicó al circuito. El resultado fue outp = "00000011 '' (decimal 3)
cuando sel = O (no desplazamiento), outp = "00000110" (decimal 6) when sel = 1 (un
desplazamiento a la izquierda), outp = "00001100" (decimal 12) cuandon sel = 2 (dos
desplazamientos a la izquierda), y así sucesivamente
100.0ns
1 .
JOJ.Ono;: :lJO.Ons 4CO.Ons
1
mo.on~ .
GCO.Ons 700 On•
1
800.0r.
j¡¡¡;inp 03 3
Dlisel DO o íl
I~ X
J l./
1\ 3 ~
~ il2_
~ou1p 03 3 'i
;, G il
11 ~ 2~ ;~
Figura 5.12
Resultados de la simulación del ejemplo 5.6
5.5 BLOCK
BLOCK Simple
La sentencia BLOCK, en su forma simple, representa solo una manera de particionar localmente
el código. Permite que un conjunto de de sentencias concurrentes sean agrupadas en un Bloque,
con el propósito de convertir el código en general más fácil de leer y más manejable (lo que
puede ser de ayuda cuando se trata de códigos largos). Su sintaxis se muestra enseguida.
label: BLOCK
[ declarative part]
BEGIN
( concurrent statements)
END BLOCK label;
Por lo tanto, el aspecto general de un código con block es el siguiente:
------------------------
ARCHITECTURE example ...
BE GIN
block!: BLOCK
BEGIN
block2: BLOCK
BE GIN
END example;
------------------------
Ejemplo:
bl: BLOCK
SIGNAL a: STD_LOGIC;
BE GIN
a<= input_sig WHEN ena='l' ELSE 'Z';
END BLOCK b 1;
Un Bloque (simple o vigilado) puede ser anidado dentro de otro Bloque. La sintaxis
correspondiente es la siguiente.
label 1 : BLOCK
[parte declarativa del bloque superior]
BEGIN
[sentencias concurrentes del bloque superior]
label2: BLOCK
[parte declarativa del bloque anidado]
BEGIN
[sentencias concurrentes del bloque anidado]
END BLOCK label2;
[más sentencias concurrentes del bloque superior]
END BLOCK label 1;
Nota: Aunque las técnicas de partición de código son el propósito de la Parte 11 del libro, y la
sentencia BLOCK que acabamos de ver sirve exactamente para este propósito, un Bloque es
descrito en esta sección debido al hecho de que está contenido dentro del código principal (es
decir, no invoca ningún PACKAGE, COMPONENT, FUNCTION, o PROCEDURE extra-estas
cuatro unidades son el verdadero objeto de la Parte 11).
Un Bloque vigilado es una clase especial de Bloque, el cual incluye una expresión adicional,
llamada expresión de guardia. Una sentencia de guardia en un Bloque vigilado se ejecuta
solamente cuando la expresión de guardia es verdadera.
Bloque vigilado:
Como los siguientes ejemplos lo ilustran, aun cuando solo sentencias concurrentes pueden ser
escritas en un Bloque, con un Bloque vigilado se pueden construir circuitos secuenciales. Esto,
sin embargo, no es un enfoque de diseño habitual.
El siguiente ejemplo implementa un latch transparente. En él, clk=' l' (línea 12) es la expresión
de guardia, mientras que q<=GUARDED d (línea 14) es una sentencia de vigilancia. Por tanto,
q<=d solo ocurrirá si clk=' l '.
1 -------------------------------
2 LIBRARY ieee;
3 USE ieee.std_logic_l 164.all;
4 -------------------------------
5 ENTITY latch IS
6 PORT (d, clk: IN STD_LOGIC;
7 q: OUT STD_LOGIC);
8 END latch;
9 -------------------------------
10 ARCHITECTURE 1atch OF latch IS
11 BEGIN
12 bl:BLOCK(clk='l')
13 BEGIN
14 q <= GUARDED d;
15 END BLOCK bl;
16 END latch ·
'
17 -------------------------------
Aquí, se diseña un, flip flap activado con transición positiva tipo O, con reset síncrono. La
interpretación del código es similar a la del ejemplo anterior. En él, clk'EVENT AND clk=' 1'
(línea 12) es la expresión de guardia, mientras que q <= GUARDED 'O' WHEN rst='l' (línea 14)
es una sentencia de guardia. Por lo tanto, q<='O' ocurrirá cuando la expresión de guardia es
verdadera y rst es ' l'.
1 -------------------------------
2 LIBRARY ieee;
3 USE ieee.std _logic _1164.all;
4 -------------------------------
5 ENTITY dff IS
6 PORT ( d, clk, rst: IN STD_LOGIC;
7 q: OUT STD_LOGIC);
8 END dff;
9 -------------------------------
10 ARCHITECTURE dffOF dffIS
11 BEGIN
12- bl: BLOCK (clk'EVENT AND clk='l')
13 BEGIN
14 q <= GUARDED 'O' WHEN rst=' 1' ELSE d;
15 END BLOCK b l :
16 END dff;
17 ------------------------------
5.6 Problemas
Los problemas propuestos en esta sección serán resueltos usando sólo código concurrente
(operadores, WHEN, GENERATE). Después de escribir el código VHDL, sintetícelo y simúlelo
para estar seguro de que trabaja como se espera.
m
.x(O)
xt l )
x(J"-1)
sel
\Figura PS.1 [
·o· ~ 7 PRJORlTY
·r - 6 ENCODER
·o· -~ 5
·o· ~ 4
_.
'1'
_.,. 3 l T
'I'
'O'
__. 2 O .., 'O'
Figura P5.2
Hemos visto el diseño de un multiplexor en los ejemplos 5.1 y 5.2. Esos circuitos eran para un
número predefinido de entradas (4 entradas) y un número predefinido de bits por entrada (1 bit).
Un verdadero mux genérico se representa en la figura P5.1. En ella, n representa el número de
bits de la entrada de selección (sel), mientras que m indica el número de bits por entrada. El
circuito tiene 2n entradas (observe que no hay relación entre m y n). Usando una sentencia
GENERIC para especificar n, y suponiendo que m = 8, diseñe este circuito.
Sugerencia: la entrada debe ser especificada como un arreglo de vectores. Por lo tanto, repase
la sección 3.5. ¿Su solución (ARQUITECTURA) requiere más de una línea de código real?
v=a/2
Figura P5.3
cin (carry in)
i
a (7:0)
+ sum (7:0)
b (7.:0) ..
Usando solo código concurrente, diseñe el Multiplicador/Divisor de la figura PS.3. El circuito tiene
dos entradas integer 8-bits (a, b) y dos salidas integer (x, y), donde x = a*b y y= a/2.
Nota: Para un divisor de punto fijo genérico, debe consultar el capítulo 9.
Usando solo sentencias concurrentes, diseñe el sumador de 8-bit unsigned de la figura PS.4.
En la figura P5.5, hemos añadido una entrada extra de 2-bit (sel) al circuito del problema 5.4, tal
que ahora el circuito puede operar como un sumador/restador signed o unsigned (vea la tabla de
verdad). Escriba un código concurrente VHDL para este circuito.
Nota: Después de haber resuelto este problema, usted puede comparar su solución con el
ejemplo correspondiente en el capítulo 9.
sel operation
cin
00 add unsigned
01 add signed
a (7:0) io sub unsigned
sum 11 sub signed
b (7:0) + (7:0)
sel (l:O)
e out
Figura P5.5
Tabla P5.6
ºººº
0001 ºººº
0001
0010 0011
0011 0010
0100 0110
0101 0111
0110 0101
0111 0100
1000 1100
1001 1101
1010 1111
1011 1110
1100 1010
1101 1011
111 o 1001
1111 1000
El código binario es el más usado de todos los códigos digitales. En él, el LSB (bit menos
significativo) tiene el peso de 20, con el peso incrementado por dos por cada bit sucesivo, hasta
2n_ 1 para el bit de más peso (bit más significativo), donde n es el número de bits en la palabra
de código. El código Gray, por otra parte, se basa en la mínima distancia de Hamming entre
palabras de código vecinos, es decir, sólo un bit puede cambiar cuando pasamos de la palabra
de código j a la (j + 1 ). Ambos códigos, para n = 4, se enlistan en la tabla P5.6. Diseñe un
circuito capaz de convertir el código binario a código Gray (para n genérico ). Si es posible,
presente más de una solución.
La figure P5.7 muestra el diagrama de un barre! shifter muy simple. En este caso el circuito debe
desplazar el vector de entrada (de tamaño 8) O o 1 posiciones a la izquierda. Cuando es
realmente desplazado (shift = 1 ), el bit LSB debe ser lleno con 'O' (mostrado en la esquina
izquierda del fondo del diagrama). Si shift = O, entonces outp = inp; sino, si shift = 1, entonces
outp(O) = 'O' y outp(i) = inp(i - 1 ), para 1 s¡ s 7. Escriba el código concurrente para este
circuito.
Nota: Un barrel shifter completo (con shift =O a n_ 1, donde n es el número de bits) se verá en el
capítulo 9.
inp(7)
outp(7)
inp(6)
outpré)
inp(5)
outp(5)
inp(4)
outp(4)
inp(3)
outp(3)
inp(2)
outp(2)
inp(I)
OUtp{I)
inp(O)
shift
Figura P5.7
Figura P5.8
sel
6 CÓDIGO SECUENCIAL
Como se mencionó en el capítulo 5, el código VHDL es inherentemente concurrente. Los
PROCESSES, FUNCTIONS, y PROCEDURES son las únicas secciones de código que son
ejecutadas secuencialmente. Sin embargo, en su conjunto, cualquiera de estos bloques es aún
concurrente con cualquier otra sentencia colocada fuera de ella.
Un aspecto importante del código secuencial es que no está limitado a lógica secuencial. En
efecto, con el podemos construir circuitos secuenciales además de circuitos combinacionales El
código secuencial también se llama código funcional.
Las sentencias discutidas son todas secuenciales, es decir, se permiten solamente dentro de
PROCESSES, FUNCTIONS, o PROCEDURES. Ellas son: IF, WAIT, CASE, y LOOP.
Las VARIABLES están también restringidas para usarse en código secuencial solamente (es
decir, dentro de un PROCESS, una FUNCTION, o un PROCEDURE). Así que, contrario a una
SIGNAL, una VARIABLE nunca puede ser global, así que su valor no puede ser pasado hacia
afuera directamente.
Aquí nos concentraremos en los PROCESSES. Las FUNCTIONS y los PROCEDURES son muy
similares, pero se usan para sistemas de niveles, por tanto se ven en la Parte 11 de este libro.
6.1 PROCESOS
Las VARIABLES son opcionales. Si se usan, deben ser declaradas en la parte declarativa de el
PROCESS (antes de la palabra BEGIN, como se indica en la sintaxis de arriba). El valor inicial no
es sintetizable siendo tomado en cuanta sólo en las simulaciones. El uso de una etiqueta también
es opcional. Su propósito es mejorar la lectura del código. La etiqueta puede ser cualquier
palabra, excepto palabras reservadas de VHDL (apéndice E).
Para construir un circuito síncrono, es necesario monitorear una señal (clock, por ejemplo). Una
manera común de detector un cambio en una señal es por medio del atributo EVENT (visto en
la sección 4.2). Por ejemplo, si clk es una señal que se va a monitorear, entonces clk'EVENT
regresa el valor TRUE cuando ocurre un cambio en clk (transición positiva o negativa). En
seguida se muestra un ejemplo que ilustra el uso de EVENT y de PROCESS.
Un OFF (OFF, figura 6.1) es el bloque de construcción más básico de circuitos lógicos
secuenciales. En él, la salida debe copiar la entrada ya sea en la transición positiva o negativa de
la señal de reloj (flanco positivo o negativo).
d q
OFF
clk
rst
Figura 6.1
OFF con Reset Asíncrono del ejemplo 6.1
En el código que se presenta, hacemos uso de la sentencia IF (discutida en la sección 6.3) para
diseñar un OFF con reset asíncrono. Si rst = '1 ', entonces la salida debe ser q = 'O' (líneas 14-
15), sin importar el estado de clk, la salida debe copiar la entrada (es decir, q = d) en la transición
positiva de clk (líneas 16-17). El atributo EVENT se usa en la línea 16 para detectar a una
transición del reloj. El PROCESS (líneas 12-19) se ejecuta cada vez que cambia cualquiera de
las señales que aparecen en su lista de sensibilidad (clk y rst, línea 12). Los resultados de la
simulación, que confirman la funcionalidad del circuito sintetizado, se presentan en la figura 6.2.
1 --------------------------------------
2 LIBRARY ieee;
3 USE ieee.std_logic_l 164.all;
4 --------------------------------------
5 ENTITY dff IS
6 PORT (d, clk, rst: IN STD_LOGIC;
7 q: OUT STD_LOGIC);
8 END dff·
'
9 --------------------------------------
1 O ARCHITECTURE behavior OF dff IS
11 BEGIN
12 PROCESS (clk, rst)
13 BEGIN
14 IF ( rst=' l ') TREN
15 q <='O';
16 ELSIF (clk'EVENT AND clk='l') TREN
17 q <= d;
18 END IF;
19 END PROCESS;
20 END behavior;
21 --------------------------------------
Las Señales y las Variables se estudiarán en detalle en el siguiente capítulo. Sin embargo, es
imposible discutir el código secuencial sin saber al menos sus características más básicas.
El VHDL tiene dos maneras de transferir valores no estáticos: por medio de una SIGNAL 0 por
medio de una VARIABLE. Una SIGNAL puede ser declarada en un PACKAGE, ENTITY o
ARCHITECTURE (en su parte declarativa), mientras que una VARIABLE sólo puede ser
declarada dentro de una pieza de código secuencial (en un PROCESS, por ejemplo).
Por lo tanto mientras que el valor de la primera puede ser global, la última es siempre local. El
valor de una VARIABLE nunca puede ser pasada al exterior de un proceso PROCESS
directamente; si es necesario debe ser asignada a una SIGNAL. Por otra parte la actualización
de una VARIABLE es inmediata, es decir, podemos pronto contar con su nuevo valor en las
líneas de texto el código. Este no es el caso con una SIGNAL (Cuando se usa en un PROCESS),
ya que su nuevo valor es generalmente garantizado que esté disponible sólo después de la
conclusión de la ejecución del PROCESS.
Finalmente, recuerde de la sección 4.1 que el operador de asignación para una SIGNAL es "<="
(p ej.: sig <= 5), mientras que para una VARIABLE es":=" (p ej.: var := 5).
6.3 IF
Como se mencionó antes, IF, WAIT, CASE, Y LOOP son las sentencias usadas en el código
secuencial. Por lo tanto, solo pueden ser usadas en el interior de un PROCESS, FUNCTION, o
PROCEDURE.
La tendencia natural para la gente es usar el IF más que cualquier otra sentencia. Aunque esto
podría, en principio, tener una consecuencia negativa (debido a que la sentencia IF/ELSE podría
inferir la construcción de un decodificador de prioridad innecesario), el sintetizador optimizará la
estructura y evitará el hardware extra. La sintaxis de IF se muestra enseguida
ELSE assignments;
END IF;
Ejemplo:
e
o
u
clk N digit (J:O)
T
E
R
Figura 6.3
contador del ejemplo 6.2.
1 -----------~---------------------------------
2 LIBRARY ieee:
'
3 USE ieee.std_logic_l 164.all;
.4 ---------------------------------------------
5 ENTITY counter IS
6 PORT (clk: IN STD_LOGIC;
7 digit : OUT INTEGER RANGE O TO 9);
8 END counter;
9 ---------------------------------------------
!O ARCHITECTURE counter OF counter IS
11 BEGIN
12 count: PROCESS(clk)
13 VARIABLE temp : INTEGER RANGE O TO 1 O·
14 BEGIN
'
15 IF ( clk'EVENT AND clk=' l ') THEN
16 temp := temp + l;
17 IF (temp=lO) THEN temp :=O;
18 END IF;
19 END IF;
20 digit <= temp;
21 END PROCESS count;
22 END counter;
23 ---------------------------------------------
Ü"" clk o
~digit HO
Figura 6.4
Resultados de la simulación del ejemplo 6.2.
Comentario: Observe que el código anterior no tiene entrada de reset ni ningún esquema de
inicialización interna para temp (y el digito, consecuentemente). Por lo tanto, el valor inicial de
temp en el circuito físico puede ser cualquier valor de 4 bits. Si tal valor está abajo de 1 O (vea la
línea 17), el circuito contará correctamente a partir de aquí. Por otra parte, si el valor está arriba
de 1 o, se usará un número de ciclos de reloj hasta que temp alcance la cuenta completa (es
decir, 15, o "1111 "), siendo así automáticamente limpiada, a partir de donde empieza la
operación correcta. La posibilidad de usar unos pocos ciclos de reloj al inicio no es generalmente
un problema. Pero si aun así, si uno quiere evitar que, temp = 1 o, in line 17, puede ser cambiada
a temp >= 1 O, pero esto aumentará el hardware. Sin embargo, si el inicio exactamente a partir de
O es siempre necesario, entonces se debe incluir una entrada de reset (como en el ejemplo 6.7).
La figura 6.5 muestra un registro de desplazamiento de 4 bits. El bit de salida (q) debe estar
cuatro transiciones positivas de reloj atrás del bit de entrada (d). También contiene un reset
asíncrono, el cual debe forzar las salidas de todos los flip-flops a 'O' cuando es activada. En este
ejemplo, se usa otra vez la sentencia IF.
d q
DFF DFF DFF' DPF
Figura 6.5
Registro de Desplazamiento del ejemplo 6.3.
1 --------------------------------------------------
2 LIBRARY ieee;
3 USE ieee.std_logic_l 164.all;
4 --------------------------------------------------
5 ENTITY shiftreg IS
6 GENERIC (n: INTEGER := 4); -- # of stages
7 PORT (d, clk, rst: IN STD_LOGIC;
g q: OUT STD_LOGIC);
9 END shiftreg;
10 --------------------------------------------------
11 ARCHITECTURE behavior OF shiftreg IS
12 SIGNAL internal: STD_LOGIC_ VECTOR (n-I DOWNTO O);
13 BEGIN
14 PROCESS (clk, rst)
15 BE GIN
16 IF (rst=' 1 ') THEN
17 interna! <= (OTHERS =>'O');
18 ELSIF ( clk'EVENT AND clk=' l ') THEN
19 interna!<= d & internal(internal'LEFT DOWNTO 1);
20 END IF;
21 END PROCESS;
22 q <= intemal(O);
23 END behavior:
'
24 --------------------------------------------------
Los resultados de la simulación se muestran en la fig. 6.6. Como puede verse, q está cuatro
transiciones positivas atrás de d.
.Figura 6.6
Resultados de la simulación del ejemplo 6.3.
6.4 WAIT
La operación WAIT es algunas veces similar a la del IF. Sin embargo, se dispone de más de una
forma de WAIT. Además, al contrario de cuando se usan 1 F, CASE, or LOOP, el PROCESS no
puede tener una lista de sensibilidad cuando se emplea WAIT. Su sintaxis se muestra abajo (hay
tres formas de WAIT).
WAIT ON, por otra parte, acepta señales múltiples. El proceso se pone en espera hasta que
cualquiera de las señales enlistadas cambia. En el siguiente ejemplo, el PROCESS continuará su
ejecución siempre que ocurre un cambio en rst o clk.
PROCESS
BE GIN
W AIT ON clk, rst;
IF (rst=' l ') THEN
output <= "00000000";
ELSIF ( clk'EVENT AND clk=' l ') THEN
output <= input;
END IF;
END PROCESS;
Finalmente, WAIT FOR es usada solo para simulación (generación de ondas para bancos de
pruebas). Ejemplo: WAIT FOR 5ns;
El siguiente código implementa el mismo OFF del ejemplo 6.1 (figuras 6.1 y 6.2). Sin embargo,
aquí WAIT ON se usa en lugar de solo IF.
1 --------------------------------------
2 LIBRAR Y ieee·
'
3 USE ieee.std _ logic _ 1164 .all;
4 --------------------------------------
5 ENTITY dff IS
6 PORT (d, clk, rst: IN STD _LOGIC;
7 q: OUT STD_LOGIC);
8 END dff;
9 --------------------------------------
10 ARCHITECTURE dffOF dffIS
11 BEGIN
12 PROCESS
13 BEGIN
14 W AIT ON rst, clk;
15 IF (rst=' l ') THEN
16 q <='O';
17 ELSIF (clk'EVENT AND clk=' l ') THEN
18 q <= d;
19 END IF;
20 END PROCESS;
21 END dff;
22 --------------------------------------
El siguiente código implementa el mismo contador decimal progresivo de un dígito del ejemplo
6.2 (figuras 6.3 y 6.4). Sin embargo, WAIT UNTIL fue usada en vez de solo IF.
1 ---------------------------------------------
2 LIBRARY ieee;
3 USE ieee.std_logic_l 164.all;
4 ---------------------------------------------
5 ENTITY counter IS
6 PORT (clk: IN STD_LOGIC;
7 digit : OUT INTEGER RANGE O TO 9);
8 END counter;
9 ---------------------------------------------
10 ARCHITECTURE counter OF counter IS
11 BEGIN
12 PROCESS -- no sensitivity list
13 VARIABLE temp: INTEGER RANGE O TO 10;
14 BE GIN
15 WAIT UNTIL (clk'EVENT AND clk='l');
16 temp := temp + 1;
17 IF (temp=lO) THEN temp :=O;
18 END iF;
19 digit <= temp;
20 END PROCESS·
'
21 END counter:
'
22 ---------------------------------------------
6.5 CASE
CASE es otr.a se~tencia usada exclusivamente para código secuencial (junto con IF, LOOP, y
WAIT). Su sintaxis se muestra enseguida.
CASE identifier IS
WHEN value => assignments;
WHEN value => assignments;
ENDCASE;
Ejemplo:
CASE control IS
WHEN "00" => x<=a; y<=b;
WHEN "O l " => x<=b; y<=c;
WHEN OTHERS => x<="OOOO"; y <="ZZZZ";
END CASE;
La sentencia CASE (secuencial) es muy similar a WHEN (combinacional). Aquí también todas las
permutaciones deben ser probadas, así que la palabra reservada OTHERS a menudo es útil.
Otra palabra reservada importante es NULL (la contraparte de UNAFFECTED), la cual debe ser
usada cuando ninguna acción tiene lugar. Por ejemplo, WHEN OTHERS => NULL;. Sin
embargo, CASE permite múltiples asignaciones para cada condición de prueba (como se mostró
en el ejemplo anterior), mientras que WHEN permite sólo una.
Como en el caso de WHEN (sección 5.3), aquí también "WHEN value" puede tomar tres formas:
El siguiente código implementa el mismo OFF del ejemplo 6.1 (figuras 6.1 y 6.2). Sin embargo,
aquí CASE se usó en vez de solo IF. Observe que unas pocas declaraciones innecesarias se
incluyeron intencionalmente en código para ilustrar su uso.
1 ----------------------------------------------
2 LIBRARY ieee· -- Declaración inecesaria,
'
3 -- poque
4 USE ieee.std_logic_l 164.all; -- se usó BIT en vez de
5 -- STD LOGIC
6 ----------------------------------------------
? ENTITY dffIS
8 PORT (d, clk, rst: IN BIT;
9 q: OUT BIT);
10 END dff;
11 ----------------------------------------------
12 ARCHITECTURE dff3 OF dffIS
13 BEGIN
14 PROCESS (clk, rst)
15 BEGIN
16 CASE rst IS
17 WHEN 'l' => q<='O';
18 WHEN 'O' =>
19 IF ( clk'EVENT AND clk=' l ') THEN
20 q <= d;
21 END IF;
22 WHEN OTHERS => NULL; -- Innecesario, rst es d tipo
23 -- BIT
24 END CASE;
25 END PROCESS;
26 END dff3;
27 ----------------------------------------------
El siguiente código implementa un contador decimal progresivo de 2 bits (O -7 99 -7 O), con reset
externo asíncrono mas una conversión de código binario decimal (BCD) a display de siete
segmentos (SSD). En la figura 6.7 se muestran los diagramas del circuito y el SSO. La sentencia
CASE (líneas 31-56) se empleó para determinar la salida para determinar la señal de salida que
alimentará los SSDs. Observe que hemos escogido la siguiente conexión entre el circuito y el
SSD: xabcdefg (es decir, el MSB alimenta el punto decimal, mientras que el LSB alimenta el
segmento g).
Como puede verse este circuito es una extensión directa de la presentada en el ejemplo 6.2, con
las diferencias de que ahora dos dígitos son necesarios en vez de uno, y que las salidas deben
estar conectadas a displays SSO. La operación del circuito puede ser verificada en los
resultados de la simulación de la figura 6.8.
SSD
e
o
u
clk N
T
E
digit2
R
digitl
Figura 6.7
Contador de 2 Dígitos del ejemplo 6.7.
1 --------------------------------------------------
2 LIBRAR Y ieee;
3 USE ieee.std_logic_ll64.all;
4 --------------------------------------------------
5 ENTITY counter IS
6 PORT (clk, reset: IN STD_LOGIC;
7 digit l , digit2: OUT STD_LOGIC_VECTOR(6 DOWNTO O));
8 END counter;
9 --------------------------------------------------
}O ARCHITECTURE counter OF counter IS
11 BEGIN
12 PROCESS(clk, reset)
13 VARIABLE temp 1: INTEGER RANGE O TO 1 O;
14 VARIABLE temp2: INTEGER RANGE O TO 10;
15 BEGIN
16 ---- counter: ----------------------
17 IF (reset=' 1 ') THEN
18 templ :=O;
·1
1
19 temp2 :=O;
20 ELSIF ( clk'EVENT AND clk=' l ') THEN
21 templ := templ + l; 'i
22
23
IF (temp l=l O) THEN
templ :=O;
l
1
24 temp2 := temp2 + 1;
25 IF (temp2=10) THEN
26 temp2 :=O;
27 END IF;
28 END IF;
29 END IF;
30 ----BCD to SSD conversion: --------
31 CASE temp 1 IS
32 WHEN O=> digitl <= "1111110"; --7E
33 WHEN 1 => digitl <= "0110000"; --30
34 WHEN 2 => digitl <= "1101101 "; --6D
35 WHEN 3 => digitl <= "1111001 "; --79
36 WHEN 4 => digitl <= "0110011 "; --33
37 WHEN 5 => digitl <= "1011011 "; --5B
38 WHEN 6 => digitl <= "1011111 "; --5F
39 WHEN 7 => digitl <= "1110000"; --70
40 WHEN 8 => digitl <= "1111111 "; --7F
41 WHEN 9 => digitl <= "1111011 "; --7B
42 WHEN OTHERS => NULL;
43 END CASE;
44 CASE temp2 IS
45 WHEN O=> digit2 <= "1111110"; --7E
46 WHEN 1 => digit2 <= "0110000"; --30
47 WHEN 2 => digit2 <= "11O11O l "; --6D
48 WHEN 3 => digit2 <= "1111001 "; --79
49 WHEN 4 => digit2 <= "0110011 "; --33
50 WHEN 5 => digit2 <= "1011011 "; --5B
51 WHEN 6 => digit2 <= "1011111 ' ; --5F
52 WHEN 7 => digit2 <= "1110000"; --70
53 WHEN 8 => digit2 <= "1111111 "; --7F
54 WHEN 9 => digit2 <= "1111011 "; --7B
55 WHEN OTHERS => NULL;
56 END CASE;
57 END PROCESS;
5 8 END counter;
59 --------------------------------------------------
----- .,...._ .. _._____ .. w-
- .• -~--
125.0ns 2:0.Dns 375.0n3 :00.0ns 625.0ns
~ rnsgt o
..... dk
aJlY lBff:~l HS
~ temp2 HO
.u> digit1 H 58
~digit2 H 7E
Figura 6.8
Resultados de la simulación del ejemplo 6. 7.
Comentario: Observe arriba que la misma rutina se repitió dos veces (usando la sentencia
CASE). Aprenderemos, en la Part 11, cómo escribir y compilar piezas de código usadas
frecuentemente en librerías definidas por el usuario, de tal manera que se eviten dichas
repeticiones .
6.6 LOOP
· Como el nombre lo dice, LOOP es útil cuando una pieza de código debe ser realizado varias
veces. Como IF, WAIT, y CASE, LOOP se usa exclusivamente para código secuencial, así que
también solo puede ser usada dentro de un PROCESS, FUNCTION, o PROCEDURE. Hay varias
maneras de usar un LOOP, como se muestra en las siguientes sintaxis.
J
WHILE ¡LOOP: El loop se repite hasta que una condición ya no se sostiene
FOR i IN O TO 5 LOOP
x(i) <= enable ANO w(i+2);
y(O, i) <= w(i);
END LOOP·
'
En el código anterior, el loop se repetirá incondicionalmente hasta que i alcance 5 (es decir, seis
veces).
Una observación importante en relación a FOR / LOOP (similar a la hecha para GENERATE, en
el capítulo 5) es que ambos límites del rango deben ser estáticos. Así que una declaración del
tipo "FOR i IN O TO choice LOOP", donde choice es un parámetro de entrada (no estático), es
generalmente no sintetizable. . .
Ejemplo de WHILE I LOOP. En este ejemplo, LOOP se repetirá mientras i < 10.
Ejemplo con EXITE: En el siguiente código, EXIT no implica un escape de la iteración presente
de el loop, sino más bien una salida definida (es decir, aun si i está todavía en el rango del dato,
la sentencia LOOP será considerada como concluida). En este caso, el loop terminará tan pronto
como un valor diferente de 'O' se encuentra en el vector de datos.
FOR i IN O TO 15 LOOP
NEXT WHEN i=skip; -- salta a lasiguiente iteración
( ... )
-- .. •""C.--~--, ----- ·-- --·
ENDLOOP·
'
En seguida se presentan varios ejemplos de diseño completos, ilustrando varias aplicaciones de
LOOP.
La figura 6.muestra un sumador carry ripple de 8 bits unsigned. El diagrama top-level muestra las
entradas y salidas del circuito: a y b son los vectores de entrada que se van a sumar, cin es el bit
del cargo de entrada, s es vector suma, y cout es el bit del cargo de salida. El diagrama one-
level-below-top muestra como los bits del cargo se propagan (ripple).
So S¡ S7
Figura 6.9
8-bit Sumador carry ripple del ejemplo 6.8
Cada sección del diagrama anterior es un sumador completo (sección 1.4). Así que sus salidas
pueden ser calculadas por medio de:
Sj = ajXOR b, XOR Cj
Cj+ 1 = (aj AND bj) OR (aj AND cj) OR (bj AND cj)
Se presentan dos soluciones, siendo una genérica (es decir, para cualquier número de bits,
basado en lo que vimos en el capítulo 4) y la otra específica para un número de 8 bits. Por otra
arte ilustramos el uso de vectores y FOR/LOOP en la primera solución, y de enteros y de IF en
la segunda. Los resultados de la simulación de cualquier solución se presentan en la figura 6.10.
Figura 6.10
Resultados de la simulación del ejemplo 6.8.
La figura 6.11 muestra el diagrama de un barrel shifter muy simple. En este caso, el circuito debe
desplazar el vector de entrada (de tamaño 8) O o 1 posición a la izquierda. Cuando realmente se
displace (shift = 1 ), el bit LSB debe ser llenado con 'O' (mostrado en la esquina izquierda del
fondo del diagrama). Sí shift= O, entonces outp = inp; Si shift ;;;: 1, Entonces outp(O) = 'O' and
outp(i) = inp(i - 1 ), para 1 <i<7. A continuación se presenta un código VHDL completo, el cual
ilustra el uso de FOR/LOOP. Los resultados de la simulación aparecen en la figure 6.12.
Nota: Un barrel shifter completo (with shift =O to n-1, donde n es el tamaño del vector de entrada)
se verá en el capítulo 9.
outp(7)
inp(6)
outp(6)
Ínp(5)
outp(5)
inp(4)
outp(4)
inp(3)
outp(3)
inp(2)
outp(2)
inp(J)
outp(l)
Ínp(O)
outp(O)
Figura 6. shift
Barrel shifter simple del ejemplo 6.9.
1 ---------------------------------------------
2 LIBRAR Y ieee;
3 USE ieee.std_logic_l 164.all;
4 ---------------------------------------------
5 ENTITY barrel IS
6 GENERIC (n: INTEGER := 8);
7 PORT ( inp: IN STD _ LOGIC _VECTOR (n-1 DOWNTO O);
8 shift: IN INTEGERRANGE O TO l;
9 outp: OUT STD_LOGIC_ VECTOR (n-1 DOWNTO O));
1 O END barrel;
11 ---------------------------------------------
12 ARCHITECTURE RTL OF barrel IS
13 BEGIN
14 PROCESS (inp, shift)
15 BEGIN
16 IF (shift=O) THEN
17 . outp <= inp;
18 ELSE
19 outp(O) <= 'O';
20 POR i IN l TO inp'HIGH LOOP
21 outp(i) <= inp(i-1);
22 ENDLOOP;
23 END IF;
24 END PROCESS·
'
25 ENDRTL·
'
26 ---------------------------------------------
Figura 6.12
Resultados de la simulación del ejemplo 6.9.
El siguiente diseño cuenta el número de leading zeros en un vector binario empezando del
extremo izquierdo. La solución ilustra el uso de LOOP I EXIT. Recuerde que EXIT no implica un
escape de la iteración actual de el loop, sino más bien una salida definitiva de él (es decir, aun si
i está dentro del rango especificado, la sentencia LOOP será considerada como concluida). En
este ejemplo, el loop terminará tan pronto como un '1' se encuentre en el vector de dato. Por lo
tanto, es apropiado para contar el número de ceros que preceden el primer uno.
l --------------------------------------------
2 LIBRAR Y ieee;
3 USE ieee.std_logic_l 164.all;
4 ---------------~----------------------------
5 ENTITY LeadingZeros IS
6 PORT (data: IN STD__ LOGIC_ VECTOR (7 DO\VNTO O);
7 zeros: OUT INTEGER RANGE O TO 8);
g END LeadingZeros;
9 --------------------------------------------
to ARCHITECTURE behavior OF LeadingZeros IS
11 BEGIN
12 PROCESS (data)
13 VARIABLE count: INTEGER RANGE O TO 8·
'
14 BEGIN
15 count := O;
16 FOR i IN data'RANGE LOOP
17 CASE data(i) IS
18 WHEN 'O' => count := count + 1;
19 WHEN OTHERS => EXIT;
20 END CASE;
21 END LOOP;
22 zeros <= count;
23 END PROCESS;
24 END behavior;
25 --------------------------------------------
Figura 6.13
Resultados de la simulación del ejempl6.1 O.
6. 7 CASE versus IF
ELSEx<=d;
CASE y WHEN son muy similares. Sin embargo, mientras que uno es concurrente (WHEN), el
otro es secuencial (CASE). Sus principales similitudes y diferencias se resumen en la tabla 6.1.
Ejemplo: Desde un punto de vista funcional, los siguientes códigos son equivalentes.
Como ejemplo, consideremos el caso de un contador que debe ser incrementado en cada
transición del reloj (positiva y negativa). Una alternative podría ser la siguiente:
PROCESS ( clk)
BEGIN
IF( clk'EVENT AND clk=' I')THEN
counter <= counter + 1;
ELSIF( clk'EVENT AND clk='O') THEN
counter <= counter + 1 ;
END IF;
END PROCESS;
En este caso, además del mensaje descrito, el compilador podría que el contador de la señal es
multiply driven. En todo caso, la compilación se suspenderá
Otro aspect importante es que el atributo EVENT debe estar relacionado a una condición de
prueba. Por ejemplo, la sentencia IF(clk'EVENT ANO clk='1 ') es correcta, pero al usar solamente
IF(clk'EVENT) el compilador tendrá que asumir una condición de prueba por defult (digamos
"ANO clk='1 "') o enviará un mensaje del tipo "clock not locally stable".
Por ejemplo, consideremos otra vez el caso de un contador que debe ser incrementado en
ambas transiciones de. Uno podría escribir:
PROCESS (clk)
BEGIN
IF( clk'EVENT) THEN
counter := counter + 1 ;
END IF;
END PROCESS;
Ya que se supone que el PROCESS anterior se ejecuta cada vez que clk cambia, uno podría
esperar que el contador se incremente dos veces por ciclo de reloj. Sin embargo, por la razón ya
mencionada, esto no sucederá. Si el compilador asume un valor por default, se sintetizará un
circuito equivocado, porque solo se considerará una transición de clk; si no se supone un valor
por default, entonces espérese un mensaje de error y sin compilación.
Finalmente si aparece una señal en la lista de sensibilidad, pero no aparece en ninguna de las
asignaciones que componen el PROCESS, entonces es probable que el compilador simplemente
la ignorará. Este hecho se ilustra con el contador de doble transición descrito anteriormente.
Oigamos que se usa el siguiente código:
PROCESS ( clk)
BE GIN
counter := counter + 1 ;
END PROCESS;
Este código refuerza la idea de que el contador de la señal sea incrementado siempre que ocurre
un evento en clk (transición positiva más transición negativa). Sin embargo en vez de eso, podría
enviarse un mensaje del tipo "ignored unnecessary pin clk".
Ejemplo: Contrario a los casos descritos anteriormente, el código de dos procesos mostrado
enseguida será correctamente sintetizable por un compilador. Sin embargo, observe que hemos
usado una señal diferente en cada proceso.
----------------------
PROCESS (clk)
BEGIN
IF( clk'EVENT AND clk=' 1 ') THEN
X<= d;
END IF;
END PROCESS;
----------------------
PROCESS ( clk)
BEGIN
IF( clk'EVENT AND clk='O') THEN
y<= d;
END IF;
END PROCESS;
----------------------
Ahora que usted sabe lo que no debe hacer, se le invita a resolver el problema 6.1.
RAM V.T_ena
d~~:rl[ ll
wordO
data in word 1 data_out
word Z 7 q
OFF
addr
dk-;;
t
clk wr ena
(a) lb)
Figura 6.14
Circuito RAM del ejemplo 6.11.
Cuando wr_ ena está en nivel bajo, q se conecta a la entrada del flip-flop, y la terminal d se abre,
así que ningún dato se escribirá en la memoria. Sin embargo, cuando wr_ena pasa a nivel alto, d
se conecta a la entrada del registro, así que en la siguiente transición positiva de clk, d
sobrescribirá sobre su valor anterior.
---------------------------------------------------
2 LIBRARY ieee·
'
3 USE ieee.std_logic_l 164.all;
4 ---------------------------------------------------
5 ENTITY ram IS
6 GENERIC (bits: INTEGER := 8; -- # ofbits per word
7 words: INTEGER := 16); -- # of words in the memory
8 PORT ( wrena, clk: IN STD_LOGIC;
9 addr: IN INTEGER RANGE O TO words-1;
10 data_in: IN STD_LOGIC_ VECTOR (bits-1 DOWNTO O);
11 data_out: OUT STD_LOGIC_ VECTOR (bits-1 DOWNTO O));
12 END ram;
13 ---------------------------------------------------
14 ARCHITECTURE ram OF ram IS
15 TYPE vector_array IS ARRA Y (O TO words-1) OF
16 STD_LOGIC_ VECTOR (bits-1 DOWNTO O);
17 SIGNAL memory: vector_array; 1
18 BEGIN
19 PROCESS (clk, wr_ena)
20 BEGIN
21 IF (wr_ena='l') THEN
22 IF ( clk'EVENT AND clk=' 1 ') THEN
23 memory(addr) <= data_in;
24 END IF;
25 END IF;
26 END PROCESS;
27 data out<= memory(addr);
28 END ram;
29 ---------------------------------------------------
Los resultados de la simulación del circuito sintetizado con el código anterior se muestra en la
figura 6.15.
1CO.Ons '200.0ns 3JD.Ons 4010ns 5CO.Ons 600.0n~. 700.0n3 EO:
ü-·m_ena
--c::lk o
iiF o¡rJ,jr 03 3 ~ .~
'(
m 52
=-
~do:!ta_in 050 K 51 ~
o · so X o ~...!2__,
data_out
ºª X 51
Figura 6.15
Resultados de la simulación del ejemplo 6.11.
Ya hemos visto que el código secuencial puede ser usado para implementar tanto circuitos
secuenciales como circuitos combinacionales. En el primer caso, los registros son necesarios, de
tal manera que serán inferidos por el compilador. Sin embargo, esto no debe pasar en el
segundo caso. Por tanto, si se tarta de hacer un circuito combinacional, entonces se debe
especificar claramente la tabla de verdad en el código.
Con el objeto de satisfacer este criterio, se deben observar las siguientes reglas:
Regla 1: Asegúrese de que todas las señales de entrada usadas (read) en el PROCESS
aparezcan en la lista de sensibilidad.
Regla 2: Asegúrese de que todas las combinaciones de las señales de entrada/salida se incluyen
en el código; es decir, asegúrese de que, al mirar el código, se puede obtener la tabla de verdad
completa del circuito (en efecto, esto es cierto tanto para código secuencial como para).
No cumplir con la regla 1 generalmente causará que el compilador simplemente emita una
advertencia diciendo que una señal de entrada dada no fue incluida en la lista de sensibilidad, y
luego procede como si la señal fuera incluida. A pesar de que no se producen daños, es una
buena práctica de diseño siempre tomar la regla 1 en consideración.
Con respecto a la regla 2, sin embargo, las consecuencias pueden ser más serias porque
especificaciones incompletas de las señales de salida podría causar que el sintetizador infiera
latches con el objeto de mantener sus valores anteriores. Este hecho se ilustra en el ejemplo
siguiente.
Consideremos el circuito de la figura 6.16, para el cual se han provisto las siguientes
especificaciones: x debe comportarse como un multiplexor; es decir, debe ser igual a la entrada
seleccionada por sel; Y, por otra parte, debe ser igual a 'O' cuando sel = "00", o 'l' si sel =
"O 1 ". Estas especificaciones se resumen en la tabla de verdad de la figura 6.16(b). Observe
·~-----,----e.o-e .. - • - ' • .
que es un circuito combinacional. Sin embargo, las especificaciones provistas para Y son
incompletas, como se puede observar en la tabla de verdad de la figura 6.16(b). Usando solo
estas especificaciones, el código podría ser el siguiente:
a
X
b sel :\ y sel X )' sel X y
e 00 a o 00 a o 00 a o
y 01 b 1 Ol h l 01 b l
d 10 e 10 e y 10 e X
11 d 11 d y 1t d X
sel (l :O)
En efecto, si vemos las ecuaciones obtenidas con Quartus 11, por ejemplo (apéndice D),
verificamos que y fue calculada como y= (sel(O) AND sel(l)) OR (sel(O) AND y) OR (sel(l)
AND y). Por lo tanto, se implementó un latch (usando AND/OR gates), el cual hace la tabla de
verdad de la figura 6.16(c).
Para evitar lógica extra requerida por el latch, se deben usar las especificaciones de la figura
6.16(d) (se usó 'X' para todos los valores desconocidos o "don't care"). Así que la línea y
<='X'; debe ser incluida debajo de las líneas 22 y 24 en el código anterior. Ahora, y puede ser tan
simple como y = sel(O).
......
RU.Uns "'"UJ.Uns .::!UJ.Um; .QUU.Uns wu.uns tlJU.Uns /UU.Uns ti
a ,_._.. 1 L __
j;jr-b a
.*»-e a
1
L--·-----~-. -
O-d n 1
i.w= s:::RI nn n X :l .:( ·1 X
-s
·' (I__
... '.l o 1 l ~ 11
a 1
-- y
Figura 6.17
Resultados de la simulación del ejemplo 6.12.
6.11 Problemas
Como los problemas que acabamos de ver, el propósito de los problemas propuestos en esta
sección es para ilustrar aun más la, construcción de código secuencial (es decir, el uso de IF,.
WAIT, CASE, y LOOP, siempre dentro de un PROCESS). Sin embargo, si usted desea conocer
más acerca de SIGNALS y VARIABLES antes de trabajar en los siguientes problemas, puede
ver el capítulo 7, y luego regresar a esta sección. Finalmente, recuerde que con código
secuencial podemos implementar circuitos lógicos secuenciales así como circuitos
combinacionales. A pesar de que va a utilizar solo código secuencial en esta sección, le
invitamos a determinar si cada circuito de los siguientes (y en los ejemplos que acabamos de
ver, hablando del tema) es realmente un circuito secuencial o combinacional.
Problema 6.1: Contador de Eventos
clk
Figura P6.1
din-- dout
DFF DFF DFF OFF
Figure P6.2
La figura P6.3 muestra el mismo codificador de prioridad del problema 5.2. El circuito debe
codificar la dirección del bit de entrada de orden más alto que está activo. La salida "000" debe
indicar que no hay solicitud a la entrada (no hay bit activo). Escriba una solución VHDL para este
circuito usando solo código secuencial. Presente dos soluciones:
'O' 7 PRJOR!TY
•1• ENCODER
6
5
4 2 __.. '1 '
'O'
-i: 3 ~ ')"
Figure P6.3
Problema 6.4: Divisor de Frecuencia Genérico
Escriba un código VHDL para un circuito capaz de dividir la frecuencia de una señal de entrada
de reloj por un entero n (figura P6.4). El código debe ser genérico; es decir, n debe ser definida
usando la sentencia GENERIC.
FREQ.
DIVIDER
Figura P6.4
¿Y qué acerca del problema 6.4 opuesto?, es decir, digamos que queremos multiplicar la
frecuencia por n. ¿Puede hacerse?
Diseñe un timer capaz de contar desde O min:OO seg a 9 min:59 seg (figura P6.6). El circuito
debe tener botones start, stop, y reset. Las salidas deben ser SSD codificadas. Considere que se
dispone de una señal de 1 HZ confiable.
clk
start
----
---->
T
I
M
111
stop
_.,. E
R
reset __..,
Figura P6.6
Considere el timer del problema 6.6. Sin embargo, digamos que ahora solo un botón está
disponible el cual debe desarro_llar las fu~ciones de start y stop ~lternativ~~ente, y también
resetea el circuito cuando se opnme por mas de dos segundos. Escriba un códiqo VHDL ara tal
timer (figura P6.7). Otra vez, considere que se dispone de una señal confiable de reloj de 1 Hz.
min sec sec
clk
T
start/
1
M
•
E
stop/ R
re set
Figure P6.7
La figura P6.8 muestra el diagrama top-level de un detector de paridad. El vector de entrada tiene
ocho bits. La salida debe ser 'O' cuando el número de '1 'sen el vector de entrada es par, o '1' en
otro caso. Escriba un código secuencial para este circuito. Si es posible, escriba más de una
solución.
PARITY
input (7:0) DETECTOR output
Figura P6.8
Tabla P6.9
Tabla PS.10
---------------- ---------------------------------------------------------------------------------------------------
Número de unos en din(7:1) dout(7:0)
----------------------------------------------------------------------------------------------------------------
o 0000000]
00000010
2 00000100
3 00001000
4 00010000
5 00100000
6 01000000
7 10000000
-------------------------------------------------------------------------------------------------------------------
Digamos que queremos diseñar un circuito que cuente el número de 'l 's en un vector binario
dado (tabla P6.9). Escriba un código VHDL que implemente tal circuito. Luego sintetice Y pruebe
su solución.
Diseñe un codificador que recibe como entrada un vector de 7 bits din, y crea a partir de él un
vector de salida dout cuyos bits son todos 'O's, excepto el bit cuyo índice corresponde al número
de '1 'sen din. Todas las situaciones posibles se resumen en la tabla P6.1 O.
Escriba un código secuencial VHDL para el circuito del problema 5.1. Si es posible, presente más
de una solución.
Escriba un código secuencial VHDL para el circuito del ejemplo 5.6. Si es posible, presente más
de una solución.
Escriba un código secuencial VHDL para el circuito del ejemplo 5.5. Si es posible, presente más
de una solución.
(b) Observe que el circuito del ejemplo 6.8 es completamente combinacional, así que también
puede ser implementado usando solamente código concurrente (esto es, sin un PROCESS).
Escriba un código de ejemplo para él. Luego simule y analice los resultados.
Considere el OFF con reset asíncrono de la figura 6.1. Abajo hay varios códigos para ese circuito.
Examine cada uno de ellos y determine si trabajarían apropiadamente. Explique brevemente sus
respuestas.
LIBRAR Y ieee;
USE ieee.std_logic_l 164.all;
--------------------------------------
ENTITY dff IS
POR T ( d, clk, rst: IN BIT;
q: OUT BIT);
END dff;