加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    •  
    • 環(huán)境構(gòu)成
    •  
    • 啟動(dòng)方式
    •  
    • 總結(jié)
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

UVM實(shí)戰(zhàn)[二]

2020/12/01
399
閱讀需 18 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

本期將講解 UVM 環(huán)境構(gòu)成和啟動(dòng)方式。主要參考資料為

[白皮書(shū)]: http://bbs.eetop.cn/thread-320165-1-1.html

[紅寶書(shū)]: http://rockeric.com/

 

環(huán)境構(gòu)成

進(jìn)行仿真驗(yàn)證的基本流程是

1. 例化 DUT

2. 產(chǎn)生并發(fā)送激勵(lì)

3. 檢測(cè)響應(yīng)

4. 檢查響應(yīng)是否正確

在驗(yàn)證環(huán)境中,產(chǎn)生并發(fā)送激勵(lì)將會(huì)交給兩個(gè)不同的類完成,即 uvm_driver 和 uvm_sequence,檢測(cè)響應(yīng)通過(guò) uvm_monitor 完成,而檢查響應(yīng)是否正確通過(guò) uvm_scoreboard。除了保證某項(xiàng)功能正確,我們還需要能夠確保 spec 中的每一項(xiàng)功能都通過(guò)測(cè)試,而衡量驗(yàn)證完備性的指標(biāo)之一就是功能覆蓋率,在我們的驗(yàn)證環(huán)境中收集功能覆蓋率的任務(wù)則交給了 conv_coverage 實(shí)現(xiàn)。

接下來(lái)將以數(shù)據(jù)從 uvm_driver 驅(qū)動(dòng)到 DUT,再?gòu)?DUT 到 uvm_monitor,再到 uvm_scoreboard 的順序講解驗(yàn)證環(huán)境的構(gòu)成。

 

接口定義

當(dāng)我們需要進(jìn)行仿真驗(yàn)證時(shí),與 DUT 的交互是一個(gè)必要的內(nèi)容,所我們首先分析 DUT 的接口,較為簡(jiǎn)單,一共有四組接口,一組寄存器配置接口,三組數(shù)據(jù)接口用于輸入特征圖、權(quán)重和偏置數(shù)據(jù)的讀取,一組數(shù)據(jù)接口用于輸出特征圖的存儲(chǔ)接口。

interface 的定義在頂層的 tb.sv 中,三組輸入數(shù)據(jù)接口可以使用同一類型的接口實(shí)現(xiàn),下列代碼中的具體內(nèi)容省略了,詳情請(qǐng)自行查看。最后一組接口用于檢測(cè)寄存器的內(nèi)容,當(dāng)前版本沒(méi)有使用寄存器模型,所以這個(gè)接口是必要的。


interface cfg_intf (input clk , input rst_n);  clocking drv_ck @(posedge clk);    default input #1ns output #1ns;  endclocking  clocking mon_ck @(posedge clk);    default input #1ns output #1ns;  endclockingendinterface
interface mem_in_intf (input clk , input rst_n);  clocking drv_ck @(posedge clk);    default input #1ns output #1ns;  endclocking  clocking mon_ck @(posedge clk);    default input #1ns output #1ns;  endclockingendinterface
interface mem_out_intf (input clk , input rst_n);  clocking mon_ck @(posedge clk);    default input #1ns output #1ns;  endclockingendinterface
interface conv_intf (input clk , input rst_n);  clocking mon_ck @(posedge clk);    default input #1ns output #1ns;  endclockingendinterface

注意在 interface 定義中,分別定義了兩個(gè)時(shí)鐘塊,一組驅(qū)動(dòng)用的時(shí)鐘塊,一組檢測(cè)用的時(shí)鐘塊,目的就是為了模擬真實(shí)的建立保持時(shí)間,時(shí)鐘塊的具體用法可以參考綠皮書(shū)的第四章內(nèi)容。

 

環(huán)境組件

有了接口定義以后,通過(guò)接口定義,我們便能夠與 DUT 交互,那么進(jìn)行交互我們需要做什么呢?

