5.小白學uvm驗證 - squence機制


  在第一節中我們提過,為什么不把 transaction 實例化、隨機和驅動全部放入 driver 中完成,我們驗證的主要工作量,除了搭建驗證環境之外,還有一大工作-拼湊場景case,其中不同場景中 transaction 的發送給數量和組織形式各有差異,我們如果把 transaction 放入driver 中,就需要經常改動 driver ,如果將驅動協議部分放入 driver 中,而在 sequnce 中完成 transaction 的實例化和隨機化任務,我們創建新 case 時僅需要新建一個 sequence 即可。
  上述過程如下圖所示:

  采用 sequence + sequencer + driver 實現驅動有以下優勢:

a.sequence 、sequencer 和driver 三者之間分工合作,任務明確
b.driver 僅實現依據驗證 Spec 實現驅動協議部分,可以重用
c.sequence 可以在上層對 transaction 進行管理組織
d.具有更好的重用性-不同的 sequence 實現不同的場景
e.具有更好的管控性如控制 sequence 的執行順序及其不同 sequnece 之間的同步

1.什么是 sequence

  上文提到,子彈,彈夾和槍的比喻,想必大家都有映像,sequence 就像一個彈夾,里面裝了很多"子彈",而這里的"子彈"就是 transaction, 彈夾可長可短,只要子彈型號一致(同一種 transaction),就能通過 sequencer 和 driver 把數據驅動給 DUT。在下圖中,有三個 sequence 掛載於同一個 sequncer 上(當然也可不同時間掛在其中一個 sequence),通過 driver 進行數據驅動,其中 sequencer 和 driver 是共同使用的。

 1.1.sequence 實例
class my_transaction extends uvm_sequence_item;
    `uvm_object_utils(my_transaction )
    rand bit[47:0]    dmac;
    rand bit[47:0]    smac;
    constrain dma_cons{
        dmac inside {[0:100]};
        smac inside {[8:90]};
    }
    function void pre_randomize();
    endfunction
    function void post_randomize();
    endfunction
    ...
endclass
class my_sequence extends uvm_sequence(# my_transaction )
    `uvm_object_utils(my_sequence)
    task body();
        repeat(item_cnt) begin
            `uvm_do(req);
            get_response(req);
        end
    endtask
endclass

  首先,我們要根據驗證 Spec 實現 my_transaction(sequence_item 類),主要是完成隨機變量的定義和約束(constrain),其中 pre_randomize() 函數會在 sequence_item 在調用 randomize() 函數前自動調用,同理 post_randomize() 為其后自動調用,這兩個函數為空函數,需要用戶根據自己的需要實現;
  第二,實現 my_sequence 類,它必須繼承於 uvm_sequence,(# my_transaction )為 uvm_sequence 參數,如果不指定則默認為 uvm_sequence_item類型,同時必須實現 body() 函數,它會在調用 sequence 啟動是自動被調用。最后需要說明一下 'uvm_do(req), 這是一個 uvm library 定義的宏,它會完成 my_transaction 的實例化、隨機化和驅動任務,在收到 driver 的反饋信息后,才結束此次發送,否則會一直等待。其中的 req 也為 uvm library 在 uvm_sequence 中定義成員變量,只要繼承於它,即可直接用,其類型為 my_transaction(默認為 uvm_sequence) ;
  第三,需要將 my_sequence 掛載於能發送 (# my_transaction ) 的 sequencer 上;
  第四,啟動 sequence。后續兩項在本節后續會詳細講述。

 2.2.sequence 管控性實例
class my_sequence extends uvm_sequence(# my_transaction )
    `uvm_object_utils(my_sequence)
    clk_rst_seq            seq_clk;
    reg_cfg_seq          seq_ral;
    data_trans_seq     seq_data; 
    task body();
        `uvm_do(seq_ral);
        repeat(item_cnt) begin
            `uvm_do(seq_a);
            `uvm_do(seq_b);
        end
    endtask
endclass

  實例中,my_sequence 包含 3 個 sequence 成員變量,其中 seq_clk 用於產生復位信號,在發送結束后,連續發送 item_cnt 組,seq_ral 和 seq_data 數據。

2.什么是 sequencer

  sequencer 為 uvm_component,是驗證環境的不動產;而 sequence 是 uvm_object 類型,是環境中的動態產物,隨時可以創建和析構。它在驗證環境作為 sequnce 於 driver 之間的連接器,我們創建出來的 sequence 實例的 sequence_item 需要通過 sequencer 才能進入驗證環境中,繼而才能傳輸給 driver。sequencer 的應用實例代碼如下(直接繼承於 uvm_sequencer,不需要添加任何代碼):

class my_sequencer extends uvm_sequencer #(my_transaction);
    `uvm_component_utils(my_sequencer);
    function new(string name, uvm_component phase);
        super.new(name,parent);
    endfunction
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
    endfunction  
