58 / 63 · Теория
План проверки FIFO и памяти, независимый scoreboard
Создайте queue-scoreboard независимо от указателей реализации и включите границы, reset, valid и неинициализированные чтения в план.
Уроки доступны бесплатно. Запишитесь, чтобы сохранять прогресс.
Эталон должен отличаться от DUT
Проверяющая модель сравнивает ожидания контракта с DUT. Копирование внутренних rptr,wptr FIFOFIFO — очередь, выдающая данные в порядке их поступления. Правила заполненности, пустоты и допустимости одновременного чтения и записи входят в контракт поведения. Подробнее может повторить ту же ошибку указателей. Независимый scoreboardScoreboard — эталонное состояние среды проверки, позволяющее по порядку сопоставлять ожидаемые данные с реальными выходами. Его вычисляют независимо, не копируя внутреннее состояние реализации. хранит лишь логический порядок в SystemVerilog queue: принятый push добавляет в конец, pop извлекает начало.
В план внесите пересечения состояния и запросов: empty/середина/full × нет запросов/только push/только pop/оба. Добавьте многократный wrap-around, последовательные pop и reset с оставшимися данными. Фиксируйте наблюдение каждого случая, seed случайных тестов и сбойные входы для воспроизведения.
Ожидаемое принятие вычисляется по прежнему размеру queue , а не full или occupancy DUT.
Извлеките старую голову до добавления новых данных, чтобы сохранить правильный ответ одновременных запросов при full. Сравнивайте dout только при ожидаемом .
Полный тестбенч
Ниже полный тестбенч для совместной компиляции с fifo4 предыдущей лекции. Это проверочный код с задержками и queue. Входы меняются на отрицательном фронте; результат неблокирующего обновленияНеблокирующее присваивание обозначается <= в последовательностном RTL. Оно вычисляет правую часть, затем планирует обновление, позволяя регистрам на одном фронте рассчитываться по предыдущему состоянию. Подробнее DUT сравнивается через 1 ps после положительного.
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
endmoduleПервый положительный фронт с rst=1 опустошает DUT и модель. === строго сравнивает и X. При valid=0 неинициализированный dout не сравнивается, но X в самом valid вызывает ошибку. Четыре последних pop проверяют все оставшиеся данные. PASS — сообщение реального запуска, прошедшего все assertions, а не утверждение, что приведённый здесь код уже исполнялся.
До добавления B0 отделяется прежняя голова A0 как ожидаемый выход. Сравнивается логический порядок данных, а не указатели и физические слоты.
- Прежняя очередь: [A0, A1, A2, A3]
- Ожидаемый результат pop: Сохраните прежнюю голову A0
- Примените push: [A1, A2, A3, B0]
- Сравните ответ: dout=A0 при valid=1
Ручная проверка scoreboard на границах
При queue=[A0,A1,A2,A3] одиночный запрос push с EE отклоняется из-за размера 4. Следующие одновременные запросы сначала сохраняют expected=A0, затем добавляют B0, получая [A1,A2,A3,B0]. Если DUT немедленно выдаст новый B0, assertion данных сработает. При empty с двумя запросами expect_pop=0, expect_push=1: сохраняется один элемент без сравнения выхода. Во время reset оба запроса игнорируются, очередь очищается, проверяется valid=0.
Для памяти принцип тот же: эталонный массив по адресам и таблица initialized. При read-first ожидание чтения берут до эталонной записи. Bypass регистровогоРегистр хранит многобитное состояние. Синхронные регистры в этом курсе записывают определённый вход на фронте тактового сигнала. Подробнее файла сначала выбирает wr_data при коллизии, затем обновляет массив. В предпосылках теста укажите, запрещены ли чтения неинициализированных адресов или допускается неопределённый ответ; не изобретайте эталонный 0. Адресной памяти нужен отдельный проверяющий модуль.
Поочерёдно внесите дефекты: новый выход при full+pop, увеличение числа при обоих принятиях, valid для empty-pop. Соответствующие проверки должны падать. Такой анализ и покрытие границ находят пропуски самого теста. Размещение RAM и временные характеристики проверяются отдельно, с помощью синтеза и временного анализа.
Попробуйте сами
Ошибочный scoreboard сначала добавляет push в queue, затем решает допустимость pop по новому размеру. В чём ошибка при одновременных запросах на empty? Когда сохранять ожидаемые данные при full? Оцените также постоянную проверку dout=0 после reset.
Прочитать объяснение
На empty модель ошибочно разрешит немедленный pop нового элемента и выставит expect_pop=1. По контракту принятие определяется размером до фронта: принимается только push, valid=0. На full сначала сохраняют прежнюю голову в expected и удаляют её, затем добавляют новые данные. Reset не обнуляет dout: проверяют valid=0 и пустые queue/occupancy. Данные сравнивают при expect_pop=1, а valid DUT — отдельно, чтобы не пропустить ошибку постоянно нулевого valid.