首先看整體結(jié)構(gòu),如果看不清,后臺(tái)回復(fù) UVM 結(jié)構(gòu)圖獲取 VISIO 文件。按照接口進(jìn)行分類,可以分成兩大類,一類通過(guò)接口與 DUT 實(shí)現(xiàn)交互,另外一類構(gòu)成了其他的組件,例如 checker,通過(guò)其他組件收集到的數(shù)據(jù)進(jìn)行數(shù)據(jù)比對(duì),保證 DUT 的功能正確性。

驗(yàn)證環(huán)境一共由四個(gè) PKG 組成,通過(guò)在頂層 import 導(dǎo)入:


import cfg_pkg::*;import mem_in_pkg::*;import mem_out_pkg::*;import conv_pkg::*;

 

與 DUT 直接聯(lián)系的組件

我們從 DUT 與驗(yàn)證環(huán)境的接口處開(kāi)始說(shuō)起,cfg_pkg 包含了對(duì)于寄存器進(jìn)行驗(yàn)證的組件。構(gòu)建 UVM 環(huán)境基本的幾個(gè)組件包括 uvm_driver,uvm_sequencer,uvm_monitor,uvm_agent。而 uvm_sequence_item 和 uvm_sequence 則不屬于環(huán)境的組件,他們是環(huán)境組件之間傳遞信息的載體。


