Curso VHDL – Diseño estructural

Una de las características más potentes del lenguaje VHDL, es la posibilidad de utilizar los módulos que hemos desarrollado para construir módulos más grandes, es decir, un módulo VHDL puede convertirse en componente de otro módulo, de esta forma reutilizamos o aprovechamos módulos ya diseñados con anterioridad, o podemos dividir un diseño complejo en varios diseños más simples.
Utilizar módulos VHDL para construir otros módulos se denomina diseño estructural, que difiere del diseño por comportamiento que hemos utilizado hasta este momento.

Vamos a aprovechar dos de los componentes que hemos creado hasta este momento para crear un componente nuevo: un medio sumador. Como primer paso recordemos la tabla de verdad de un medio sumador: tenemos un par de entradas (A y B) y un par de salidas: suma (S) y acarreo (C):

B A C S
0 0 0 0
0 1 0 1
1 0 0 1
1 1 1 0

Expresándolo en ecuaciones booleanas:
C = A · B
S = A ⊕ B


Vamos a utilizar como componentes los módulos compuerta_and y compuerta_xor para crear un nuevo módulo medio_sumador. Así que vayamos a nuestro proyecto en ISE Project Navigator y agreguemos un nuevo módulo llamado medio_sumador, en el asistente indicaremos cuatro señales, dos entradas (A y B) y dos salidas (C y S):

Continuamos hasta finalizar con el asistente y tener el código creado por Project Navigator, modificamos los comentarios como lo hemos hecho hasta ahora de forma que quedemos con este código:

----------------------------------------------------------------------------------
-- Medio sumador estructural
-- Mexchip - Tutorial VHDL
-- Diciembre 2010
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity medio_sumador is
	port(
		A : in  STD_LOGIC;
		B : in  STD_LOGIC;
		C : out  STD_LOGIC;
		S : out  STD_LOGIC
	);
end medio_sumador;

architecture estructural of medio_sumador is

begin

end estructural;

Ahora si, podemos continuar con el código de nuestro módulo medio_sumador en el que usaremos los módulos compuerta_and y compuerta_xor como componentes.

Antes de utilizar un componente, es necesario declararlo, esta declaración se realiza en el código de la arquitectura, entre la declaración del nombre de arquitectura y la instrucción begin, se incluye el nombre de la entidad que se utilizará como componente, además de la declaración de sus señales (mediante la instrucción port), la sintaxis es la siguiente:

architecture NOMBRE_ARQUITECTURA of NOMBRE_ENTIDAD is
  component NOMBRE_ENTIDAD_COMPONENTE
    port(
      NOMBRE_SEÑAL1: DIRECCIÓN TIPO;
      NOMBRE_SEÑAL2: DIRECCIÓN TIPO
    );
  end component;
  
begin
end NOMBRE_ARQUITECTURA;

La instrucción port dentro de la declaración del componente es prácticamente una copia de las señales que se declararon en el código de entidad del módulo que se usará como componente. Por ejemplo, la declaración de componente del módulo compuerta_and sería la siguiente:

component compuerta_and is
  port(
    a : in  std_logic;
    b : in  std_logic;
    s : out  std_logic
  );
end component;

Con este ejemplo ya podemos agregar las declaraciones de los componentes compuerta_and y compuerta_xor en el módulo medio_sumador (solo se muestra el código de la arquitectura):

----------------------------------------------------------------------------------
-- Medio sumador estructural
-- Mexchip - Tutorial VHDL
-- Diciembre 2010
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity medio_sumador is
	port(
		A : in  STD_LOGIC;
		B : in  STD_LOGIC;
		C : out  STD_LOGIC;
		S : out  STD_LOGIC
	);
end medio_sumador;

architecture estructural of medio_sumador is
	component compuerta_and is
	  port(
		 a : in  std_logic;
		 b : in  std_logic;
		 s : out  std_logic
	  );
	end component;

	component compuerta_xor is
		port(
		 a : in  std_logic;
		 b : in  std_logic;
		 s : out  std_logic
		);
	end component;
	
begin

end estructural;

Ya se han declarado los componentes, ahora debemos hacer uso de ellos, esto se hace mediante una instanciación de componente, en la que se indica el componente del cual se crea una instancia y las conexiones de sus señales, esta es la sintaxis:

ETIQUETA: NOMBRE_COMPONENTE port map( CONEXION1, CONEXION2 );

La etiqueta es obligatoria, permite identificar entre diferentes instanciaciones del mismo componente. La etiqueta va seguida del nombre del componente y después se indican las conexiones de las señales del componente, es decir, se indica con qué entradas, salidas o señales auxiliares (que veremos más adelante) se conectarán las señales que forman parte de la interfaz del componente. Para el caso de nuestro medio sumador, las entradas A y B se conectarán con las entradas a y b de ambas compuertas, mientras que la salida del módulo compuerta_and se conectará con la señal de salida C y la salida del módulo compuerta_xor con la señal de salida S. Veámoslo en código:

