歡迎您光臨本站 登入註冊首頁

編寫Testbench的一些技巧

admin @ 2014-03-26 , reply:0

概述

1Testbench的結構1)單頂層結構   一種結構是testbench只有一個頂層,頂層再把所有的模塊實例化進去。打個比方,類似樹結構,只有一個模塊有子節點而沒有父節……

1 Testbench的結構
1) 單頂層結構
    一種結構是testbench 只有一個頂層,頂層再把所有的模塊實例化進去。打個比方,類似樹結構,只有一個模塊有子節點而沒有父節點,其它模塊都有父節點。如下圖結構所示:
 
    測試模塊是一些介面模型,介面模型還可能包含了一些激勵在內。測試模塊和DUV之間通過埠映射進行互連。

2) 多頂層結構
    另外一種結構是多頂層結構,如下圖所示:
 
    在這種結構中,有一個頂層是作為測試向量模塊,一個或多個頂層是一些公用子程序,這些子程序由於完成一些通用的功能被封裝成任務、函數等被公用。
    還有一個叫harness的頂層,該頂層由DUV和一些介面模型構成一個狹義上的測試平台,其它模塊可以調用BFM裡面的 task 或 event 等,向DUV施加激勵。注意這些頂層之間是沒有埠映射的,它們之間的互相調用和訪問是通過層次路徑名的方式來訪問,上圖的虛線表示層次路徑名的訪問。下面舉例說明層次路徑是如何訪問的。
    由於大部分人對C都有所認識,在這裡作個比較,便於了解。Verilog HDL的頂層類似於C的結構體,而實例化的模塊、任務、函數、變數等就是結構體里的成員,可以通過句點( . )隔開的方式訪問結構體裡面的每一個成員。如:頂層 harness 實例化進來的模塊 BFM1 裡面有一個任務SEND_DATA , 該任務可以產生激勵輸入到DUV,在 testcase 里調用該任務就可寫為:
initial
begin
  ……
  harness . BFM1 . SEND_DATA ( …… ) ;
end
    多頂層結構的可擴展和重用性比單頂層結構強得多。層次路徑的訪問方式非常有用,在下一節會講述更多的應用。

2 如何編寫Testbench
1) 何時使用initial和always
    initial和always 是2個基本的過程結構語句,在模擬的一開始即開始相互并行執行。通常被動的檢測響應使用always語句,而主動的產生激勵使用initial語句。
    initial和always的區別是always 語句不斷地重複執行,initial語句則只執行一次。但是,如果希望在initial里的多次運行一個語句塊,怎麼辦?這時可以在initial里嵌入循環語句(while,repeat,for,forever 等),如:
initial
begin
forever /* 無條件連續執行*/
begin
  ……
  end
end
    其它循環語句請參考一些教材,這裡不作贅述。
    另外,如果希望在模擬的某一時刻同時啟動多個任務,可以使用fork....join語句。例如,在模擬開始的 100 ns 后,希望同時啟動發送和接收任務,而不是發送完畢后再進行接收,如下所示:
initial
begin
  #100 ;
  fork /*并行執行 */
    Send_task ;
    Receive_task ;
  join
End

2) 如何作多種工作模式的遍歷測試
    如果設計的工作模式很多,免不了做各種模式的遍歷測試,而遍歷測試是需要非常大的工作量的。我們經常遇到這樣的情況:很多時候,各種模式之間僅僅是部分寄存器配置值的不同,而各模式間的測試都是雷同的。有什麼方法可以減輕這種遍歷測試的工作量?不妨試試for循環語句,採用循環變數來傳遞各種模式的配置值,會幫助減少很多測試代碼,而且不會漏掉每一種模式.
initial
begin
  for ( i = 0 ; i < m ; i = i + 1 ) /*遍歷模式1至模式m*/
    for ( j = 0 ; j < n ; j = j +1 ) /*遍歷子模式1至子模式n */
    begin
      case ( j ) /* 設置每種模式所需的配置值 */
      0 : 配置值 = a ;
      1 : 配置值 = b ;
      2 : 配置值 = c ;
      ……
      endcase
