En este enlace podréis ver una modelización de una fifo como la solicitada en la tarea 1 mediante una cola y un pequeño banco de pruebas de algunos casos de verificación.
- Ni qué decir tiene que esa fifo no es sintetizable
- El caso 1 de verificación solo puede comprobarse el correcto comportamiento mediante la observación de una forma de ondas
- El caso 2 de verificación ya comprueba el propio código el funcionamiento y no requiere que visualice las formas de ondas. Esto se consigue con el «task» denominado «automatic leo_comprueba_vector_2pos» . Aparece por tanto el valor esperado («golden vector») que es introducido en la «task» de lectura y comprobación en la línea 7.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//ejemplo MFB bajo nivel task automatic leo_comprueba_vector_2pos; ref [7:0] datos_sal_obtenido; ref enable_lectura; ref reloj; input [7:0] datos_sal_esperado; begin @(posedge reloj); #(Thold); enable_lectura=1'b1; @(posedge reloj); #(Thold); enable_lectura=1'b0; #(T-Tsetup-Thold); assert (datos_sal_obtenido == datos_sal_esperado) else $error ("La fifo esta fallando"); end endtask |
- En esta misma task se puede ver un ejemplo sencillo de utilización de una aserción inmediata (líneas de 16 a 18). Como conozco el valor esperado y en qué instante tiene que aparecer, observo si el valor obtenido en la salida de la FIFO es el igual al esperado, y si no lo es determino que hay un error en la FIFO que estoy simulando. Por supuesto esta aserción inmediata se ejecutará tantas veces como este «task» sea llamado desde el «initial» general del banco de pruebas; pero en cualquier caso siempre en un ámbito secuencial inmediato.
Paso de argumentos por referencia
- Quizá en este ejemplo de «task» sea necesario insistir en la cuestión: ¿Qué es el paso de argumentos por referencia? Supongamos que tenemos una instanciación de la FIFO en nuestro banco de pruebas de la siguiente forma
1 2 3 4 5 6 7 8 |
FIFO #(32,8) myFIFO (.CLOCK(clk), .RESET_N(reset), .DATA_OUT(dato_salida), .DATA_IN (dato_entrada), .FULL_N(lleno) .EMPTY_N(vacio) .USE_DW(use_dw)); |
- En principio el task de lectura y comprobación podría haberse escrito como
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
task leo_comprueba_vector_2pos; input [7:0] datos_sal_esperado; begin @(posedge clk); #(Thold); read=1'b1; @(posedge clk); #(Thold); read=1'b0; #(T-Tsetup-Thold); assert (datos_salida == datos_sal_esperado) else $error ("La fifo esta fallando"); end endtask |
- Puede verse que ahora solo tenemos en la «task» un argumento (el valor esperado en la salida de la fifo que se observa en la línea 3). Puede comprobarse que el resto de señales que se leen en el interior del «task» no han sido transferidos al mismo a través de argumentos, sino que se utilizan directamente las variables y señales globales que tengo en el banco de pruebas (clk, read, dato_salida). Esto se puede hacer pero evidentemente este «task» cada vez que quiera reusarlo tendré que rescribirlo para adaptarme a los nombres que estas señales tengan en el nuevo banco de pruebas.
- Una alternativa es pasar esas variables al «task» a través de nuevos argumentos
1 2 3 |
ref [7:0] datos_sal_obtenido; ref enable_lectura; ref reloj; |
siempre teniendo en cuenta que el paso no puede ser por valor sino por referencia (por eso se utiliza la palabra reservada ref). Si yo pasara por valor con
1 2 3 |
input [7:0] datos_sal_obtenido; input enable_lectura; input reloj; |
el problema que tenemos es que estas señales cuando llamáramos al task de la forma
1 2 |
@(negedge clk); leo_comprueba_vector_2pos(dato_salida,read,clk, valor); |
tendrían en el interior del task siempre el MISMO VALOR que tenían cuando dicho task fue lanzado (en el @negedge clk previo) y permanecería su VALOR constante a través de toda la ejecución interna del task (que puede durar varios ciclos). Si yo quiero que esos argumentos sigan los cambios de las señales que se referencian, debo utilizar ref