Altifigence Academy

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 NN, а не full или occupancy DUT.

q=pop∧(N>0)p=push∧((N<4)∨q)N+=N+p−q\begin{aligned} q&=\mathrm{pop}\land(N>0)\\ p&=\mathrm{push}\land((N<4)\lor q)\\ N^{+}&=N+p-q \end{aligned}

Извлеките старую голову до добавления новых данных, чтобы сохранить правильный ответ одновременных запросов при full. Сравнивайте dout только при ожидаемом q=1q=1.

Полный тестбенч

Ниже полный тестбенч для совместной компиляции с fifo4 предыдущей лекции. Это проверочный код с задержками и queue. Входы меняются на отрицательном фронте; результат неблокирующего обновленияНеблокирующее присваивание обозначается <= в последовательностном RTL. Оно вычисляет правую часть, затем планирует обновление, позволяя регистрам на одном фронте рассчитываться по предыдущему состоянию. Подробнее DUT сравнивается через 1 ps после положительного.

SystemVerilog
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 как ожидаемый выход. Сравнивается логический порядок данных, а не указатели и физические слоты.

Порядок действий scoreboard при одновременных запросах в полном FIFO
  1. Прежняя очередь: [A0, A1, A2, A3]
  2. Ожидаемый результат pop: Сохраните прежнюю голову A0
  3. Примените push: [A1, A2, A3, B0]
  4. Сравните ответ: 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.

Выбор действует в этом браузере. Его можно изменить внизу страницы.