package cfg_pkg;  import uvm_pkg::*;  `include "uvm_macros.svh"
  typedef enum {WR,RD,IDLE}cmd_t;
  class cfg_item extends uvm_sequence_item;  endclass : cfg_item
  class cfg_driver extends uvm_driver #(cfg_item);  endclass :cfg_driver
  class cfg_sequencer extends uvm_sequencer #(cfg_item);  endclass: cfg_sequencer
  class cfg_base_sequence extends uvm_sequence #(cfg_item);  endclass: cfg_conv_sequence
  class cfg_monitor extends uvm_monitor;  endclass: cfg_monitor
  class cfg_agent extends uvm_agent;    cfg_driver driver;    cfg_monitor monitor;    cfg_sequencer sequencer;    local virtual cfg_intf vif;  endclass:cfg_agent
endpackage

UVM 的思想之一就是要降低組件之間的耦合度,讓組件的功能更加單純。

uvm_driver 和 uvm_monitor 是距離 DUT 最近的兩個(gè)組件,可以直接與 DUT 的接口進(jìn)行互動(dòng)。

uvm_monitor 通過(guò)檢測(cè)接口上的信號(hào),轉(zhuǎn)化為數(shù)據(jù)包,如實(shí)地發(fā)送給 checker,只實(shí)現(xiàn)這一單純的功能,而對(duì)于驅(qū)動(dòng) DUT 這一功能則交給 uvm_driver。

uvm_driver 通過(guò)從 uvm_sequencer 獲取到的 uvm_sequence_item 解析出驅(qū)動(dòng)數(shù)據(jù),如實(shí)的將 uvm_sequence_item 的內(nèi)容驅(qū)動(dòng)到 DUT 的接口上,也只實(shí)現(xiàn)這一單純的功能,至于具體的激勵(lì)內(nèi)容,則通過(guò) uvm_sequencer 暴露接口給頂層環(huán)境,讓驗(yàn)證人員通過(guò) uvm_sequencer 發(fā)送激勵(lì)。

而 uvm_sequencer 的功能就更加簡(jiǎn)單了,只需要實(shí)現(xiàn)傳遞 uvm_sequence_item 即可,不需要關(guān)注其他的工作。

而 uvm_agent 中則通常會(huì)例化四個(gè)組件,uvm_driver,uvm_sequencer,uvm_monitor 和對(duì)應(yīng)的 interface。uvm_agent 的功能也非常單一,僅僅只是將對(duì)于一組接口的相關(guān)組件進(jìn)行一個(gè)打包,把他們整合起來(lái),這樣在頂層就只需要例化這一個(gè)組件即可。

mem_in_pkg 和 mem_out_pkg 的內(nèi)容整體上和 cfg_pkg 基本一致,不再贅述。

 

與 DUT 沒(méi)有直接聯(lián)系的組件

通過(guò) cfg_pkg 的內(nèi)容,我們實(shí)現(xiàn)了對(duì) DUT 的驅(qū)動(dòng)與檢測(cè),那么驅(qū)動(dòng)的內(nèi)容從何而來(lái),而檢測(cè)的數(shù)據(jù)包又要發(fā)送到哪里去呢?從前面圖中我們可以看到,除了五個(gè) agent 以外,我們還有其他的組件,包括 conv_checker,conv_coverage 和 conv_virtual_sequencer。


  class conv_checker extends uvm_scoreboard;  endclass:conv_checker
  class conv_coverage extends uvm_component;  endclass:conv_coverage
  class conv_virtual_sequencer extends uvm_sequencer;    cfg_sequencer    cfg_sqr;    mem_in_sequencer fmi_sqr;    mem_in_sequencer wt_sqr;    mem_in_sequencer bias_sqr;  endclass:conv_virtual_sequencer

conv_checker 繼承自 uvm_scoreboard,他通過(guò)前面所述的五個(gè) agent 中的 monitor,獲取 DUT 的信息,進(jìn)行數(shù)據(jù)對(duì)比檢查。在現(xiàn)在的 DUT 中,他所實(shí)現(xiàn)的功能是,在一次卷積運(yùn)算結(jié)束后,使用軟件算法直接進(jìn)行卷積運(yùn)算,然后與 DUT 的計(jì)算結(jié)果進(jìn)行對(duì)比,確保 DUT 功能正確。

UVM 并沒(méi)有預(yù)置的類用于覆蓋率收集,所以 conv_coverage 繼承自 uvm_component,成為最簡(jiǎn)單的 UVM 組件。由于除了寄存器的接口以外,其他都是簡(jiǎn)單的 sram 接口,所以只對(duì)寄存器進(jìn)行覆蓋率收集。覆蓋率組件通過(guò)覆蓋率的收集,量化功能驗(yàn)證的完備性,根據(jù)對(duì)應(yīng)的功能點(diǎn),設(shè)定對(duì)應(yīng)的覆蓋率,而功能驗(yàn)證的目的就是為了達(dá)到 100%的功能覆蓋率。

conv_virtual_sequencer 繼承自 uvm_sequencer,其本身并沒(méi)有什么功能,所以他的名字中帶有 virtual,他只是一個(gè)虛擬的 sequencer。其內(nèi)部包含了 cfg_sqr,fmi_sqr,wt_sqr 和 bias_sqr,作用就是將他們整合在一起,sequencer 就是一根數(shù)據(jù)線,uvm_sequence_item 就是傳輸?shù)臄?shù)據(jù),而 virtual_sequencer 就一個(gè)集線器或者說(shuō)一個(gè)拓展塢,把很多條數(shù)據(jù)線綁在一起。

 

conv_env

數(shù)據(jù)的驅(qū)動(dòng)由 uvm_driver 實(shí)現(xiàn),檢測(cè)由 uvm_monitor 實(shí)現(xiàn),激勵(lì)由 uvm_sequencer 傳遞,數(shù)據(jù)對(duì)比由 conv_checker 實(shí)現(xiàn),覆蓋率收集由 conv_coverage 實(shí)現(xiàn)。那么接下來(lái)就需要把這些組件全部整合在一起,成為一個(gè)驗(yàn)證環(huán)境,這便是 conv_env。在這里,我們只需要完成各個(gè)組件的例化和他們之間的連接,不要關(guān)心其他工作。


  class conv_env extends uvm_env;    cfg_agent cfg_agt;    mem_in_agent fmi_agt;    mem_in_agent wt_agt;    mem_in_agent bias_agt;    mem_out_agent fmo_agt;    conv_checker chker;    conv_coverage cvrg;    conv_virtual_sequencer virt_sqr;  endclass: conv_env

 

uvm_test

現(xiàn)在我們已經(jīng)獲得了一個(gè)針對(duì)卷積模塊的驗(yàn)證環(huán)境,那么如何開(kāi)始仿真測(cè)試?回憶前面所提到的 virtual_sequencer,我們只需要通過(guò) virtual_sequencer 對(duì)每個(gè) DUT 的接口進(jìn)行驅(qū)動(dòng),就能讓 DUT 運(yùn)轉(zhuǎn)起來(lái)。針對(duì)每一個(gè)測(cè)試,我們需要?jiǎng)?chuàng)建對(duì)應(yīng)的 uvm_test 類,然后再 uvm_test 內(nèi)通過(guò) virtual_sequencer 進(jìn)行激勵(lì)發(fā)送即可。


  class conv_base_test extends uvm_test;    conv_env env;
    task run_phase(uvm_phase phase);      phase.raise_objection(this);      this.run_top_virtual_sequence();      phase.drop_objection(this);    endtask
    virtual task run_top_virtual_sequence();    endtask
  endclass: conv_base_test

上述代碼中的 run_phase 的內(nèi)容就是在構(gòu)建環(huán)境后,整個(gè)仿真真正需要進(jìn)行的測(cè)試內(nèi)容??梢钥吹轿覀兌x了一個(gè) run_top_virtual_sequence 方法,用于運(yùn)行 virtual_sequence。

與 virtual_sequencer 對(duì)應(yīng)的,virtual_sequence 就是 virtual_sequencer 所需要傳輸?shù)膬?nèi)容,它的內(nèi)部會(huì)包括各式各樣的 sequence,針對(duì)每一個(gè) agent 發(fā)送不同的激勵(lì)。通過(guò)修改 virtual_sequence 的內(nèi)容,我們就能夠完成不同的測(cè)試用例。

 

啟動(dòng)方式

這里先不討論整個(gè)環(huán)境的樹(shù)狀結(jié)構(gòu)、連接方式和運(yùn)行機(jī)制,這些將在后續(xù)的推送中講解。

在構(gòu)建完整個(gè)環(huán)境和測(cè)試用例以后,我們就需要在頂層啟動(dòng)測(cè)試。


module tb ();logic clk;logic rst_n;    conv i_conv (); // 這里省略了端口連接,具體請(qǐng)參考實(shí)驗(yàn)代碼  // clock generation  initial begin    clk <= 0;    forever begin      #5 clk <= !clk;    end  end  // reset trigger  initial begin    #10 rst_n <= 0;    repeat(10) @(posedge clk);    rst_n <= 1;  end  import uvm_pkg::*;  `include "uvm_macros.svh"  import cfg_pkg::*;  import mem_in_pkg::*;  import mem_out_pkg::*;  import conv_pkg::*;
cfg_intf cfg_if(.*);mem_in_intf fmi_if(.*);mem_in_intf wt_if(.*);mem_in_intf bias_if(.*);mem_out_intf fmo_if(.*);conv_intf conv_if(.*);
assign conv_if.start              =i_conv.i_regfile.start              ;assign conv_if.done               =i_conv.i_regfile.done               ;assign conv_if.fmap_in_w          =i_conv.i_regfile.fmap_in_w          ;assign conv_if.fmap_in_h          =i_conv.i_regfile.fmap_in_h          ;assign conv_if.fmap_in_ch_div_32  =i_conv.i_regfile.fmap_in_ch_div_32  ;assign conv_if.k_w                =i_conv.i_regfile.k_w                ;assign conv_if.k_h                =i_conv.i_regfile.k_h                ;assign conv_if.fmap_out_w         =i_conv.i_regfile.fmap_out_w         ;assign conv_if.fmap_out_h         =i_conv.i_regfile.fmap_out_h         ;assign conv_if.fmap_out_w_div_32  =i_conv.i_regfile.fmap_out_w_div_32  ;assign conv_if.fmap_out_ch_div_32 =i_conv.i_regfile.fmap_out_ch_div_32 ;assign conv_if.pooling_bypass     =i_conv.i_regfile.pooling_bypass     ;assign conv_if.act_bypass         =i_conv.i_regfile.act_bypass         ;assign conv_if.padding_cnt        =i_conv.i_regfile.padding_cnt        ;assign conv_if.stripe             =i_conv.i_regfile.stripe             ;assign conv_if.last_pixel         =i_conv.i_regfile.last_pixel         ;assign conv_if.last_pixel_div_32  =i_conv.i_regfile.last_pixel_div_32  ;assign conv_if.fmap_out_ch        =i_conv.i_regfile.fmap_out_ch        ;assign bias_if.addr[15:8]='0;
  initial begin    uvm_config_db#(virtual mem_in_intf)::set(uvm_root::get(), "uvm_test_top", "fmi_in_vif", fmi_if);    uvm_config_db#(virtual mem_in_intf)::set(uvm_root::get(), "uvm_test_top", "wt_vif", wt_if);    uvm_config_db#(virtual mem_in_intf)::set(uvm_root::get(), "uvm_test_top", "bias_vif", bias_if);    uvm_config_db#(virtual cfg_intf)::set(uvm_root::get(), "uvm_test_top", "cfg_vif", cfg_if);    uvm_config_db#(virtual mem_out_intf)::set(uvm_root::get(), "uvm_test_top", "fmo_vif", fmo_if);    uvm_config_db#(virtual conv_intf)::set(uvm_root::get(), "uvm_test_top", "conv_vif", conv_if);    // If no external configured via +UVM_TESTNAME=my_test, the default test is    // std_test    run_test("std_test");  end