ACARREO: compuerta_and port map( A, B, C );
SUMA: compuerta_xor port map( A, B, S );

De esta forma, el código final del módulo medio_sumador es el siguiente:

----------------------------------------------------------------------------------
-- Medio sumador estructural
-- Mexchip - Tutorial VHDL
-- Diciembre 2010
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity medio_sumador is
	port(
		A : in  STD_LOGIC;
		B : in  STD_LOGIC;
		C : out  STD_LOGIC;
		S : out  STD_LOGIC
	);
end medio_sumador;

architecture estructural of medio_sumador is
	component compuerta_and is
	  port(
		 a : in  std_logic;
		 b : in  std_logic;
		 s : out  std_logic
	  );
	end component;

	component compuerta_xor is
		port(
		 a : in  std_logic;
		 b : in  std_logic;
		 s : out  std_logic
		);
	end component;
	
begin

ACARREO: compuerta_and port map( A, B, C );
SUMA: compuerta_xor port map( A, B, S );

end estructural;

Al guardar el código del módulo medio_sumador, notemos que la jerarquía de archivos del proyecto ha cambiado:

Los módulos compuerta_and y compuerta_xor aparecen anidados dentro del módulo medio_sumador, indicando que se están utilizando como componentes de este, además, se muestran las etiquetas que hemos utilizado para instanciar los componentes.
Lo siguiente es verificar la sintaxis
Y procedemos con la simulación:

¡Perfecto! Hemos creado nuestro primer módulo mediante diseño estructural :). Aprovechemos esta inercia y creemos ahora un módulo sumador_completo también de manera estructural. Para crear este módulo, podemos utilizar dos diferentes estrategias: obtener la función lógica del sumador completo y utilizar como componentes los módulos de compuertas que hemos creado, o utilizar dos instancias del módulo medio_sumador para construir el sumador completo. Vamos a utilizar esta última opción, así que recordemos cómo construir un sumador completo de un bit a partir de dos medios sumadores:

Ahora agregamos un nuevo módulo VHDL a nuestro proyecto, como nombre pondremos sumador_completo, tendrá tres entradas: EntradaA, EntradaB y Cin (acarreo de entrada), y dos salidas: Suma, Cout (acarreo de salida). De la misma manera que con el módulo medio_sumador, el nombre de la arquitectura será estructural. En realidad, el nombre de la arquitectura puede ser el que deseemos, solamente estamos usando estructural para que sea más sencillo recordar que esatmos haciendo uso de este tipo de diseño. Continuemos hasta finalizar con el asistente de nuevo módulo VHDL.
Ahora, en el código agregaremos la declaración de los componentes que usaremos: medio_sumador y compuerta_or, de manera que nos quede algo como esto:

----------------------------------------------------------------------------------
-- Sumador completo de 1 bit
-- Mexchip - Curso VHDL
-- Diciembre 2010
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity sumador_completo is
	Port (
		EntradaA : in  STD_LOGIC;
		EntradaB : in  STD_LOGIC;
		Cin : in  STD_LOGIC;
		Suma : out  STD_LOGIC;
		Cout : out  STD_LOGIC
	);
end sumador_completo;

architecture estructural of sumador_completo is
	component medio_sumador
	port(
		A : in  STD_LOGIC;
		B : in  STD_LOGIC;
		C : out  STD_LOGIC;
		S : out  STD_LOGIC
	);
	end component;

	component compuerta_or
		port (
			a : in std_logic;
			b : in std_logic;
			s : out std_logic
		);
	end component;

begin


end estructural;

Muy bien, ahora toca realizar la instanciación de los componentes e indicar sus conexiones, no debe haber ningún problema para la conexión de las señales del módulo sumador_completo (EntradaA, EntradaB, Cin, Suma y Cout), pero seguro habrá duda sobre cómo realizar las conexiones entre los componentes, por ejemplo, ¿cómo indicamos que la salida S del primer medio sumador debe conectarse a la entrada A del segundo? La respuesta es: mediante señales auxiliares. Las señales auxiliares podrían ser algo afin a variables temporales en otros lenguajes de programación (como las usadas en los ciclos, o las usadas para permitir una mayor claridad de código), en VHDL nos permiten realizar la conexión entre señales de varios componentes. En la siguiente figura se muestran en rojo las señales auxiliares de las que haremos uso para realizar las interconexiones de los componentes del módulo sumador_completo

De manera similar a los componentes, las señales deben ser declaradas antes de poder utilizarse, y también como el caso de los componentes, las señales auxiliares se declaran después de la línea que indica el nombre de la arquitectura y antes de la instrucción begin. La sintaxis para declarar una señal es la siguiente:

