Altifigence Academy

58 / 63 · 概念

FIFO、記憶體驗證計畫與獨立 scoreboard

編寫不依賴實作指標的 queue scoreboard,將邊界、reset、valid 與未初始化讀取納入驗證計畫。

產生答案的模型應不同於 DUT

驗證器將契約所定的預期行為與 DUT 比較。若複製 FIFOFIFO是先進先出的佇列。滿、空,以及是否允許同時讀寫的規則,都是行為契約的一部分。 詳細內容 的 rptr、wptr 計算答案,可能重現同一指標錯誤。獨立 scoreboardScoreboard是驗證環境中的參考狀態,用來依序比對預期資料與實際輸出。應獨立計算,不能直接複製實作的內部狀態。 以 SystemVerilog queue 只保存邏輯順序:接受 push 加尾端,接受 pop 取前端。

驗證計畫列出狀態與請求交叉組合:empty/中間/full 各檢查無請求、只 push、只 pop、同時請求,再加反覆回捲、連續 pop、仍有資料時 reset。記錄各案例是否觀察過,保留亂數 seed 與失敗輸入以重現。

預期接受條件依 queue 舊大小 NN 計算,不依 DUT full 或 occupancy。

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}

先取舊 head 再接新資料,保留 full 同時請求的原頭部。只有預期 q=1q=1 時才比較 dout。

完整測試平台

以下是與上一堂 fifo4 一起編譯的完整測試平台,屬於使用延遲與 queue 的驗證程式。輸入在下降緣改變,上升緣 1 ps 後比較 DUT 的 nonblocking非阻塞賦值(nonblocking)在循序 RTL 中以 <= 表示。它先求右側運算式的值,再排程更新,讓同一邊緣的暫存器都能依據先前狀態計算。 詳細內容 更新結果。

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,但 valid 本身若為 X 就失敗。最後四次 pop 檢查全部剩餘資料。PASS 是實際執行並通過所有 assertion 的訊息,不表示此處列出的程式已經執行。

加入 B0 前,先將舊 head A0 分離成預期輸出。比較的是資料邏輯順序,不是指標或實體槽位。

FIFO 滿時同時請求,scoreboard 的處理順序
  1. 先前佇列: [A0, A1, A2, A3]
  2. pop 預期值: 保留原有 head A0
  3. 套用 push: [A1, A2, A3, B0]
  4. 比對回應: valid=1 時,dout=A0

用邊界案例核對 scoreboard

Queue=[A0,A1,A2,A3] 時若只有 push,大小為 4,捨棄 EE。接著同時請求,先保留 expected=A0,再把 B0 接尾,形成 [A1,A2,A3,B0]。若 DUT 立即輸出新 B0,資料 assertion 就失敗。Empty 同時請求則 expect_pop=0、expect_push=1,不比輸出,只存一個。Reset 期間忽略兩請求,清 queue 並檢查 valid=0。

記憶體驗證也用同原則:維護位址參考陣列與 initialized 表,read-first 在參考寫更新前取得讀預期。暫存器暫存器儲存多個位元的狀態。本課程中的同步暫存器會在時脈邊緣儲存指定的輸入。 詳細內容檔案 bypass 在碰撞時先選 wr_data,再更新參考陣列。未初始化位址讀取是禁止還是允許未定回應,須列為測試前提,不得任意拿 0 當答案。位址型記憶體需要獨立驗證器。

分別注入 full 同時 pop 卻輸出新值、雙接受時數量增加、empty pop 開 valid 等缺陷,對應檢查應失敗。透過這類審查與各邊界覆蓋找出測試遺漏。RAM 資源配置與時序需另以合成和時序驗證確認。

自己試試看

錯誤 scoreboard 先把 push 加到 queue,再用更新後大小判斷 pop。Empty 同時 push/pop 會如何誤判?Full 同時請求應何時保留預期資料?也評估每次 reset 後檢查 dout=0 的規則。

查看解說

會誤以為可立即 pop 新項目,產生 expect_pop=1。契約依邊緣前大小決定接受,因此只接受 push、輸出 valid=0。Full 時先保留舊 head 為 expected 並移除,再把新資料加到尾端。Reset 不把 dout 清零,只檢查 valid=0、queue 與 occupancy 為空。資料在 expect_pop=1 時比較,但也要獨立比較 DUT valid,才不會漏掉 valid 永遠為 0 的錯誤。

此選擇適用於本瀏覽器,隨時可從頁尾變更。