/*共同的測試向量*/
    end
end

3) 如何加速問題定位過程
    在這部分里,通過一些實際例子,介紹在出現問題時如何藉助 testbench 加快問題的定位過程。
1、監測內存分配
 
內存分配和回收示意圖
    在這個例子里,假設總共有2K的內存塊,希望在測試程序里監測內存分配和回收的塊號是否正確,監測是否存在同一塊號重複分配、重複回收的情況。設置一個2K位的變數對內存的使用情況進行記錄,每一位對應一個內存塊,空閑的塊號記為1,被佔用的塊號記為0。該變數的初始值為全1,當分配一個塊號出去時先判斷該位是否為空閑,若是空閑則將該位設置為被佔用,否則就為重複分配錯誤。相反,當回收一個塊號時,先判斷該位是否被佔用,若是被佔用則將該位設置為空閑,否則就為重複回收錯誤。程序如下:
always @(posedge Clk or negedge Rst )
begin
  if ( Rst == 1'b0 )
    Mem_status <= 2048 {1'b1} ;
  else
  begin
    if ( 層次路徑 . rd ) /* 監測內存分配,block_rd 是分配的內存塊號*/
      if ( Mem_status [ block_rd ] == 1'b1 )
        Mem_status [ block_rd ] <= 1'b0 ;
      else
      begin
        $display ( "Error! 重複分配同一內存塊! ") ;
        $stop ;
      end
    if ( 層次路徑 . wr ) /* 監測內存回收,block_wr 是回收的內存塊號*/
      if ( Mem_status [ block_wr ] == 1'b0 )
        Mem_status [ block_wr ] <= 1'b1 ;
      else
      begin
      $display ( "Error! 重複回收同一內存塊! ") ;
      $stop ;
      end
    end
End

2、監測內部介面
    如果你是位驗證工程師,在做晶元級的模擬時,相信你會或曾遇到過這樣的問題:在一個埠輸入了激勵數據,但另一埠卻得不到正確的響應,而且這條路徑涉及到很多模塊和很多個不同設計者,為了定位問題,你可能很盲目地逐個找來設計人員,逐個模塊地記錄模擬波形,到解決問題時,可能幾天已經過去了。
    我們都知道,如果問題定位在越小的範圍,就越便於解決問題。所以,我們可以把模塊介面間交換的數據記錄到文件里,當出現問題時,就可以查看各介面的記錄數據,看問題到底出現在哪個區間,簡單地查看記錄文件后,你就明確該找那位designer來解決問題。

3、記錄有用的DEBUG信息
     記錄有用的debug信息,輸出到標準的I/O設備上(屏幕或文件),會給你的debug帶來很大的便利,由上面的例子也可見一斑,在檢測到有錯誤時也可使用$stop令模擬停下來。
    值得注意的是,UNIX系統只有32個I/O,每個輸出文件佔用1個I/O設備號,其中第1個是屏幕顯示,設備號是32'b1,其它I/O設備號由輸出文件佔用,一個信息可同時輸出到屏幕和文件,如:
initial
begin
  Ptr_log = $fopen ("log.txt ") ; /* 創建一個文件,獲得文件指針 */
  Ptr_log = Ptr_log | 32'b1 ; /* 指針同時指向 log.txt 文件和屏幕 */
end
always @(……)
begin
  $fwrite ( Ptr_log, "useul message ",……) ; /*信息除了記錄到文件同時,還顯示到屏幕*/
  ……
end
    雖然記錄文件會給debug帶來很多便利,但文件操作會降低模擬的速度,因此應當適可而止。
    另外寫文件通常有2種方式,不同的模擬工具有所差異。一種是每寫一個位元組打開關閉一次文件,如Verilog-XL。另一種是先把字元暫存到內存,等累積到一定數量(如8K位元組)后再通過DMA方式把字元從內存寫到文件,如Verilog-NC。因此,后一種方式就大大地降低了文件的操作次數,有利於提高模擬速度。

[admin via 研發互助社區 ] 編寫Testbench的一些技巧已經有4406次圍觀

http://cocdig.com/docs/show-post-43025.html