1. 覆蓋率類型
概述
- 覆蓋率是衡量設計完備性的一個通用詞語
- 隨着測試逐步覆蓋各種合理的組合,仿真過程會慢慢勾畫出你的設計情況
- 覆蓋率共居會在仿真過程中收集信息,然后進行后續處理並且得到覆蓋率報告
- 通過這個報告找出覆蓋之外的盲區,然后修改現有測試或者創建行動測試來填補這些盲區
- 這個過程可以一直迭代進行,直到你對覆蓋率滿意為止
覆蓋率反饋回路
note:定向測試和隨機約束並沒有嚴格界線,隨機約束很窄的時候也可以看作定向測試
- 可以使用一個反饋回路來分析覆蓋率的結果,並決定采取哪種行動來達到100%覆蓋率
- 首要的選擇是使用更多的種子來運行現有的測試程序
- 當大量種子依然對於覆蓋率增長沒有幫助時,需要建立新的約束
- 只有在確實需要的時候才會求助於創建定向測試
代碼覆蓋率
- 不添加任何額外的HDL code,共居會通過分析源代碼和增加隱藏代碼來自動完成代碼覆蓋率的統計(只收集設計代碼的覆蓋率)
- 當運行完所有測試,代碼覆蓋率工具變回創建相應的數據庫
- 放着呢起都帶有代碼覆蓋率的工具,覆蓋率數據也可以被轉換為可讀格式
- 行覆蓋率:多少行代碼已經被執行過
- 路徑覆蓋率:在穿過代碼和表達式的路徑中有哪些已經被執行過
- 翻轉覆蓋率:哪些單位比特變量的值為0或1
- 狀態機覆蓋率:狀態機哪些狀態和狀態轉換已經被訪問過
- 代碼覆蓋率最終的結果用於衡量你執行了設計中的多少代碼
- 關注點應該放在設計代碼的分析上,而不是測試平台
- 未經測試的設計代碼里可能隱藏硬件漏洞,也可能僅僅就是冗余的代碼
- 代碼覆蓋率衡量的是測試對於硬件設計描述的“實現”究竟測試得有多徹底,而非針對驗證計划
- 代碼覆蓋率達到了100%,並不意味着驗證工作已經完整,但代碼覆蓋率100%是驗證工作完備性的必要條件
note:工具只會自動收集code coverage
斷言覆蓋率
- 斷言是用於一次性或在一段時間對一個或者多個設計信號在邏輯或者時序上的聲明性代碼
- 斷言可以跟隨設計和測試平台一起仿真,也可以被形式驗證工具所證實
- 也可以使用SVA來表達
- 斷言最常用於查找錯誤,如兩個信號是否應該互斥,或者請求和許可信號之間的時序
- 一段檢測到問題,仿真應該立即停止
- 有些斷言可以用於查找感興趣的信號值或者設計狀態
- 可以使用cover property來測量這些關心的信號值或者狀態是否發生
- 在方針結束時,仿真工具可以自動生成斷言覆蓋率數據
- 斷言覆蓋率數據以及其他覆蓋率數據都會被集成在同一個覆蓋率數據庫中,verifier可對其展開分析
漏洞率曲線
- 在一個項目實施期間,應該保持追蹤每周有多少個漏洞被發現
- 一開始,創建測試程序時,通過觀察可能就會發現很多漏洞
- 當設計逐漸穩定,需要利用自動化的檢查方式來協助發現可能的漏洞
- 在設計臨近流片時,漏洞率會下降,甚至有望為零,即便如此,驗證工作仍然不能結束
- 每次漏洞率下降時,就應該尋找各種不同的辦法去測試可能的邊界情況(corner case)
- 漏洞率可能每周都有變化,這跟很多因素都有關,不過漏洞率如果出現意外的變化可能預設着潛在的問題
功能覆蓋率
- 驗證的目的就是確保世界在實際環境中的行為正確
- spec詳細說明了設計應該如何運行,而驗證計划則列出了相應的功能應該如何激勵,驗證和測量
- 當手機測量數據希望好處那些功能已經被覆蓋時,其實就是在計算設計的覆蓋率
- 功能覆蓋率和功能誰意圖緊密相連的,有時也被稱為描述覆蓋率,而code coverage則是衡量設計的實現情況
- 某個功能在設計中可以被遺漏,code coverage不能發現這個錯誤,但是function coverage可以
note:覆蓋率不只是功能仿真才可以做,形式驗證也可以
function coverage收集的前提是testcase pass
- 每一次仿真都會產生一個帶有覆蓋率信息的數據庫,記錄隨機游走的軌跡
- 把這些信息全部合並在一起就可以得到功能覆蓋率,從而衡量整體的進展程度
- 通過分析覆蓋率數據可以決定如何修改回歸測試集
- 如果覆蓋率在穩步增長,那么添加心中或者加長測試實際即可
- 如果覆蓋率增速放緩,那么需要添加額外的約束來產生更多“有意思”的激勵
- 如果覆蓋率停止增長,然而設計某些測試點沒有被覆蓋到,那么就需要創建新的測試
- 如果覆蓋率100%但依然有行動設計漏洞,那么覆蓋率可能沒有覆蓋到設計中的某些設計功能區域
2. 功能覆蓋策略
收集信息而非數據
- 對於MCDF,需要關心的是合法的寄存器地址和非法的寄存器地址,可寫的寄存器域和非法的寄存器域,而不是具體的寄存器地址數值
- 一旦關注的地方着眼於感興趣的狀態,而不是具體數值,那么這對於如何定義功能覆蓋率,以及如何收集信息會減輕很大的負擔
- 設計信號如果數量范圍太大,就應該拆分為多個小范圍再加上邊界情況
只測需要的內容
- verifier需要懂得,在是能覆蓋率收集時,會降低很大的仿真性能
- 由於收集功能覆蓋率數據的開銷很大,所以只測量會用來分析並且改進測試的那部分數據
- 同時需要設定合理的覆蓋率采樣事件,一方面提升采樣效率,一方面也可以降低收集覆蓋率的開銷
驗證的完備性
- 完備的覆蓋率測量結果和漏洞增長曲線,可以幫助確認設計是否被完整的驗證過
- 如果代碼覆蓋率低但功能覆蓋率高,這說明驗證計划不完整,測試沒有執行設計的所有代碼
- 如果代碼覆蓋率高但功能覆蓋率低,這說明即使測試平台很好的執行了設計的所有代碼,但測試還是沒有把設計定位到所有感興趣的狀態上
- 目標是同時驅動高的代碼覆蓋率和功能覆蓋率
note:一般而言,code coverage和function coverage需要達到95%
3.覆蓋組
概述
- covergroup與類類似,一次定義后便可以多次實例化
- covergroup可以包含一個或者多個coverpoint,且全都在同一時間采集(coverpoint在特定時刻用來監測某些特定的值)
- covergroup可以定義在類中,也可以定義在interface或者module中
- covergroup可以采樣任何可見的變量,例如程序變量,接口信號或者設計端口
- 一個類里可以包含多個covergroup
- 當你擁有多個獨立的covergroup時,每個covergroup可以根據需要自行使能或者禁止
- 每個covergroup可以定義單獨的觸發采樣事件,允許從多個源頭收集數據
- covergroup必須被例化才可以用來收集數據
note:covergroup,random,contraint均可以做使能
在類中定義covergroup
class Transactor; Transaction tr; mailbox mbx_in; covergroup CovPort; coverpoint tr.port; endgroup function new(mailbox mbx_in); CovPort = new( ); this.mbx_in = mbx_in; endfunction task main; forever begin tr = mbx_in.get; ifc.cb.port <= tr.port; ifc.cb.data <= tr.data; CovPort.sample( ); end endtask endclass
coverpoint可以采樣sw,hw的變量,數值,當不定義具體數值時,采樣所有可能數值
上述代碼中,類型的名字和實例的名字一致。這種情況只能例化一次
也可以這樣例化: CovPort cg1 = new(),可例化多次
covergroup的采樣出發
- covergroup由采樣的數據和數據被采樣的事件構成
- 當這兩個條件都准備好以后,測試平台變會出發covergroup
- 這個過程可以直接使用sample()函數完成,也可以在covergroup中采樣阻塞表達式或者使用wait或@實現在信號或事件上的阻塞
- 如果你希望在code中顯式地觸發covergroup采樣,或者不存在采樣時刻的信號或事件,又或者一個covergroup被例化為多個實例需要單獨觸發,那么可以使用sample()方法
- 如果想借助已有的事件或者信號出發covergroup,可以再covergroup聲明中使用阻塞語句
- 與直接調用sample( )相比,使用事件觸發的好處在於能夠借助已有的事件
event trans_ready; covergroup CovPort @(trans_ready); coverpoint ifc.cb.port; //也可以使用posedge進行采樣 endgroup
4.數據采樣
概述
- 當你在coverpoint指定采樣一個變量或表達式時,SV會創建很多“倉(bin)”來記錄每個數值被捕捉到的次數
- 這些bin是衡量功能覆蓋率的基本單位
- covergroup中可以定義多個coverpoint,coverpoint中可以自定義多個cover bin或者SV幫助自動定義多個cover bin
- 每次covergroup采樣,SV都會在一個或者多個cover bin中留下標記,用來記錄采樣時變量的數值和匹配的cover bin
- 在仿真后,可以使用分析工具讀取這些數據庫來生成覆蓋率報告,包含了各部分和總體的覆蓋率
- 所有的coverpoint的覆蓋率最終構成一個covergroup的覆蓋率
- 所有的covergroup的覆蓋率構成了整體的功能覆蓋率
bin的創建和應用
- SV會默認為某個coverpoint創建bin,用戶也可以自己定義bin的采樣域
- 如果采樣變量的域范圍過大而沒有制定bin,那么系統會默認分配64個bin,將值域范圍平均分配給這64個bin
- 用戶可以通過covergroup的選項auto_bin_max來制定自動創建bin的最大數目
- 實際操作中,自動創建bin的方法不實用,建議用戶自行定義bin,或者減小auto_bin_max的數值
covergroup CovPort; option.auto_bin_max = 8; //所有coverpoint auto_bin數量=8 coverpoint tr.port { option.auto_bin_max = 2;} //特定coverpoint auto_bin數量=2 endgroup
命名coverpoint和bin
note:coverpoint定義使用{}而不是begin...end,大括號結尾沒有分號,和end一樣
條件覆蓋率
- 可以使用關鍵詞iff給coverpoint添加條件
- 這種做法常用於在復位期間關閉覆蓋以忽略不合理的條件觸發
- 也可以使用start和stop函數來控制covergroup各個獨立實例
covergroup CoverPort; coverpoint port iff(!bus_if.reset); endgroup initial begin CoverPort.ck =new(); #1ns; ck.stop(); bus_if.reset = 1; #100ns bus_if.reset = 0; ck.start(); ... ck.sample(); end
note : iff可以添加到covergroup,也可以添加到coverpoint
翻轉覆蓋率
- coverpoint也可以用來記錄變量從A值到B值的跳轉情況
- 還可以確定任何長度的翻轉次數
covergroup CoverPort; coverpoint port{ bins t1 = (0=>1), (0=>2), (0=>3); } endgroup
wildcard覆蓋率
- 可以使用關鍵字wildcard來創建多個狀態或者翻轉
- 在表達式中,任何x,z或者?都會被當成0或1的通配符
忽略的bin
- 在某些coverpoint可能始終無法得到全部的域值
- 對於哪些不計算功能的域值可以使用ignore_bins來排除,最終它們不會記入coverpoint的覆蓋率
非法的bin
- 有些采樣值不僅不該被忽略,而且如果出現還應該報錯
- 這種情況可以使用illegal_bins對特定的bin進行表示
交叉覆蓋率
- coverpoint記錄的是單個變量或者表達式的觀測值
- 如果像記錄在某一時刻,多個變量之間值的組合情況,需要使用交叉(cross)覆蓋率
- cross語句只允許帶coverpoint或者簡單的變量名
class Transaction; rand bit [3:0] kind; rand bit [2:0] port; enclass Transaction tr; covergroup CovPort; kind: coverpoint tr.kind; port: coverpoint tr.port; cross kind, port; endgroup
排除部分cross bin
- 通過使用ignore_bins,binsof和intersect分別指定coverpoint和值域,這樣可以清楚很多不關心的cross bin
covergroup CovPort; port: coverpoint tr.port {bins port[] = {[0:$]}; } kind: coverpoint tr.kind { bins zero = {0}; bins lo = {[1:3]}; bins hi[] = {[8:$]}; bins misc = default; } cross kind, port{ ignore_bins hi = binsof(port) intersect {7}; ignore_bins md = binsof(port) intersect {0} && binsof(kind) intersect {[9:11]}; ignore_bins lo = binsof(kind.lo) } endgroup
精細的交叉覆蓋率指定
- 隨着cross覆蓋率越來越精細,可能需要花費不少時間來指定哪些bin應該被使用或者被忽略
- 更合適的方式是自己聲明
class Transction; rand bit a,b; endclass covergroup CrossBinNames; a: coverpoint tr.a { bins a0 = {0}; bins a1 = {1}; option.weight = 0;} //不計算覆蓋率 b: coverpoint tr.b { bins b0 = {0}; bins b1 = {1}; option.weight = 0;} //不計算覆蓋率 ab: cross a,b { bins a0b0 = binsof(a.a0) && binsof (b.b0); bins a1b0 = binsof(a.a1) && binsof (b.b0); bins b1 = binsof(b.b1); } endgroup
class Transction; rand bit a,b; endclass covergroup CrossBinNames; a: coverpoint tr.a { option.weight = 0;} //不計算覆蓋率 b: coverpoint tr.b { option.weight = 0;} //不計算覆蓋率 ab: cross a,b { bins a0b0 = binsof(a) intersect {0} && binsof (b) intersect {0}; bins a1b0 = binsof(a) intersect {1} && binsof (b) intersect {0}; bins b1 = binsof(b) intersect {1}; } endgroup
5.覆蓋選項
單個實例的覆蓋率
如果對一個covergroup例化多次,那么默認情況下,SV會將所有實例的覆蓋率合並到一起,如果需要單獨列出每個covergroup實例的覆蓋率,需要設置覆蓋選項
covergroup CoverLength; coverpoint tr.length; option.per_instance = 1; endgroup
注釋
- 如果有多個covergroup實例,可以通過參數來對每一個實例傳入單獨的注釋,這些注釋最終會顯示到覆蓋率數據的總結報告中
covergroup Coverport(int lo,hi, string comment); option.comment = comment; option.per_instance = 1; coverpoint port {bins range = {[lo:hi]} } endgroup ... CoverPort cp_lo = new(0, 3, "Low port numbers"); CoverPort cp_hi = new(4, 7, "High port numbers");
覆蓋次數限定
- 默認情況下,數值采樣了1次就可以記入有效的bin,可以通過修改at_least來修改每個bin的數值最少的采樣次數,如果低於at_least數值,則不會被計入bin
- option.at_least可以再covergroup中聲明來影響所有的coverpoint,也可以在coverpoint中聲明來只影響該coverpoint下所有的bin
覆蓋率目標
一個covergroup或者一個coverpoint目標是100%覆蓋率
也可以設置為低於100%目標,option.goal = 90%
covergroup方法
- sample():采樣
- get_coverage()/get_inst_coverage():獲取覆蓋率,返回0-100的real數值
- set_inst_name(string):設置covergroup的名稱
- start()/stop():使能或者關閉覆蓋率的收集
6.數據分析
概述
- 使用$get_coverage()可以得到總體的覆蓋率
- 也可以使用covergroup_inst.get_inst_coverage()來獲取單個covergroup實例的覆蓋V領
- 這些函數最實際的用處是在一個測試當中監測覆蓋率的變化
- 如果覆蓋率水平在一段時間后沒有提高,那么這個測試就應該停止
- 重啟新的隨機種子或者測試可能有望提高覆蓋率
- 如果測試可以基於功能覆蓋率采取一些深入的行動,例如重新限定隨機約束