58 / 63 · Conceito
Plano de verificação de FIFO e memória com scoreboard independente
Use uma fila de referência independente dos ponteiros e planeje a verificação de fronteiras, reset, valid e leituras de posições não inicializadas.
As aulas podem ser lidas gratuitamente. Inscreva-se para salvar o progresso.
Usar um modelo de referência diferente do DUT
Copiar rptr e wptr para o testbench pode reproduzir o mesmo erro de ponteiros. Um scoreboardScoreboard Estado de referência do ambiente de verificação que compara, em ordem, os dados esperados com as saídas reais. É calculado de forma independente, sem copiar o estado interno da implementação. independente usa uma fila de SystemVerilog para manter a ordem lógica: um push aceito insere no final e um pop aceito retira a cabeça.
O plano de cobertura cruza vazio/intermediário/cheio com nenhuma solicitação/somente push/somente pop/ambas. Inclua várias voltas dos ponteiros, retiradas contínuas e reset com dados pendentes. Registre cobertura, semente e condições de qualquer falha.
Calcule a aceitação a partir do tamanho anterior N do modelo, sem depender de full nem da ocupação informada pelo DUT:
Retire e guarde a cabeça anterior antes de inserir o dado novo. Isso representa corretamente a substituição com a FIFO cheia. Compare dout quando o q esperado for 1.
Testbench completo
Compile este testbench junto com o fifo4 da unidade anterior. A fila e os atrasos pertencem ao ambiente de verificação. As entradas são aplicadas na borda de descida; as verificações ocorrem 1 ps após a borda de subida, depois do processamento das atribuições não bloqueantesAtribuição não bloqueante Atribuição escrita com <= em RTL sequencial. Avalia o lado direito e agenda a atualização, permitindo que os registradores da mesma borda calculem a partir do estado anterior. Saiba mais.
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
endmoduleA primeira borda de subida com rst=1 esvazia tanto o DUT quanto o modelo. === compara estritamente, incluindo valores X. Não se compara dout quando inválido, mas um valid desconhecido provoca falha. Os quatro pops finais verificam os elementos restantes. PASS só é impresso se a execução real superar todas as asserções; apresentar este exemplo não equivale a tê-lo executado.
Antes de inserir B0, guarda-se A0, a cabeça anterior, como dado esperado. O modelo representa a ordem lógica da fila, sem reproduzir suas posições físicas nem seus ponteiros.
- Fila anterior: [A0, A1, A2, A3]
- Valor esperado do pop: Guarde a cabeça anterior A0
- Aplique push: [A1, A2, A3, B0]
- Compare a resposta: Com valid=1, dout=A0
Conferir manualmente as fronteiras do scoreboard
Com a fila [A0,A1,A2,A3], um push de EE sem pop é rejeitado. Em um push de B0 e pop simultâneos, espera-se A0 e a fila passa a [A1,A2,A3,B0]. Se o DUT retornar o novo B0, a asserção de dados falha. Com a fila vazia e ambas as solicitações, espera-se pop=0, push=1: a saída não é comparada e um elemento fica armazenado. O reset ignora as duas solicitações, esvazia a fila e exige valid=0.
Para uma memória endereçada, use um array de referência e indicadores de inicialização. Com read-first, determine a leitura esperada antes de escrever no array de referência. Em um banco de registradoresRegistrador Circuito que armazena vários bits de estado. Os registradores síncronos deste curso capturam a entrada especificada na borda de clock. Saiba mais com bypass, escolha wr_data quando houver coincidência e depois atualize o array. A precondição deve proibir leituras não inicializadas ou permitir resultado indefinido; não invente um valor esperado igual a 0. A memória endereçada precisa de seu próprio verificador.
Introduza falhas deliberadas: retornar o dado novo ao retirar de uma FIFO cheia, incrementar a ocupação com as duas operações ou ativar valid ao fazer pop de uma FIFO vazia. Cada falha deve ser detectada pela verificação correspondente. Essas mutações e a cobertura das fronteiras revelam lacunas nos testes. O mapeamento para RAM física e o cumprimento temporal exigem verificações separadas de síntese e temporização.
Experimente
Um scoreboard incorreto primeiro insere o dado de push e depois decide se aceita pop usando o tamanho já atualizado. O que ele espera incorretamente quando a FIFO vazia recebe push e pop simultâneos? Com a FIFO cheia, em que momento deve guardar o dado esperado? Avalie também uma verificação que sempre exige dout=0 após o reset.
Ler a explicação
Quando a FIFO está vazia, o scoreboard incorreto supõe que pode retirar imediatamente o dado recém-inserido e espera expect_pop=1. O contrato usa o tamanho anterior e aceita somente push, portanto valid=0. Quando a FIFO está cheia, ele deve guardar e retirar a cabeça anterior antes de inserir o dado novo. O reset não apaga dout: verificam-se valid=0, a fila vazia e a ocupação, não um dado igual a 0. O dado só é comparado quando expect_pop=1, mas valid do DUT também deve ser verificado separadamente; caso contrário, um DUT que mantém valid sempre em 0 poderia passar despercebido.