signal NOMBRE_SEÑAL : tipo;

El código para agregar las señales s1, c1 y c2 a nuestro código sería:

signal s1 : std_logic;
signal c1, c2 : std_logic;

Notemos que el tipo de las señales auxiliares debe coincidir con el tipo de las señales de componentes con las que deseamos realizar la conexión. Recordemos que este código va en el código de la arquitectura, antes de la instrucción begin, por el momento no lo colocamos completo el código para no saturar la página ;).
Bien, ahora vamos con la instanciación, crearemos dos instancias del módulo medio_sumador y una del módulo compuerta_or, y utilizaremos las señales s1, c1 y c2 para interconectar dichas instancias. La sintaxis para instanciar un componente la vimos un poco más arriba, el código sería:

MS0: medio_sumador port map( EntradaA, EntradaB, c1, s1 );
MS1: medio_sumador port map( s1, Cin, c2, Suma );
COR: compuerta_or port map( c1, c2, Cout );

Entonces, el código completo del módulo sumador_completo sería:

----------------------------------------------------------------------------------
-- Sumador completo de 1 bit
-- Mexchip - Curso VHDL
-- Diciembre 2010
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity sumador_completo is
	Port (
		EntradaA : in  STD_LOGIC;
		EntradaB : in  STD_LOGIC;
		Cin : in  STD_LOGIC;
		Suma : out  STD_LOGIC;
		Cout : out  STD_LOGIC
	);
end sumador_completo;

architecture estructural of sumador_completo is
	component medio_sumador
		port(
			A : in  STD_LOGIC;
			B : in  STD_LOGIC;
			C : out  STD_LOGIC;
			S : out  STD_LOGIC
		);
	end component;

	component compuerta_or
		port (
			a : in std_logic;
			b : in std_logic;
			s : out std_logic
		);
	end component;

	signal s1 : std_logic;
	signal c1, c2 : std_logic;

begin

MS0: medio_sumador port map( EntradaA, EntradaB, c1, s1 );
MS1: medio_sumador port map( s1, Cin, c2, Suma );
COR: compuerta_or port map( c1, c2, Cout );

end estructural;

En cuanto guardemos el código, la jerarquía del proyecto se modificará como antes ocurrió con el módulo medio_sumador:

Notaremos que todos los módulos anteriores se han anidado dentro del módulo sumador_completo, además de que hay dos apariciones del módulo medio_sumador, correspondiendo con las instancias que se han creado del mismo.
Lo siguiente, realizar una revisión de sintaxis, si no hay problemas procedemos con la simulación, pero antes recordemos la tabla de verdad para el sumador completo de 1 bit:

Cin EntradaB EntradaA Cout Suma
0 0 0 0 0
0 0 1 0 1
0 1 0 0 1
0 1 1 1 0
1 0 0 0 1
1 0 1 1 0
1 1 0 1 0
1 1 1 1 1


Ahora si, la simulación. Para facilitar las cosas, utilizaremos nuevamente la opción Force Clock…, colocando una frecuencia de 100 ns para EntradaA, 200 ns para EntradaB y 400 ns para Cin, tendremos:

El rectángulo rojo engloba todas las combinaciones de la tabla de verdad, donde podemos ver que el módulo funciona de manera correcta :). Además, en la simulación es posible ver los valores toman las señales auxiliares, de forma que sea más fácil depurar y encontrar posibles errores lógicos del módulo.

Bien, hasta aquí llegamos en esta entrega del curso, esperamos que les vaya gustando hasta el momento, con gusto recibimos sus comentarios y/o sugerencias, hasta la próxima :).

10 comentarios

  1. Otra pregunta… por que cuando hago alguna modificacion y vuelvo a compilar, cargo mi programa mediante el soft de digilent y al arrancar la tarjeta permanece el programa enterior. Debo compilar dos veces para poder cargar el programa en la tarjeta de otra forma el programa anterior permanece. Quizas este haciendo algo mal, ojala puedas ayudarme… gracias

  2. Qué tal Antonio, durante la primera semana de 2011 estaremos publicando la siguiente entrega del curso de VHDL, ahí se muestra cómo realizar la configuración de la tarjeta. Ya está prácticamente listo pero nos interrumpieron las vacaciones ;). Con respecto a lo que mencionas del software de Digilent, no nos había ocurrido, pero en cuanto tengamos ocasión probaremos.

    ¡FELIZ AÑO NUEVO!

    • Léete de nuevo esta entrega del curso, los componentes (en este caso, las compuertas que creamos al iniciar el curso), se declaran en la arquitectura, antes de la instrucción begin, mientras que la instanciación (la conexión de los componentes, compuertas en este caso), se hace entre las instrucciones begin y end de la arquitectura.

Responder a Luis Gonzalez Cancelar respuesta

Tu dirección de correo electrónico no será publicada.


*