endclass
3.什么是 driver

   driver 也為 uvm_component,是驗證環境的不動產,常用套路如下,繼承於 uvm_driver ,重新實現 run_phase 函數, 因為一般不在 driver 中結束驗證仿真,所以 driver 的 run_phase() 中采用了一個永動機while(1)操作,它只要 seq_item_port 有 transaction 就會通過 get_next_item(req)獲取,並通過 send(req) 函數按照 驗證 Spec 時序驅動至 DUT,最后在發送結束后,通過 seq_item_port.item_done() 通知 sequence 當前 transaction已經發送完畢,可以發送下一個;也可以通過專用回復通道 seq_item_port.put_response(rsp) 通知 sequence 當前 transaction已經發送完畢,其中的 rsp 為返回的 transaction ,sequence 中的 get_response(req) 和 driver 中的 put_response(rsp) ,需要配合一起使用或者都不用,否則會產生掛死。

class my_driver  extends uvm_driver #(my_transaction);
     `uvm_component_utils(my_driver );
    ...
    virtual task run_phase(uvm_phase phase);
        ...
        while(1)begin
            seq_item_port.get_next_item(req);
            send(req);
            rsp = new("rsp");
            rsp.set_id_info(req);
            seq_item_port.item_done();
            seq_item_port.put_response(rsp);
        end
    endtask
endclass
4.sequence、sequencer 和 driver 連接關系

  sequence 為 uvm_obejct 類型,它與組件類 sequencer 的關系並非連接,而是掛載。在 uvm 中組件之間的連接一般在 connect_phase 完成,是一種固定的連接關系,在驗證環境仿真期間不會發送變換,sequence 與 sequencer 具體的掛載過程將在 sequence 啟動 一節詳細講述。
  sequencer 和 driver 之間為組件之間的連接-TLM通信,uvm library 為它們之間內嵌了兩套 TLM 通信端口,一組為 seq_item_port 和 seq_item_export,這組 TML 端口用於 sequence 與 driver 之間雙向通信(seq_item_port .item_done(rsp) 也可以用於返回 transaction);另一組為 rsp_export 和 rsp_port,這組 TML 端口僅用於 driver 向 sequencer 傳遞反饋信息(傳遞 rsp),為單向通信方式。

  先解釋一下 ·uvm_do(req),這個宏具體做了什么,其主要動作如下代碼所示,

virtual task body();
    …
    tr = new("tr");
    start_item(tr);
    assert(tr.randomize() with {tr.pload.size() == 200;});
    finish_item(tr);
    …
endtask    

  稍微解釋一下上述代碼:

a. tr = new("tr"); : 首先對待發送的 transaction 實例化
b. start_item(tr); : 通過 start_item 獲取 sequencer 的權限;
c. assert(tr.randomize() with {tr.pload.size() == 200;}); : transaction 隨機化;
d. finish_item(tr); : 將 transaction 發送至 driver ,在獲取到 driver 反饋信息后結束。

  start_itemfinish_item細節如下:

  

  為了理清上述代碼的所有過程,我們首先得清楚 sequence 和 sequencer 與 driver 之間的信息傳遞的過程,一個 sequence 在向 sequencer 發送 transaction 前,要先向 sequencer 發送一個請求,sequencer 把這個請求放在一個仲裁隊列中。作為 sequencer,它需做兩件事情:**第一,檢測仲裁隊列里是否有某個 sequence 發送 transaction 的請求;第二,檢測 driver 是否申請 transaction **。

a. 如果仲裁隊列里有發送請求,但是 driver 沒有申請 transaction,那么 sequencer 將會一直處於等待 driver 的狀態,直到 driver 申請新的 transaction。此時,sequencer 同意 sequence 的發送請求,sequence 在得到 sequencer 的批准后,產生出一個 transaction 並交給 sequencer,后者把這個 transaction 交給driver。
b. 如果仲裁隊列中沒有發送請求,但是 driver 向 sequencer 申請新的 transaction,那么 sequencer 將會處於等待 sequence 的狀態,一直到有 sequence 遞交發送請求,sequencer 馬上同意這個請求,sequence 產生 transaction 並交給 sequencer,最終 driver 獲得這個transaction。
c. 如果仲裁隊列中有發送請求,同時 driver 也在向 sequencer 申請新的 transaction,那么將會同意發送請求,sequence 產生 transaction 並交給 sequencer,最終 driver 獲得這個 transaction。

  現在上述代碼就容易理解了,pre_do,mid_do和 post_do 為空函數,需要用戶根據需求實現;wait_for_grant() 用於向 sequencer 請求發送transaction;send_request() 用於等待 sequencer 批准,在獲得權限后,將 transaction 發送給 sequencer;wait_for_item_done()用於等待 sequencer 傳遞的結束信號(sequencer 在 driver 執行 item_done 后,才認為當前 transaction 已經發送結束)。
  除了 'uvm_do 宏外,常用宏的還有 'uvm_create(創建 transaction)'uvm_send(發送 transction)'uvm_do_with(比 'uvm_do 多了隨機化) 等,這些宏的使用方式大同小異,在使用時再去看書吧。

5.sequence 啟動方式

a. my_sequence.start(my_sequencer) :start 函數有兩個用途,其一,將 my_sequence 掛在於 my_sequencer 上;其二,啟動 my_sequence(啟動后,自動執行 body() 函數)
b. uvm_config_db#(uvm_object_wrapper)::set(this, “sqr.main_phase”,”default_sequence”,my_sequence::type_id::get()) :工程應用中最常用的方式,將 my_sequencer 掛載於 sqr(env 中 sequencer 實例名) 上,該 sequence 會在 main_phase 運行階段啟動。也可以將 default_sequence 設置在其他 phase 運行時啟動,需要注意的是這種方式,設置的帶待啟動的 sequence 是采用的已經被實例化的實例名,而不是 sequence 的類名;
c.uvm_config_db#(uvm_object_wrapper)::set(this, “sqr.main_phase”,”default_sequence”,my_sequence::get_type) ,這種啟動方式和第二種類似,只是采用的是 sequence 類名。

task my_sequencer::main_phase(uvm_phase phase);
    ...
    seq.starting_phase = phase;
    seq.start(this);
endtask

c. uvm_x_on(my_sequence, my_sequencer) :此處的 uvm_x_on() 主要有兩種 uvm_do_on() uvm_creat_on(),它們相比 uvm_do() uvm_creat() 原有功能,額外增加的是,將 my_sequence 掛載於 my_sequencer 上。

6.什么是 virutal sequence/sequencer
 6.1.為什么要引入 virutal sequence/sequencer

  為了幫助理解,我們先來說說為什么要引入 virtual sequence/sequencer 在前面的示例中,我們均對 sequence 和 sequencer 通過 #(my_transaction),設置了當前 sequence 和 sequencer 的參數類型,但這同時也限定了當前 sequencer 僅能傳遞參數類型是 #(my_transaction) 的 sequence 類型。其實下面代碼所示的這種用法是有前提的,因為 'uvm_do(sequence) 這個宏默認采用的 sequencer 是 my_sequence 類掛載的 sequncer,所以 clk_rst_seq reg_cfg_seqdata_trans_seq 3 個 sequnce類的參數類型必須同為 #(my_transaction) 參數。

class my_sequence extends uvm_sequence(# my_transaction )
    `uvm_object_utils(my_sequence)
    clk_rst_seq            seq_rst;
    reg_cfg_seq          seq_ral;
    data_trans_seq     seq_data; 
    task body();
        `uvm_do(seq_rst);
        repeat(item_cnt) begin
            `uvm_do(seq_ral);
            `uvm_do(seq_data);
        end
    endtask
endclass

  那么有沒有辦法將兩個截然不同的 transaction 交給同一個 sequencer 呢?可以,只是需要將 sequencer 和 driver 能夠接受的數據類型設置為 uvm_sequence_item (默認類型):

class my_sequencer extends uvm_sequencer #(uvm_sequence_item);
class my_driver extends uvm_driver#(uvm_sequence_item);

  這樣帶來的問題是,由於 driver 中接收的數據類型是 uvm_sequence_item,如果它要使用 clk_rst_seq reg_cfg_seqdata_trans_seq 中的成員變量,必須使用 cast 轉換:

task my_driver::main_phase(uvm_phase phase);
    clk_rst_seq            seq_rst;
    reg_cfg_seq          seq_ral;
    data_trans_seq     seq_data; 
    …
    while(1) begin
        seq_item_port.get_next_item(req);
        if($cast(seq_rst, req)) begin
            drive_my_transaction(req);
            `uvm_info("driver", "receive clk_rst_seq type ", UVM_MEDIUM)
        end
        else if($cast(seq_ral, req)) begin
            drive_your_transaction(req);
            `uvm_info("driver", "receive  reg_cfg_seq type ", UVM_MEDIUM)
        end if($cast(seq_data, req)) begin
            drive_your_transaction(req);
            `uvm_info("driver", "receive data_trans_seq type ", UVM_MEDIUM)
        end
        else begin
            `uvm_error("driver", "receive type is unknown")
        end
        seq_item_port.item_done();
    end
endtask

  通過 cast() 來區分是不是很麻煩,如果你不想這種方式,virutal sequence/sequencer 就是你的最佳選擇:

virutal sequence : 可以用於承載不同目標的 sequence (參數不同),組織協調不同的 sequence 按照一定的協議順序,異步或者同步啟動; 它與普通的 sequence 的區別是,它可以包含不同類型的 sequence,其他沒有任何區別。
virutal sequencer : virutal sequencer 和普通 sequencer 不同,它用於橋接其 virtual sequencer 內包含的 sequencer,即 virutal sequencer 用於鏈接所有底層 sequencer 的句柄;virtual sequencer 本身並不傳遞 transaction,因此 virutal sequencer 不需要和任何 driver 進行 TML 連接;但是用戶需要在頂層的 connect_phase 中做好 virtual sequencer 中各 sequencer 句柄與底層 sequencer 實體的一一對接,避免句柄懸空

  如上圖所示,包含了 virutal sequence / sequencer 以及 3 個 agent,其中 virutal sequence 中包含三個句柄/實例,並在其 body() 函數中對 3 個 sequence 通過 'uvm_do 宏發送。virutal sequencer 中僅包含 3 個句柄,其並不對這 3 個句柄進行實例化,而上通過 connect_phase() 指向 3 個 agent 中的對應 sequencer 實例,在此 virutal sequencer 相當於 sequencesequencer 之間的一個中轉路由器的作用,上圖中紅線部分用於完成 virtual sequencer 中各 sequencer 句柄與底層 sequencer 實體的一一對接。

 6.2.virutal sequence/sequencer 實例
//step1: virtual sequence 實現
class vseq extends uvm_sequence;
    `uvm_declare_p_sequencer(vseq);
    clk_rst_seq           seq_rst;
    reg_cfg_seq         seq_ral;
    data_trans_seq    seq_data; 
    ...
    `uvm_object_utils(vseq)
    task body();
        `uvm_do_on(seq_rst,p_sequencer.seqr_rst);
        repeat(item_cnt) begin
            `uvm_do_on(seq_rst,p_sequencer.seqr_ral);
            `uvm_do_on(seq_rst,p_sequencer.seqr_data);
        end
    endtask
