58 / 63 · Concepto
Plan de verificación de FIFO y memoria con un scoreboard independiente
Use una cola de referencia independiente de los punteros y planifique la comprobación de fronteras, reset, valid y lecturas de posiciones sin inicializar.
Las lecciones se pueden leer gratis. Inscríbete para guardar el progreso.
Usar un modelo de referencia distinto del DUT
Copiar rptr y wptr en el testbench puede reproducir el mismo error de punteros. Un scoreboardScoreboard Estado de referencia del entorno de verificación que compara, en orden, los datos esperados con las salidas reales. Se calcula de forma independiente, sin copiar el estado interno de la implementación. independiente usa una cola de SystemVerilog para conservar el orden lógico: un push aceptado añade al final y un pop aceptado retira la cabecera.
El plan de cobertura cruza vacío/intermedio/lleno con ninguna solicitud/solo push/solo pop/ambas. Añada vueltas repetidas de los punteros, extracciones continuas y reset con datos pendientes. Registre la cobertura, la semilla y las condiciones de cualquier fallo.
Calcule la aceptación a partir del tamaño anterior N del modelo, sin depender de full ni de la ocupación que informa el DUT:
Retire y guarde la cabecera anterior antes de añadir el nuevo dato. Así se representa correctamente el reemplazo cuando la FIFO está llena. Compare dout cuando el q esperado sea 1.
Testbench completo
Compile este testbench junto con el fifo4 de la unidad anterior. La cola y los retardos pertenecen al entorno de verificación. Las entradas se aplican en el flanco de bajada; las comprobaciones se realizan 1 ps después del flanco de subida, una vez procesadas las asignaciones no bloqueantesAsignación no bloqueante Asignación escrita con <= en RTL secuencial. Evalúa el lado derecho y programa la actualización, permitiendo que los registros del mismo flanco calculen a partir del estado anterior. Más información.
module tb_fifo4;
timeunit 1ns;
timeprecision 1ps;
typedef logic [7:0] word_t;
logic clk=0, rst=1, push=0, pop=0;
word_t din=0, dout;
logic dout_valid, full, empty;
logic [2:0] occupancy;
word_t model[$], expected;
bit expect_pop, expect_push;
fifo4 dut (.*);
always #5ns clk = ~clk;
always @(posedge clk) begin
if (rst) begin
model.delete();
expect_pop = 0;
end else begin
expect_pop = pop && (model.size() != 0);
expect_push = push && ((model.size() < 4) || expect_pop);
if (expect_pop) expected = model.pop_front();
if (expect_push) model.push_back(din);
end
#1ps;
assert (dout_valid === expect_pop)
else $fatal(1, "valid mismatch");
if (expect_pop)
assert (dout === expected)
else $fatal(1, "data mismatch");
assert (int'(occupancy) === model.size())
else $fatal(1, "count mismatch");
assert (empty === (model.size() == 0))
else $fatal(1, "empty mismatch");
assert (full === (model.size() == 4))
else $fatal(1, "full mismatch");
end
task automatic step(input bit r, p, o, input word_t d);
@(negedge clk);
rst=r; push=p; pop=o; din=d;
endtask
initial begin
step(0,0,1,0);
step(0,1,1,8'hA0);
for (int i=1; i<4; i++)
step(0,1,0,word_t'(160+i));
step(0,1,0,8'hEE);
step(0,1,1,8'hB0);
repeat (5) step(0,0,1,0);
repeat (3) begin
for (int i=0; i<4; i++) step(0,1,0,word_t'(i));
repeat (4) step(0,0,1,0);
end
step(0,1,0,8'h5A);
step(1,1,1,8'hFF);
step(0,0,1,0);
repeat (100)
step(0,bit'($urandom_range(1,0)),bit'($urandom_range(1,0)),
word_t'($urandom));
repeat (4) step(0,0,1,0);
@(negedge clk);
$display("PASS");
$finish;
end
endmoduleEl primer flanco de subida con rst=1 vacía tanto el DUT como el modelo. === compara estrictamente e incluye los valores X. No se compara dout si es inválido, pero un valid desconocido sí produce un fallo. Los cuatro pops finales comprueban los elementos restantes. PASS solo se imprime si la ejecución real supera todas las aserciones; mostrar este ejemplo no equivale a haberlo ejecutado.
Antes de añadir B0, se conserva A0, la cabecera anterior, como dato esperado. Se modela el orden lógico de la cola, sin reproducir sus posiciones físicas ni sus punteros.
- Cola anterior: [A0, A1, A2, A3]
- Valor esperado del pop: Guarde la cabecera anterior A0
- Aplique push: [A1, A2, A3, B0]
- Compare la respuesta: Con valid=1, dout=A0
Revisar a mano las fronteras del scoreboard
Con la cola [A0,A1,A2,A3], un push de EE sin pop se rechaza. Ante push de B0 y pop simultáneos, se espera A0 y la cola queda [A1,A2,A3,B0]. Si el DUT devuelve el nuevo B0, falla la aserción de datos. Con la cola vacía y ambas solicitudes, se espera pop=0, push=1: no se compara la salida y queda un elemento almacenado. El reset ignora ambas solicitudes, vacía la cola y exige valid=0.
Para una memoria direccionada, use un array de referencia y marcas de inicialización. Con read-first, determine la lectura esperada antes de escribir en el array de referencia. En un banco de registrosRegistro Circuito que almacena varios bits de estado. Los registros síncronos de este curso capturan la entrada especificada en el flanco de reloj. Más información con bypass, elija wr_data si hay coincidencia y después actualice el array. La precondición debe prohibir las lecturas sin inicializar o permitir un resultado indefinido; no invente un valor esperado de 0. La memoria direccionada necesita su propio comprobador.
Introduzca fallos deliberados: devolver el dato nuevo al extraer de una FIFO llena, incrementar la ocupación con ambas operaciones o activar valid al hacer pop de una FIFO vacía. Cada uno debe hacer fallar la comprobación correspondiente. Estas mutaciones y la cobertura de fronteras revelan huecos en las pruebas. La correspondencia con RAM física y el cumplimiento temporal requieren comprobaciones separadas de síntesis y temporización.
Inténtalo tú
Un scoreboard incorrecto añade primero el dato de push y luego decide si acepta pop usando el tamaño ya actualizado. ¿Qué espera erróneamente cuando la FIFO vacía recibe push y pop simultáneos? Con la FIFO llena, ¿cuándo debe guardar el dato esperado? Evalúe también una comprobación que exige siempre dout=0 después del reset.
Leer la explicación
Al estar vacía, el scoreboard incorrecto supone que puede extraer inmediatamente el dato recién añadido y espera expect_pop=1. El contrato usa el tamaño anterior y acepta solo push, por lo que valid=0. Al estar llena, debe guardar y retirar la cabecera anterior antes de añadir el dato nuevo. El reset no borra dout: se comprueban valid=0, la cola vacía y la ocupación, no un dato igual a 0. El dato se compara solo cuando expect_pop=1, pero valid del DUT también debe comprobarse por separado; de lo contrario, podría pasar inadvertido un DUT que mantiene valid siempre en 0.