RTL 與驗證

功能覆蓋率(functional coverage)

設想你為一台自動販賣機寫了一份詳盡的測試計畫:它應當能收準確的零錢、找零、拒收外幣硬幣、處理某一格已售罄的情況、在購買途中斷了一下電後還能恢復。現在你把機器交給一名測試員,問他:「這些情況裡,你到底實際試過哪幾種?」功能覆蓋率就是回答這個問題的記分板——一份滾動累計的清單,記錄你的測試究竟真正演練過哪些設計意圖中的行為和場景,每碰到一種,就在模擬過程中逐一打勾。

說得更確切些,功能覆蓋率衡量的是你對照一份親手寫出的覆蓋模型所取得的進展:這份清單列出了一個正確的設計必須應對的那些值得關注的情形。你會宣告覆蓋點(cover point,記錄這個運算碼欄位取過的每一個值)、分桶(bins,把這些值歸併到真正要緊的幾個桶裡——最小值、最大值、溢位)以及交叉覆蓋(cross coverage,一次讀和一次寫有沒有在同一個週期裡撞上?)。當你的測試跑起來——在 UVM 驗證平台裡,往往是一大批約束隨機激勵——模擬器就記錄下哪些桶被命中了。被覆蓋到的桶所佔的百分比,就是你對「我們測夠了沒有?」這一問的誠實回答。

至關重要的一點是,它不是那個空有相似名字的「程式碼覆蓋率」的近親。程式碼覆蓋率(行、分支、翻轉)只告訴你哪些 RTL 被執行過——你可以把每一行都跑到 100%,卻從未測過那個「連續背靠背請求」的場景,而臭蟲恰恰就藏在那裡。功能覆蓋率追蹤的是意圖:那些你一開始就認定值得檢查的場景。程式碼覆蓋率高而功能覆蓋率低,意味著你只是閉著眼睛跑了一大堆程式碼;功能覆蓋率高、且各項檢查都通過,才讓一個團隊能板著臉、問心無愧地簽字放行。

covergroup cg @(posedge clk);
  cp_op    : coverpoint opcode { bins arith[] = {ADD, SUB}; bins mem[] = {LD, ST}; }
  cp_burst : coverpoint burst  { bins single = {0}; bins burst = {1}; }
  cross cp_op, cp_burst;   // did each op happen during a burst?
endgroup

一個 covergroup 在每個時脈緣取樣運算碼和叢發旗標;其中的 cross(交叉)則追問:每一種運算是否都在一次叢發傳輸期間被演練過。

「覆蓋收斂」(coverage closure)——把功能覆蓋率推到(接近)100%——是一個專案里程碑,並不等於正確性的保證。覆蓋率只告訴你某個場景被走到過;至於到了那裡之後設計行為對不對,則要由你的檢查器和斷言來裁定。一個被覆蓋到卻沒有任何檢查的桶,不過是在一頁沒人讀過的紙上打了個勾。

又稱
functional coveragefeature coveragecoverage closure (metric)功能覆盖率功能覆蓋率