endmodule : tb

在 tb 的頂層模塊中,我們要做 5 件事:

1. 定義時(shí)鐘與復(fù)位 2. 例化 dut3. 例化與連接各個(gè) interface4. 將每個(gè) interface 句柄通過(guò) uvm_config_db 傳遞到環(huán)境中去 5. 通過(guò) run_test()方法啟動(dòng)測(cè)試

uvm_config_db 是 UVM 所提供用于傳遞數(shù)據(jù)的靜態(tài)方法,在后續(xù)的推送中將會(huì)展開(kāi)講解。這里值得注意的是,一定要在 run_test()之前實(shí)現(xiàn) uvm_config_db 傳遞,否則在 run_test()開(kāi)始后,環(huán)境內(nèi)部將無(wú)法獲取句柄,導(dǎo)致報(bào)錯(cuò)。

run_test()是 UVM 提供的測(cè)試啟動(dòng)方法,傳遞參數(shù)是一個(gè)字符串變量,該字符串將用于指定默認(rèn)的 testcase。如果在命令選項(xiàng)中,沒(méi)有通過(guò)+UVM_TESTNAME 指定具體的 TESTNAME,將會(huì)運(yùn)行默認(rèn)的 testcase。

 

總結(jié)

本次講解了驗(yàn)證環(huán)境的基本組件和構(gòu)成,以及在頂層啟動(dòng)的注意事項(xiàng)。

1. 與 DUT 直接交互的組件為 uvm_driver 和 uvm_monitor,傳遞激勵(lì)信息的組件為 uvm_sequencer,uvm_agent 將三者組合起來(lái)。

2.uvm_scoreboard 獲取各個(gè) uvm_monitor 傳遞過(guò)來(lái)的數(shù)據(jù),進(jìn)行比對(duì),保證 DUT 功能的正確性。

3.conv_coverage 用于收集覆蓋率

4.conv_virtual_sequencer 將每個(gè) uvm_agent 中的 uvm_sequencer 集中起來(lái)進(jìn)行管理,起到集線器或者說(shuō)路由器的效果

5.conv_env 將上述所有組件容納起來(lái),并且進(jìn)行連接

6.uvm_test 通過(guò)編寫(xiě) conv_virtual_sequence,經(jīng)由 conv_virtual_sequencer 發(fā)送激勵(lì)實(shí)現(xiàn)不同的 testcase

7. 在頂層實(shí)現(xiàn)各項(xiàng)例化,并且在 run_test()之前傳遞接口句柄

8. 通過(guò) run_test()啟動(dòng)測(cè)試,并且指定默認(rèn) testcase

相關(guān)推薦

電子產(chǎn)業(yè)圖譜