Curso VHDL – Sumador/Restador con operadores aritméticos

Se nota que me gustan los sumadores ¿verdad? En las anteriores entregas hemos armado sumadores a base de compuertas que se unieron para crear medios sumadores y a partir de estos sumadores completos. Posteriormente armamos un sumador de un bit a partir de su ecuación booleana, que utilizamos para armar un sumador de cuatro bits. En esta entrega veremos un circuito de suma más (que también resta), pero en esta ocasión haremos uso de los operadores aritméticos que VHDL pone a nuestra disposición. Sin más introducción, empecemos con nuestro proyecto.

Creamos un nuevo proyecto en ISE Project Navigator, lo nombramos SumaRestaVHDL, el módulo principal será de tipo HDL.

Ahora, agregamos un nuevo fuente, ya sea con el botón a la izquierda de la ventana Design o mediante el menú que aparece tras hacer clic derecho sobre el nombre de nuestro proyecto, el módulo será de tipo VHDL y por nombre le pondremos SumaResta:


Nuestro sumador/restador será de 4 bits, por lo que tiene dos entradas de 4 bits y una salida también de 4 bits, en VHDL podemos declarar puertos de varios bits a los que se denominan buses, el asistente para agregar un nuevo archivo fuente nos permite indicar los puertos que son buses de esta forma:

Marcamos la casilla en la columna Bus para indicar que el puerto es un bus, además, el número de bits del bus lo indicamos mediante un par de números en las columnas MSB (Most Significant Bit – bit más significativo) y LSB (Least Significant Bit – bit menos significativo), esto es, el número de bit que corresponde a los bits más y menos significativos. Recordemos, en electrónica digital los buses, registros o demás elementos compuestos por más de un bit generalmente se numeran del bit más a la derecha (el menos significativo) hacia la izquierda iniciando en cero, por ejemplo, un elemento de cuatro bits podría ser:

# bit 3 2 1 0
Valor X X X X


El bit menos significativo es el 0, mientras que el más signicativo es el bit 3, que son los que indicamos en el asistente para nuestro nuevo módulo. En cuanto terminemos de agregar el módulo usando el asistente veremos cómo se declara un bus en código VHDL.

Quizá alguien se haya preguntado para qué se utilizará la entrada denominada Sel. Recordemos que nuestro módulo debe sumar y restar, pero solo puede realizar una operación a la vez (solo tiene una salida), así que ¿cómo le indicamos si debe sumar o restar las entradas? La entrada Sel nos permitirá seleccionar la operación a realizar, si su valor es 1, la salida corresponderá a la resta (A – B), mientras que, cuando Sel tenga un valor de 0, la salida corresponderá a la suma (A + B), una vez aclarada la función de la entrada Sel finalicemos con el asistente y pongamos atención a la sintaxis para declarar un bus en VHDL:

A : in std_logic_vector( 3 downto 0 );

La diferencia contra las declaraciones que hasta el momento habíamos utilizado (puertos de 1 bit), es la utilización del tipo STD_LOGIC_VECTOR en lugar de STD_LOGIC, seguido de la indicación de los bits más y menos significativos indicados entre paréntesis (separados por la instrucción downto. VHDL nos permite numerar los bits de un bus del modo que nosotros deseemos, ya sea empezando por el menos significativo y aumentando hacia el más significativo o al revés, que el más significativo sea el bit 0 por ejemplo y el menos significativo el bit número 3 (para el caso de un bus de cuatro bits), para lo que se usaría la instrucción to en lugar de downto, o incluso empezar a numerar de un valor mayor a 0, a continuación pondremos algunos ejemplos de declaraciones de un puerto de cuatro bits:

puerto1 : in std_logic_vector( 3 downto 0 );
puerto2 : in std_logic_vector( 0 to 3 );
puerto3 : in std_logic_vector( 1 to 4 );
puerto4 : in std_logic_vector( 10 downto 7 );

Todas las instrucciones anteriores declaran puertos de entrada de cuatro bits, la diferencia estriba en la manera en la que podremos acceder a los bits individuales de cada uno de ellos, por ejemplo, para acceder al bit más significativo de puerto1 usaríamos:

puerto1(3);

mientras que para acceder al bit más significativo de puerto3 tendríamos que usar:

puerto3(1);

Pero como mencionamos, en electrónica digital generalmente se numera a partir de cero y empezando por el bit menos significativo, así que esa será la forma como trabajaremos las declaraciones de buses en este curso.

Continuemos ahora con el código de nuestro módulo SumaResta, tenemos que cuando la entrada Sel tenga un valor de 0 a la salida enviaremos el resultado de la suma de las entradas A y B, y cuando valga 1 enviaremos el resultado de la resta A – B. En los lenguajes de programación de PC podemos ejecutar una u otra porción de código dependiendo del resultado de la evaluación de una expresión empleando la sentencia if, en VHDL también existe dicha instrucción, pero solo puede utilizarse dentro de un bloque de proceso, los cuales veremos hasta la próxima entrega, así que por ahora no los usaremos ;). VHDL nos proporciona otra instrucción que consigue un efecto similar y que puede emplearse fuera de un proceso, se trata de la sentencia when-else, cuya sintaxis es la siguiente:

SALIDA <= (RESULTADO) when selector=VALOR else (OTRORESULTADO);

RESULTADO se encuentra entre paréntesis porque peude ser un valor constante o el resultado de una operación. Utilizando la sentencia when-else para asignar a la salida de nuestro módulo la suma o resta de las entradas A y B según el valor de la entrada Sel tendríamos lo siguiente en nuestro código:

Salida <= ( A - B ) when Sel = '1' else ( A + B );

Esto es, asigna a Salida el resultado de la operación (A - B) cuando Sel valga '1' (nótese que un valor de un bit va entre comillas simples), para otros valores de Sel asigna a la salida el resultado de la operación (A + B).

El código completo quedaría similar a este:

----------------------------------------------------------------------------------
-- Módulo sumador y restador de 4 bits
-- Mexchip - Tutorial VHDL
-- Diciembre 2011
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity SumaResta is
	port (
		A : in  STD_LOGIC_VECTOR (3 downto 0);
      B : in  STD_LOGIC_VECTOR (3 downto 0);
      Sel : in  STD_LOGIC;
      Salida : out  STD_LOGIC_VECTOR (3 downto 0)
	);
end SumaResta;

architecture Behavioral of SumaResta is

begin

	Salida <= ( A - B ) when Sel = '1' else ( A + B );

end Behavioral;

Ahora si, guardemos el módulo, revisamos sintaxis y... ¡Error!

¿Qué ocurrió? Veamos los mensajes de error que nos arrojó Project Navigator:

ERROR:HDLParsers:808 - "E:/.../SumaResta.vhd" Line 22. - can not have such operands in this context.
ERROR:HDLParsers:808 - "E:/.../SumaResta.vhd" Line 22. + can not have such operands in this context.

Nos indica que ambos problemas se encuentran en la línea 22, indicando que no podemos tener operadores - o + en tal contexto, la línea 22 de nuestro código:

	Salida <= ( A - B ) when Sel = '1' else ( A + B );

Peri si seguimos la sintaxis indicada para la sentencia when-else, ¿cuál es el problema? El problema es que los operadores de suma y resta no se encuentran definidos por defecto en VHDL, sino que son parte de una biblioteca adicional, la biblioteca STD_LOGIC_UNSIGNED (para valores sin signo, también existe la biblioteca STD_LOGIC_SIGNED que utiliza valores con signo), la cual debemos incluir para poder utilizar estos operadores, entonces, incluyámosla:

----------------------------------------------------------------------------------
-- Módulo sumador y restador de 4 bits
-- Mexchip - Tutorial VHDL
-- Diciembre 2011
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity SumaResta is
	port (
		A : in  STD_LOGIC_VECTOR (3 downto 0);
      B : in  STD_LOGIC_VECTOR (3 downto 0);
      Sel : in  STD_LOGIC;
      Salida : out  STD_LOGIC_VECTOR (3 downto 0)
	);
end SumaResta;

architecture Behavioral of SumaResta is

begin

	Salida <= ( A - B ) when Sel = '1' else ( A + B );

end Behavioral;

Revisamos sintaxis y en esta ocasión todo está correcto.

Muy bien, entonces procedamos a simular nuestro diseño, vamos a la vista Simulation en la ventana Design y hacemos doble clic sobre el proceso Simulate Behavioral Model:

Ahora tendremos abierto ISim:

Si observamos, los puertos que son buses en nuestro diseño tienen entre corchetes los números de los bits MSB y LSB, además, a la izquierda tienen una flecha, si hacemos clic sobre ella podremos ver desplegados en la ventana de formas de onda cada uno de los bits que componen el bus:

Ejecutemos ahora una primera simulación, sumaremos los valores 5 (entrada A) y 3 (entrada B), habrá también que indicar un valor de 0 para la entrada Sel. Utilizaremos la función Force Constant..., primero haremos clic derecho sobre el nombre de la señal a[3:0] y seleccionaremos Force Constant... del menú desplegable:

En la ventana que aparece indicaremos el valor en el campo Force to Value:, debemos indicar un valor que corresponda al formato indicada en el campo Value Radix, que en este caso es Binary, que nos indica valores binarios:

Pero también podemos indicar valores en formato decimal si cambiamos el campo Value Radix a Unsigned Decimal (también existe la opción Signed Decimal):

Repitamos la operación para las entradas B (0011 - 3) y Sel (0):


Ahora si, hacemos clic en el botón Run for the time specified on the toolbar y tendremos:

Como podemos observar, la salida tiene el valor binario 1000 que corresponde con la suma que esperábamos (5+3), también podemos cambiar el formato con el que se visualizan los valores en la ventana de formas de onda haciendo clic derecho sobre el nombre del puerto que deseamos modificar, luego vamos al menú Radix y seleccionamos el deseado, por ejemplo Unsigned Decimal:

Así, si modificamos el formato para los tres buses (A, B y Salida) a Unsigned Decimal nuestra ventana de simulación se vería así:

Ahora puede ser más fácil observar los resultados (sobre todo cuando los buses sean de más bits).
Muy bien, hagamos otra simulación, ahora modifiquemos el valor de la entrada Sel a 1:

Con lo que esperaríamos que ahora el resultado fuera la resta de A - B:

Al parecer, nuestro módulo funciona correctamente, como ejercicio pueden realizar algunas otras operaciones para asegurarse de que así sea, aquí nosotros continuaremos con la implementación en una tarjeta Nexys 2, la asignación de pines la hemos hecho de esta forma:

# Pines de la tarjeta Nexys 2
# para sumador/restador de 4 bits
NET "Sel" LOC = H13;
NET "A<3>" LOC = R17;
NET "A<2>" LOC = N17;
NET "A<1>" LOC = L13;
NET "A<0>" LOC = L14;
NET "B<3>" LOC = K17;
NET "B<2>" LOC = K18;
NET "B<1>" LOC = H18;
NET "B<0>" LOC = G18;
NET "Salida<3>" LOC = K14;
NET "Salida<2>" LOC = K15;
NET "Salida<1>" LOC = J15;
NET "Salida<0>" LOC = J14;

Y este es un breve video con unas pruebas (5 +/- 3) y ( 8 +/- 1) para observar el funcionamiento:

Muy bien, hasta aquí llegamos con esta entrega, ¡hasta la próxima!