endclass

//step2: virtual sequencer 實現
class vseqr extends uvm_sequencer;
    clk_rst_seqr           seqr_rst;
    reg_cfg_seqr         seqr_ral;
    data_trans_seqr    seqr_data; 
   `uvm_component_utils(vseqr)
	...
endclass

//step3: sequencer 連接
class env extends uvm_env;
    vseqr                   my_vseqr;
    `uvm_component_utils(env)	
	virtual function void build_phase(uvm_phase phase);
		my_vseqr = vseqr::type_id::create("vseqr",this);
	endfunction
	virtual function void connect_phase(uvm_phase phase);
		seqr_rst     = clk_rst_agt.seqr_rst;
		seqr_ral     = reg_cfg_agt.seqr_ral;
		seqr_data  = data_trans_agt.seqr_data;
	endfunction
endclass

//step4 vseq 啟動
class my_test extends uvm_test;
    `uvm_component_utils(my_test)	
    virtual function void build_phase(uvm_phase phase);
	`uvm_config_db#(uvm_object_wrapper)::set(this,"env.vseqr.main_phase","default_sequence",vseq::get_type())
    endfunction
endclass

  上述代碼中,步驟一,實現了 virtual sequence ,因為 3 個 sequence 的 sequencer 不同,需要采用 'uvm_do_on 宏 單獨將對應的 sequence 掛載到對應的 sequencer 上;步驟三,實現了 vseq 中句柄與對應 agent 的 sequencer 實例連接;最后提一點 'uvm_declare_p_sequencer(vseq) 宏用於定義一個 p_sequencer 句柄,其類型為,當前 vseq 掛載的 vseqr 類型,'uvm_declare_p_sequencer(vseq)的代碼原型為:

`define uvm_declare_p_sequencer(SEQUENCER)\
    SEQUENCER p_sequencer;\
    virtual function void m_set_p_sequencer();\
    super.m_set_p_sequencer();
    if(!cast(p_sequencer,m_sequencer))\
    ...
    endfunction
 6.3.m_sequencer Vs p_sequencer

a. sequencer 中自帶的 default sequencer,指向當前 sequence 掛載的 sequencer,類型為 uvm_sequencer(m_sequencer 不能對指向的 vseqr 中的 seqr 句柄進行點操作)
b. p_sequencer 為 m_sequencer 經過 cast 后的指針,類型與當前 sequence 掛載的 sequencer一致。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM