Skip to main content

Tutorial de Simulación con GHDL. Full Adder

VHDL es usado generalmente para el diseño de Hardware. Este ejemplo comienza con un sumador completo cuya descripción se encuentra en el archivo adder.vhdl:

entity adder is
    -- i0, i1 y el carry-in ci son las entradas del sumador.
    -- s es la salida ( la suma ), co es el carry-out.
    port (i0, i1 : in bit; ci : in bit; s : out bit; co : out bit);
end adder;
 
architecture rtl of adder is
    begin
    --  La arquitectura de este sumador contiene dos asignaciones concurrentes.
    --  Calculamos la suma.
    s <= i0 xor i1 xor ci;
    --  Calculamos el acarreo ( carry ).
    co <= (i0 and i1) or (i0 and ci) or (i1 and ci);
end rtl;

Podemos analizar el archivo de diseño:

$ ghdl -a adder.vhdl

Podemos tratar de ejecutar el diseño del "sumador", pero esto es innecesario, porque no obtendremos ningún resultado visible. Si queremos chequear el diseño, debemos correr un testbench. Este testbench es muy simple, debido a que el sumador también es muy simple: lo único que hace el tb es chequear exhaustivamente todas las entradas y controlar las salidas.
El archivo adder_tb.vhdl contiene el testbench para el sumador:

--  Un testbench no tiene puertos.
entity adder_tb is
end adder_tb;
 
architecture behav of adder_tb is
    --  Declaración del componente que será instanciado.
    component adder
        port (i0, i1 : in bit; ci : in bit; s : out bit; co : out bit);
    end component;
    --  Especificamos que entidad está ligada con el componente.
    for adder_0: adder use entity work.adder;
    signal i0, i1, ci, s, co : bit;
        begin
            --  Instanciamos el componente.
            adder_0: adder port map (i0 => i0, i1 => i1, ci => ci, s => s, co => co);   
            --  Este process hace realmente el trabajo.
            process
                type pattern_type is record
                    --  Las entradas del sumador.
                    i0, i1, ci : bit;
                    --  Las salidas esperadas del sumador.
                    s, co : bit;
                end record;
                --  Los patrones a aplicar.
                type pattern_array is array (natural range <>) of pattern_type;
                constant patterns : pattern_array :=(('0', '0', '0', '0', '0'),('0', '0', '1', '1', '0'),
                                                                           ('0', '1', '0', '1', '0'),('0', '1', '1', '0', '1'),
                                                                           ('1', '0', '0', '1', '0'),('1', '0', '1', '0', '1'),
                                                                           ('1', '1', '0', '0', '1'),('1', '1', '1', '1', '1'));
                begin
                    --  Chequeamos cada patrón.
                    for i in patterns'range loop
                        --  Seteamos las entradas.
                        i0 <= patterns(i).i0;
                        i1 <= patterns(i).i1;
                        ci <= patterns(i).ci;
                        --  Esperamos por los resultados.
                        wait for 1 ns;
                        --  Chequeamos las salidas.
                        assert s = patterns(i).s
                            report "resultado de la suma erróneo" severity error;
                        assert co = patterns(i).co
                            report "acarreo de salida erróneo" severity error;
                    end loop;
                    assert false report "fin del test" severity note;
                    --  Esperamos por siempre; esto terminará la simulación.
                    wait;
            end process;
end behav;

Como es usual, debemos analizar el diseño:

$ ghdl -a adder_tb.vhdl

Y construir un ejecutable para el testbench:

$ ghdl -e adder_tb

No debemos especificar que objetos son requeridos: GHDL los conoce y automaticamente los agrega en el ejecutable. Ahora, es tiempo de correr el testbench:

$ ghdl -r adder_tb

adder_tb.vhdl:52:7:(assertion note): fin del test

Si el diseño es algo más complejo, podríamos inspeccionar las señales. Los valores de las señales pueden ser volcados usando el formato de archivo VCD. El resultado puede ser leído con un visor de ondas como puede ser GTKWave. Primero debemos simular el diseño y volcarlo en un archivo de forma de onda.

$ ghdl -r adder_tb --vcd=adder.vcd

Luego, vemos las ondas:

$ gtkwave adder.vcd