18 comentarios

  1. implementé el programa y funciona muy bien. Pero tengo un problema: cuando hago una resta y el resultado es negativo el programa no funciona. Qué sentencia debería agregar para que funcione¿?

  2. Qué tal, ¿no te funciona en la simulación o en la implementación? ¿Puedes poner un ejemplo de una resta que no te funcione? Recuerda que en binario, los números negativos se representan en complemento a 2.

  3. trataba de implementar un módulo igual antes de ver tu post, en efecto el modulo funciona correctamente, pero si haces una suma de un 15+15 no te dará el 30 que debería, porque el 30 ocupa 5 bits. Cuando trate de aumentarme un bit a la salida, me marcaba error de síntesis… es posible corregir ese error?

    • En efecto, al utilizar el operador +, no es posible obtener el acarreo de salida pues el resultado de dicho operador debe ser de la misma longitud de bits que el mayor de sus operandos, que en este caso es 4 bits. Hay algunas formas de “solucionarlo”:

      • Declarar operadores de mayor longitud. Esta es una solución “falsa” al usuario, porque seguiremos perdiendo el acarreo en caso de desbordar esta nueva longitud.
      • Antes de efectuar la operación +, concatenar a los operadores un bit 0 como más significativo, provocando que el resultado pueda ser de un bit más y se conserve el acarreo de salida.
      • Convertir los operandos a entero, realizar la operación y finalmente convertir el resultado a std_logic_vector con un tamaño un bit mayor que los operandos.

      Si tienes problemas para implementarlo, con gusto pronto publicamos cómo hacerlo.

  4. buena tarde cual es el programa con el que hacen la simulación?
    actualmente tengo que hacer un sumadro en VHDL pero no tengo un programa para simularlo cuales me recoemienda que pueda bajar gratis.

    • En los ejemplos que se han publicado, utilizamos el simulador incluído con el Webpack de Xilinx. Puedes encontrarlo en el sitio web de Xilinx, es gratuito, aunque la descarga es bastante pesada. Otra herramienta que puedes utilizar es Active HDL de Aldec.

  5. Qué tal, una pregunta podrías ayudarme con un sumador BCD de 2 bits? tengo hecho el sumador BCD y tengo entendido que debo usar dos de ellos pero lo he intentado y no lo he conseguido, te agradecería que me pudieras ayudar, saludos.

  6. Que tal. Implementé tu diseño y me funcionó. Supongo que para una suma sencilla de dos números de 4 bits se implementa de la misma manera o es diferente?

  7. Hola; será que me puedas echar la mano para hacer un termómetro? Lo que me pide el profe es un LM35+ADC+Nexys2.

    Algo que me puedas ayudar!

  8. Hola muchas gracias por tu valioso tiempo al introducirnos a este maravilloso mundo, gracias a ti aprendi como ocupar mi tarjeta, esperó subas mas tutoriales respecto a este tema, nuevamente gracias.

  9. Hola que tal me podrias ayudar con el siguiente codigo ?
    Es un contador ascendendte desendente de 0 -15, lo tengo que implementar en la tarjeta spartan 3.
    Mis dudas son en estas dos lineas if (w=’0′ AND q>”0000″) y q<= (q – 1);
    pasa usar mayor que menor que, es simplemente como lo puse? se ocupa alguna libreria especifica? y para la suma/resta creo que con la libreria que comentas bastaria pero, hay algun error que identifiques ?
    De verdad agradeceria tu ayuda. Gracias.

    entity contador is port (
    clk, reset : in std_logic;
    w : in std_logic;
    t : OUT std_logic_vector (3 downto 0);
    q : buffer std_logic_vector (3 downto 0);
    lleno : out std_logic);

    end contador;

    architecture Behavioral of contador is
    begin

    process (clk, reset)begin
    if reset = '0' then q<="000";lleno”0000″) then
    q<= (q – 1);
    elsif (w='1' AND q<"1111") then
    q<= (q + 1);
    end if;
    if q="1111" then
    lleno<='1';
    end if;

    end if;
    end if;
    end process;
    t <= q;

    end Behavioral;

Responder a mexchip Cancelar respuesta

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


*