5.1. Prueba de valores extremos

En ocasiones no es posible probar todos los valores de entrada posibles y se recurre a verificar sólo los valores extremos, junto con algunas muestras aleatorias intermedias. A esta estrategia se la suele denominar boundary testing. El mayor problema de este método es que si nuestro sistema es complejo el análisis y selección de que valores conviene probar puede ser sumamente complejo. La idea es probar los valores que sabemos que podrían generar problemas.

Para ilustrar esta metodología mostraremos dos casos. El primero referido a un circuito asincrónico y el segundo a otro sincrónico, en ambos casos se seleccionaron circuitos muy simples.

5.1.1. Ejemplo asincrónico

Como dispositivo a verificar hemos seleccionado un conversor de BCD natural a código Aiken. El código fuente del mismo puede encontrarse en el Ejemplo 5-1.

Ejemplo 5-1. Conversor de BCD natural a Aiken (bcd2aiken.vhdl)

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

entity BCD2Aiken is
   port(
      A  : in  std_logic_vector(3 downto 0);
      B  : out std_logic_vector(3 downto 0));
end entity BCD2Aiken;

architecture Flujo of BCD2Aiken is
begin
   B <= A when unsigned(A)<5 else
        std_logic_vector(unsigned(A)+6) when unsigned(A)<10 else
        "XXXX";
end architecture Flujo; -- Entity: BCD2Aiken

Como valores a verificar hemos seleccionado: 0, 4, 5, 9 y 10, por tratarse de los extremos donde el código cambia. En el Ejemplo 5-2 vemos un banco de pruebas que realiza dicha verificación. Se puede observar que para esto se va asignando a la entrada los estímulos y luego se verifica que la salida corresponda a los valores esperados. Notar que es necesario esperar a que la entrada se propague a la salida, por tratarse de una simulación funcional, sin tener en cuenta los tiempos de un dispositivo en particular, se eligió un valor completamente arbitrario de tiempo para dicha espera.

Ejemplo 5-2. Prueba de extremos del conversor (t_b2a_1.vhdl)

library IEEE;
use IEEE.std_logic_1164.all;

entity T_B2A_1 is
end entity T_B2A_1;

architecture Simulador of T_B2A_1 is
   -- Declaración del componente a probar
   component BCD2Aiken is
      port(
         A  : in  std_logic_vector(3 downto 0);
         B  : out std_logic_vector(3 downto 0));
   end component BCD2Aiken;
   -- Señales auxiliares
   signal entrada : std_logic_vector(3 downto 0):="0000";
   signal salida  : std_logic_vector(3 downto 0);
begin
   -- Instancia del dispositivo a probar
   DUT : BCD2Aiken
      port map(A => entrada, B => salida);
   -- Proceso secuencial que realiza las pruebas
   pruebas:
   process
   begin
      report "Probando el conversor de BCD a Aiken" severity note;
      -- Primer valor de entrada
      entrada <= "0000";
      -- Esperamos a que se propague a la salida
      wait for 1 ns;
      -- Verificamos que el valor sea el esperado
      assert salida="0000" report "Falla para 0000" severity failure;
      -- Segundo valor
      entrada <= "0100";
      wait for 1 ns;
      assert salida="0100" report "Falla para 0100" severity failure;
      -- Los otros valores
      entrada <= "0101";
      wait for 1 ns;
      assert salida="1011" report "Falla para 0101" severity failure;
      entrada <= "1001";
      wait for 1 ns;
      assert salida="1111" report "Falla para 1001" severity failure;
      entrada <= "1010";
      wait for 1 ns;
      assert salida="XXXX" report "Falla para 1010" severity failure;
      -- Fin de las pruebas
      report "Prueba exitosa!" severity note;
      wait;
   end process pruebas;
end architecture Simulador; -- Entity: T_B2A_1

Este ejemplo es un tanto reiterativo, por lo que una forma de hacerlo más compacto es la que se muestra en el Ejemplo 5-3. En este caso los estímulos y los valores esperados se encuentran almacenados en vectores.

Ejemplo 5-3. Prueba de extremos del conversor, uso de vectores (t_b2a_2.vhdl)

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

entity T_B2A_2 is
end entity T_B2A_2;

architecture Simulador of T_B2A_2 is
   -- Declaración del componente a probar
   component BCD2Aiken is
      port(
         A  : in  std_logic_vector(3 downto 0);
         B  : out std_logic_vector(3 downto 0));
   end component BCD2Aiken;
   type vector is array (1 to 5) of std_logic_vector(3 downto 0);
   -- Estímulos
   constant ESTIMULO   : vector:=("0000","0100","0101","1001","1010");
   -- Valores esperados
   constant REFERENCIA : vector:=("0000","0100","1011","1111","XXXX");
   -- Señales auxiliares
   signal entrada : std_logic_vector(3 downto 0):="0000";
   signal salida  : std_logic_vector(3 downto 0);
begin
   -- Instancia del dispositivo a probar
   DUT : BCD2Aiken
      port map(A => entrada, B => salida);
   -- Proceso secuencial que realiza las pruebas
   pruebas:
   process
   begin
      report "Probando el conversor de BCD a Aiken" severity note;
      for i in 1 to 5 loop
          entrada <= ESTIMULO(i);
          wait for 1 ns;
          assert salida=REFERENCIA(i) report "Falla para "&
             integer'image(to_integer(unsigned(entrada)))
             severity failure;
      end loop;
      report "Prueba exitosa!" severity note;
      wait;
   end process pruebas;
end architecture Simulador; -- Entity: T_B2A_2

5.1.2. Ejemplo sincrónico

Como dispositivo a verificar hemos seleccionado un simple contador decimal, su implementación puede observarse en Ejemplo 5-4.

Ejemplo 5-4. Contador decimal (contador.vhdl)

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

entity Contador is
   port(
      clk   : in  std_logic;
      reset : in  std_logic;
      valor : out unsigned(3 downto 0));
end entity Contador;

architecture RTL of Contador is
   signal v : unsigned(3 downto 0);
begin
   cuenta:
   process (clk)
   begin
      if rising_edge(clk) then
         if reset='1' or v=9 then
            v <= (others => '0');
         else
            v <= v+1;
         end if;
      end if;
   end process cuenta;
   valor <= v;
end architecture RTL; -- Entity: Contador

Para su verificación hemos seleccionado comprobar las siguientes situaciones: estado inicial, una cuenta de 3, contar hasta 9 y desborde de 9 a 0. La implementación propuesta se puede observar en el Ejemplo 5-5.

Ejemplo 5-5. Verificación del contador decimal (t_contador.vhdl)

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

entity T_Contador is
end entity T_Contador;

architecture Simulador of T_Contador is
   component Contador is
      port(
         clk   : in  std_logic;
         reset : in  std_logic;
         valor : out unsigned(3 downto 0));
   end component Contador;

   constant FREQ_CLK : integer:=50; -- Frecuencia del reloj en MHz
   constant PERI_CLK : time:=1 us/FREQ_CLK;
   signal clk        : std_logic;      -- Reloj
   signal detener    : boolean:=false; -- Termina el test
   signal rst        : std_logic;      -- Pulso de reset
   signal v          : unsigned(3 downto 0); -- Valor del contador
begin
   gen_reloj:
   process
   begin
      clk <= '1', '0' after PERI_CLK/2;
      wait for PERI_CLK;
      if detener then
         wait;
      end if;
   end process gen_reloj;

   rst <= '1', '0' after PERI_CLK*3/2;

   dut : Contador
      port map(
         clk => clk, reset => rst, valor => v);

   do_test:
   process
      variable l : line;
   begin
      write(l,string'("* Comenzando el test"));
      writeline(output,l);
      wait until rst='0';
      assert v=0 report "No arrancó en 0!" severity failure;
      wait until rising_edge(clk); -- 1
      wait until rising_edge(clk); -- 2
      wait until rising_edge(clk); -- 3
      wait for 1 fs; -- Esperar a que el tiempo pase!
      assert v=3 report "No cuenta bien! ("&integer'image(to_integer(v))&")"
         severity failure;
      wait until rising_edge(clk); -- 4
      wait until rising_edge(clk); -- 5
      wait until rising_edge(clk); -- 6
      wait until rising_edge(clk); -- 7
      wait until rising_edge(clk); -- 8
      wait until rising_edge(clk); -- 9
      wait for 1 fs;
      assert v=9 report "No cuenta bien! ("&integer'image(to_integer(v))&")"
         severity failure;
      wait until rising_edge(clk); -- 0 de nuevo
      wait for 1 fs;
      assert v=0 report "No vuelve a 0! ("&integer'image(to_integer(v))&")"
         severity failure;
      write(l,string'("* Todo OK!"));
      writeline(output,l);
      detener <= true;
      wait;
   end process do_test;
end architecture Simulador; -- Entity: T_Contador

En este caso se optó por informar el progreso usando la salida estándar, este método genera mensajes más claros y evita confundir mensajes informativos con errores. Las demoras de 1 fs son a los simples fines de permitir que la señal de salida se actualice. Cabe mencionar que al haber elegido el tipo unsigned para la salida del contador se han podido evitar un número importante de conversiones de tipo, obteniéndose así un código mucho más simple y más entendible.

Copyright © 2011 UTN FRBA - INTI - Ing. Salvador E. Tropea