VHDL: diseñando una unidad aritmético lógica

VHDL diseñando una ALU.

Aquí dejo otro code-kata en VHDL, esta vez de una ALU básica con 8 modos de funcionamiento: suma, resta, multiplicación, exponente a 2, complemento a 2, and, or y not. ALU son las siglas de Arithmetic Logic Unit. Se trata de un componente básico de los procesadores. Resumiendo, es un circuito que recibe unas entradas y saca unas señales de salida, destinado a hacer operaciones aritméticas o lógicas como pueden ser la suma, resta, multiplicación, and, or, etcétera.

En esta ALU tenemos 3 entradas: 2 de ellas son los números, y la tercera entrada es el modo de funcionamiento (la operación que queremos que haga). En las salidas le he puesto 3 salidas: 1 es el resultado básico para la mayoría de estas operaciones, 1 resultado de multiplicación que es un vector de bits más largo, y una salida de estado que aunque no se en éste código usa la idea es dar una señal de éxito 0 o de error 1.

El código de la ALU

Para esta ALU sólo hay 8 modos de funcionamiento, seleccionados con la entrada mode de 3 bits, el resultado funcionando ha quedado algo tal que así:

-- ALU
--
-- 000 A + B
-- 001 A - B
-- 010 A * B
-- 011 A^2
-- 100 -A
-- 101 A and B
-- 110 A or B
-- 111 not A

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity alu is
    port (
        status : out std_logic;
        result : out std_logic_vector (7 downto 0);
        resultMult : out std_logic_vector (15 downto 0);
        mode : in std_logic_vector (2 downto 0);
        a : in std_logic_vector (7 downto 0);
        b : in std_logic_vector (7 downto 0)
    );
end entity;

architecture arch_alu of alu is
begin

    -- Este process se ejecuta cada vez que cambian las señales 
    -- a las que es sensible: mode, a y/o b.
    main_process: process (mode, a, b)
    begin
        if mode = "000" then
            result <= std_logic_vector(unsigned(a) + unsigned(b));
            resultMult <= "UUUUUUUUUUUUUUUU";
            status <= '0';
        elsif mode = "001" then
            result <= std_logic_vector(unsigned(a) - unsigned(b));
            resultMult <= "UUUUUUUUUUUUUUUU";
            status <= '0';
        elsif mode = "010" then
            result <= "UUUUUUUU";
            resultMult <= std_logic_vector(signed(a) * signed(b));
            status <= '0';
        elsif mode = "011" then
            result <= "UUUUUUUU";
            resultMult <= std_logic_vector(signed(a) * signed(a));
            status <= '0';
        elsif mode = "100" then
            result <= std_logic_vector(-signed(a));
            resultMult <= "UUUUUUUUUUUUUUUU";
            status <= '0';
        elsif mode = "101" then
            result <= a and b;
            resultMult <= "UUUUUUUUUUUUUUUU";
            status <= '0';
        elsif mode = "110" then
            result <= a or b;
            resultMult <= "UUUUUUUUUUUUUUUU";
            status <= '0';
        elsif mode = "111" then
            result <= not a;
            resultMult <= "UUUUUUUUUUUUUUUU";
            status <= '0';
        end if;
    end process;

end architecture;

El código del banco de pruebas

Y el banco de pruebas, esta vez generando bucles para algunas de las posibles combinaciones de señales de entrada:

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity alu_tb is
end entity;

architecture arch_alu_tb of alu_tb is

    component alu is
        port (
            status : out std_logic;
            result : out std_logic_vector (7 downto 0);
            resultMult : out std_logic_vector (15 downto 0);
            mode : in std_logic_vector (2 downto 0);
            a : in std_logic_vector (7 downto 0);
            b : in std_logic_vector (7 downto 0)
        );
    end component;

    signal testStatus : std_logic;
    signal testResult : std_logic_vector (7 downto 0);
    signal testResultMult : std_logic_vector (15 downto 0);
    signal testMode : std_logic_vector (2 downto 0);
    signal testA : std_logic_vector (7 downto 0);
    signal testB : std_logic_vector (7 downto 0);

begin

    testing_unit: alu port map (
        status => testStatus,
        result => testResult,
        resultMult => testResultMult,
        mode => testMode,
        a => testA,
        b => testB
    );

    generate_signals : process
    begin

        report "Comienza la simulación.";

        -- Simulando números pequeños
        -- en los 8 modos disponibles
        for i in 0 to 2 loop
            for j in 0 to 2 loop
                for k in 0 to 7 loop

                    report integer'image(i) & " " & integer'image(j) & " " & integer'image(k);

                    testA <= std_logic_vector(to_unsigned(i, 8));
                    testB <= std_logic_vector(to_unsigned(j, 8));
                    testMode <= std_logic_vector(to_unsigned(k, 3));

                    wait for 10 ns;

                end loop;
            end loop;
        end loop;

        -- Simulando números grandes
        -- en los 8 modos disponibles
        for i in 253 to 255 loop
            for j in 253 to 255 loop
                for k in 0 to 7 loop

                    report integer'image(i) & " " & integer'image(j) & " " & integer'image(k);

                    testA <= std_logic_vector(to_unsigned(i, 8));
                    testB <= std_logic_vector(to_unsigned(j, 8));
                    testMode <= std_logic_vector(to_unsigned(k, 3));

                    wait for 10 ns;

                end loop;
            end loop;
        end loop;

        report "Finaliza la simulación.";
        wait;
    end process;

end architecture;

Se simulan todos los modos de funcionamientos, pero los números de entradas van de los rangos 0 a 2 y 253 a 255 en decimal. Queda pendiente el hacer comprobaciones usando assert y report para que esté completo.. dejo esto para otro post.

Compartir..

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *