Programación en VHDL/Bancos de pruebas
En VHDL es posible describir modelos para la simulación. Estos modelos no tienen demasiadas restricciones, necesitando únicamente un intérprete de las instrucciones VHDL. En cambio, en la síntesis se añaden una cantidad de restricciones como pueden ser aquellas que tienen que ver con las del tiempo, ya que no es posible aplicar retardos a la hora de diseñar un circuito.
Retrasos
[editar]El retraso es uno de los elementos más importantes de la simulación, puesto que el comportamiento de un circuito puede cambiar dependiendo del cambio de las diferentes señales. Cuando se realiza una asignación se produce de forma inmediata, puesto que no se ha especificado ningún retraso. Este comportamiento puede ser alterado mediante la opción AFTER cuando se asigna un valor a una señal. Su sintaxis corresponde a la siguiente línea.
señal <= valor AFTER tiempo;
Donde tiempo es un valor de tiempo indicado en us, ns, ms, ... Un ejemplo puede ser el siguiente.
rst <= '0' AFTER 15 ns;
Pero esta sentencia es mucho más compleja, por ejemplo se puede asignar inicialmente un valor y modificarlo posteriormente o incluso que sea modificado cada cierto tiempo.
signal clk : std_logic := '0';
....
rst <= '1', '0' AFTER 15 ns; -- Inicialmente rst=1, despues de 15 ns rst=0
clk <= not clk AFTER 5 ns; -- Cada 5 ns clk cambia de valor
También es posible introducir una espera entre dos sentencias mediante la palabra reservada WAIT. La sintaxis de esta operación es más compleja que la anterior.
WAIT [ON lista | UNTIL condicion | FOR tiempo];
Lista corresponde a una lista de sensibilidad de señales, es decir, se permanecerá en espera hasta que se produzca un cambio en alguna de las señales de la lista. Condición se trata de una espera indeterminada hasta que la condición sea verdadera. Por último, el tiempo es un valor definido en una unidad de tiempo. A continuación se muestra un ejemplo de las tres esperas posibles.
-- WAIT ON
stop <= '1';
WAIT ON semaforo; -- Hasta que semaforo no cambie se permanecerá en el WAIT
stop <= '0';
....
-- WAIT UNTIL
ack <= '1';
WAIT UNTIL clk'event and clk = '1'; -- Hasta que no exista un evento de clk y sea uno se permanece en WAIT
ack <= '0';
....
-- WAIT FOR
start <= '0';
WAIT FOR 50 ns; -- Se espera 50 ns
start <= '1';
Niveles lógicos
[editar]Normalmente existen tres niveles lógicos: 0, 1 y X, donde éste último se refiere a un valor desconocido (puede estar a alto o bajo nivel, no se sabe cuál de los dos).
También existen las llamadas fuerzas. S equivale a la salida obtenida de una conexión a alimentación o a tierra a través de un transistor. R es idéntico al anterior, pero su obtención es a través de una resistencia. Z también es igual a las anteriores, pero esta vez es cuando la señal es obtenida mediante una alta impedancia. Por último, se encuentra I, que indica que no se sabe qué fuerza existe en el bus.
El tipo std_logic amplía estas fuerzas, incluyendo U y -. La primera indica que una señal no ha sido inicializada y la segunda que no importa el valor que se ponga.
Notificaciones
[editar]En la simulación de circuitos es interesante el uso de las notificaciones, gracias a ellas se pueden advertir que señales han sido activadas o incluso comprobar si una señal ha tomado un valor determinado. El uso de notificaciones se realiza mediante ASSERT, seguida de una condición como elemento de activación. La sentencia puede utilizarse tanto en entornos concurrentes como en serie.
ASSERT <condición>
[REPORT <expresión>]
[SEVERITY <expresión>];
Si la condición no se cumple aparecerá en la pantalla del simulador el mensaje que se ha especificado y el nivel de gravedad, ambos son opcionales y en el caso de no indicar ningún mensaje aparecerá "Assertion Violation". Los diferentes niveles de gravedad pueden ser (de menor a mayor): note, warning, error (por defecto) y failure. Dependiendo del nivel de gravedad la simulación puede detenerse.
A continuación se muestran una serie de ejemplos de esta sentencia.
ASSERT adrr = X"00001111";
....
ASSERT addr = X"10101010" REPORT "Direccion Erronea";
....
ASSERT addr > X"00001000" and addr < X"00002000" REPORT "Direccion correcta" SEVERITY note;
....
ASSERT addr < X"00001000" and addr > X"00002000" REPORT "Direccion incorrecta" SEVERITY warning;
Descripción de un banco de pruebas
[editar]Una de las partes más importantes en el diseño de cualquier sistema son las pruebas para la verificación del funcionamiento de un sistema. Con las metodologías tradicionales la verificación sólo era posible tras su implementación física, lo que se traducía en un alto riesgo y coste adicional. Lo más sencillo es cambiar las entradas para ver cómo son las salidas, en una herramienta de simulación, siempre que su diseño sea sencillo, en caso contrario lo más cómodo sería crear un banco de pruebas.
Un banco de pruebas es una entidad sin puertos, cuya estructura contiene un componente que corresponde al circuito que se desea simular y la alteración de las diferentes señales de entrada a dicho componente, para poder abarcar un mayor número de casos de prueba. Es recomendable realizar la descripción del banco de pruebas de un sistema a la vez que se describe su diseño. Las siguientes líneas muestra la sintaxis de un banco de pruebas.
ENTITY nombre_test IS
END nombre_test;
ARCHITECTURE test OF nombre_test IS
-- Declaraciones
BEGIN
-- Cuerpo de las pruebas
END test;
A continuación se muestran las diferentes metodologías que se pueden llevar a cabo para la realización de un banco de pruebas. Para su explicación se utilizará un ejemplo de un diseño muy sencillo, donde un vector es rotado un bit hacia la izquierda o derecha dependiendo de la entrada met, su entidad corresponde al siguiente trozo de código.
ENTITY round IS
clk, rst, met : IN std_logic;
e : IN std_logic_vector(3 DOWNTO 0);
s : OUT std_logic_vector(3 DOWNTO 0)
END round;
Método tabular
[editar]Para verificar la funcionalidad de un diseño se debe elaborar una tabla con las entradas y las respuestas que se esperan a dichas entradas. Todo ello se deberá relacionar mediante código VHDL. Las siguientes líneas muestran un ejemplo con el diseño que se expuso anteriormente.
USE work.round;
ENTITY test_round IS
END test_round;
ARCHITECTURE test OF test_round IS
SIGNAL clk, rst, met : std_logic;
SIGNAL e, s : std_logic_vector(3 DOWNTO 0);
TYPE type_test IS RECORD
clk, rst, met : std_logic;
e, s : std_logic_vector(3 DOWNTO 0);
END RECORD;
TYPE lista_test IS ARRAY (0 TO 6) OF type_test;
CONSTANT tabla_test : lista_test :=(
(clk=>'0', rst =>'1', met=>'0', e=>"0000", s=>"0000"),
(clk=>'1', rst =>'0', met=>'0', e=>"0000", s=>"0000"),
(clk=>'1', rst =>'0', met=>'0', e=>"0001", s=>"0010"),
(clk=>'1', rst =>'0', met=>'0', e=>"1010", s=>"0101"),
(clk=>'1', rst =>'0', met=>'1', e=>"0000", s=>"0000"),
(clk=>'1', rst =>'0', met=>'1', e=>"0001", s=>"1000"),
(clk=>'1', rst =>'0', met=>'1', e=>"1001", s=>"1100"));
BEGIN
r : ENTITY work.round
PORT MAP(clk => clk, rst => rst, met => met, e => e, s => s);
PROCESS
VARIABLE vector : type_test;
VARIABLE errores : boolean := false;
BEGIN
FOR i IN 0 TO 6 LOOP
vector := tabla_test(i);
clk<=vector.clk; rst<=vector.rst; met<=vector.met; e<=vector.e;
WAIT FOR 20 ns;
IF s /= vector.s THEN
ASSERT false REPORT "Salida incorrecta" SEVERITY error;
errores:=true;
END IF;
END LOOP;
ASSERT errores REPORT "Test OK" SEVERITY note;
WAIT;
END PROCESS;
END test;
Uso de ficheros (vectores de test)
[editar]En el caso anterior los casos de prueba y el código de simulación permanecían juntos, pero es posible separarlos de forma que, por un lado se encuentren las pruebas y por otro el código. Esto es posible ya que VHDL dispone de paquetes de entradas/salida para la lectura/escritura en ficheros de texto, como ya se comentó el paquete textio dispone de los subprogramas necesarios para el acceso a dichos ficheros.
Supóngase los casos de prueba desarrollados en el caso anterior, en el siguiente fichero de texto se han escrito los vectores de prueba:
clk rst met e s 0 1 0 0000 0000 1 0 0 0001 0010 1 0 0 1010 0101 1 0 1 0000 0000 1 0 1 0001 1000 1 0 1 1001 1100
A continuación se muestra el código relacionado con la simulación, en él se incluye el acceso al fichero anterior que contiene los diferentes vectores de test.
USE std.textio.ALL; -- No es necesario porque se incluye por defecto
USE work.round;
ENTITY test_round IS
END test_round;
ARCHITECTURE test OF test_round IS
SIGNAL clk, rst, met : std_logic;
SIGNAL e, s : std_logic_vector(3 DOWNTO 0);
BEGIN
r : ENTITY work.round
PORT MAP(clk => clk, rst => rst, met => met, e => e, s => s);
PROCESS
FILE vector_test : text OPEN read_mode IS "test.txt";
VARIABLE errores : boolean := false;
VARIABLE vector : line;
VARIABLE clk_tmp, rst_tmp, met_tmp : std_logic;
VARIABLE e_tmp, s_tmp : std_logic_vector(3 DOWNTO 0);
BEGIN
readline(vector_test,vector); -- Lee los nombres (la primera linea)
WHILE NOT endfile(vector_test) LOOP
readline(vector_test,vector);
read(vector,clk_tmp);
read(vector,rst_tmp);
read(vector,met_tmp);
read(vector,e_tmp);
read(vector,s_tmp);
clk <= clk_tmp; rst <= rst_tmp; met <= met_tmp; e <= e_tmp;
WAIT FOR 20 ns;
IF s_tmp /= s THEN
ASSERT false REPORT "Salida incorrecta" SEVERITY error;
errores:=true;
END IF;
END LOOP;
file_close(vector_test);
ASSERT errores REPORT "Test OK" SEVERITY note;
WAIT;
END PROCESS;
END test;
Metodología algorítmica
[editar]Existe otro tipo de test, los cuales se basan en realizar algoritmos para cubrir el mayor número de casos posibles. A continuación se muestra un ejemplo, el cual aplica esta metodología.
USE work.round;
ENTITY test_round IS
END test_round;
ARCHITECTURE test OF test_round IS
SIGNAL clk : std_logic := '0';
SIGNAL rst, met : std_logic;
SIGNAL e, s : std_logic_vector(3 DOWNTO 0);
BEGIN
clk <= NOT clk after 10 ns;
r : ENTITY work.round
PORT MAP(clk => clk, rst => rst, met => met, e => e, s => s);
PROCESS
BEGIN
rst <= '1'; met <= '0'; e <= "0000";
WAIT FOR 20 ns;
ASSERT (s="0000") REPORT "Error en reset" SEVERITY error;
rst <= '0';
e <= "0000";
WAIT FOR 20 ns;
ASSERT (s="0000") REPORT "Error desplazamiento izquierda" SEVERITY error;
e <= "0001";
WAIT FOR 20 ns;
ASSERT (s="0010") REPORT "Error desplazamiento izquierda" SEVERITY error;
e <= "1010";
WAIT FOR 20 ns;
ASSERT (s="0101") REPORT "Error desplazamiento izquierda" SEVERITY error;
met <= '1';
e <= "0000";
WAIT FOR 20 ns;
ASSERT (s="0000") REPORT "Error desplazamiento derecha" SEVERITY error;
e <= "0001";
WAIT FOR 20 ns;
ASSERT (s="1000") REPORT "Error desplazamiento derecha" SEVERITY error;
e <= "1001";
WAIT FOR 20 ns;
ASSERT (s="1100") REPORT "Error desplazamiento derecha" SEVERITY error;
ASSERT false REPORT "Test Finalizado" SEVERITY note;
WAIT;
END PROCESS;
END test;