DUT中寄存器的值可能是實時變更的, 寄存器模型並不能實時地知道這種變更, 因此, 寄存器模型中的寄存器的值有時與DUT中相關寄存器的值並不一致。 對於任意一個寄存器, 寄存器模型中都會有一個專門的變量用於最大可能地與DUT保持同步, 這個變量在寄存器模型中稱為DUT的鏡像值( mirrored value) 。寄存器模型中還有一個值叫期望值(respected value),這個值保存我們希望寫入寄存器的值。比如希望向DUT某個寄存器寫入'h1,用set函數設置期望值,然后用update任務(update會比較鏡像值和期望值,如果不一樣,將期望值寫入DUT,並更新鏡像值)。
寄存器模型的使用的優點:
1.可進行后門方式訪問dut中的寄存器;
2.在參考模型中不必引入全局變量可得到寄存器模型中值的變化;
3.提供很多函數使用方便;
4.可以很方便的對寄存器的 coverage 驗證點的收集。
寄存器模型中的單位
uvm_reg_field:這是寄存器模型中的最小單位。
uvm_reg:它比 uvm_reg_field 高一個級別,但是依然是比較小的單位。一個寄存器中至少包含一個 uvm_reg_field。
uvm_reg_block:它是一個比較大的單位,在其中可以加入許多的 uvm_reg,也可以加入其他的 uvm_reg_block。一個寄存器模型中至少包含一個 uvm_reg_block。
uvm_reg_map:每個寄存器在加入寄存器模型時都有其地址,uvm_reg_map 就是存儲這些地址,並將其轉換成可以訪問的物理地址(因為加入寄存器模型中的寄存器地址一般都是偏移地址,而不是絕對地址)。當寄存器模型使用前門訪問方式來實現讀或寫操作時,uvm_reg_map 就會將地址轉換成絕對地址,啟動一個讀或寫的 sequence,並將讀或寫的結果返回。在每個 reg_block 內部,至少有一個(通常也只有一個)uvm_reg_map。
一個簡單的寄存器模型,每個派生自uvm_reg的類都有一個build()函數,它只能手工調用.(可直接通過腳本將excel生成寄存器模型。腳本地址 https://download.csdn.net/download/qq_38620826/18198642)
class reg_invert extends uvm_reg; rand uvm_reg_field reg_data; //在reg中定義field virtual function void build(); //build reg_data = uvm_reg_field::type_id::create("reg_data"); // parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible reg_data.configure(this, 1, 0, "RW", 1, 0, 1, 1, 0); //上面一行是參數介紹 endfunction function new(input string name="reg_invert"); //parameter: name, size, has_coverage super.new(name, 16, UVM_NO_COVERAGE); //16是這個寄存器的寬度, endfunction endclass
寄存器模型中常用的函數
read:函數的原型(將根據從dut寄存器中讀取的結果同時更新寄存器模型的期望值和鏡像值),有多個參數, 常用的是其前三個參數。 其中第一個是uvm_status_e型的變量, 這是一個輸出, 用於表明讀操作是否成功;第二個是讀取的數值, 也是一個輸出; 第三個是讀取的方式, 可選UVM_FRONTDOOR和UVM_BACKDOOR。
extern virtual task read(output uvm_status_e status, output uvm_reg_data_t value, input uvm_path_e path = UVM_DEFAULT_PATH, input uvm_reg_map map = null, input uvm_sequence_base parent = null, input int prior = -1, input uvm_object extension = null, input string fname = "", input int lineno = 0);
使用示例
p_sequencer.p_rm.invert.read(status, value, UVM_FRONTDOOR);
write:函數的原型(將值寫入到dut的寄存器中同時更新寄存器模型的期望值和鏡像值),參數也有很多個, 但是與read類似, 常用的也只有前三個。 其中第一個為uvm_status_e型的變量, 這是一個輸出, 用於表明寫操作是否成功。 第二個要寫的值, 是一個輸入, 第三個是寫操作的方式, 可選UVM_FRONTDOOR和UVM_BACKDOOR。
extern virtual task write(output uvm_status_e status, input uvm_reg_data_t value, input uvm_path_e path = UVM_DEFAULT_PATH, input uvm_reg_map map = null, input uvm_sequence_base parent = null, input int prior = -1, input uvm_object extension = null, input string fname = "", input int lineno = 0);
使用示例
p_sequencer.p_rm.invert.write(status, 1, UVM_FRONTDOOR);
peek:后門讀函數(將根據從dut寄存器中讀取的結果同時更新寄存器模型的期望值和鏡像值),雖然read函數也能選擇后門訪問的方式讀取寄存器,但是dut中的寄存器如果是只寫的則使用呢read不能讀取出來,但是使用peek是不管dut中寄存器的類型的,一定可以讀取出來。
extern virtual task peek(output uvm_status_e status, output uvm_reg_data_t value, input string kind = "", input uvm_sequence_base parent = null, input uvm_object extension = null, input string fname = "", input int lineno = 0);
使用示例
p_sequencer.p_rm.invert.peek(status, value);
poke:后門寫函數(將值寫入到dut的寄存器中同時更新寄存器模型的期望值和鏡像值)。雖然write函數也能選擇后門訪問的方式寫寄存器,但是dut中如果是一個只讀寄存器,使用write則不能寫入成功,但poke是不管dut中寄存器的類型的,可以完成對只讀寄存器的寫入。
extern virtual task poke(output uvm_status_e status, input uvm_reg_data_t value, input string kind = "", input uvm_sequence_base parent = null, input uvm_object extension = null, input string fname = "", input int lineno = 0);
使用示例
set:將值寫入到寄存器模型的期望值中
extern virtual function void set (uvm_reg_data_t value, string fname = "", int lineno = 0);
使用示例
p_sequencer.p_rm.invert.set(16'h1);
update:將期望值和鏡像值比對,如果不一致則吧期望值寫入到dut的寄存器中,同時更新寄存器模型鏡像值。uvm_reg級別被調用, 也可以在uvm_reg_block級別被調用。
extern virtual task update(output uvm_status_e status, input uvm_path_e path = UVM_DEFAULT_PATH, input uvm_reg_map map = null, input uvm_sequence_base parent = null, input int prior = -1, input uvm_object extension = null, input string fname = "", input int lineno = 0);
使用示例
p_sequencer.p_rm.invert.update(status, UVM_FRONTDOOR);
get:得到寄存器模型的期望值
extern virtual function uvm_reg_data_t get(string fname = "", int lineno = 0);
使用示例
value = p_sequencer.p_rm.invert.get();
get_mirrored_value:得到寄存器模型的鏡像值
extern virtual function uvm_reg_data_t get_mirrored_value(string fname = "", int lineno = 0);
使用示例
value = p_sequencer.p_rm.invert.get_mirrored_value();
mirror:將dut中的寄存器值更新到寄存器模型的鏡像值值和期望值,其中第二個參數指的是如果發現DUT中寄存器的值與寄存器模型中的鏡像值不一致, 那么在更新寄存器模型之前是否給出錯誤提示。 其可選的值為UVM_CHECK和UVM_NO_CHECK。它有兩種應用場景, 一是在仿真中不斷地調用它, 使得到整個寄存器模型的值與DUT中寄存器的值保持一致, 此時check選項是關閉的。 二是在仿真即將結束時, 檢查DUT中寄存器的值與寄存器模型中寄存器的鏡像值是否一致, 這種情況下, check選項是打開的。mirror操作既可以在uvm_reg級別被調用, 也可以在uvm_reg_block級別被調用。
extern virtual task mirror(output uvm_status_e status, input uvm_check_e check = UVM_NO_CHECK, input uvm_path_e path = UVM_DEFAULT_PATH, input uvm_reg_map map = null, input uvm_sequence_base parent = null, input int prior = -1, input uvm_object extension = null, input string fname = "", input int lineno = 0);
使用示例
p_sequencer.p_rm.counter.mirror(status, UVM_CHECK, UVM_FRONTDOOR);
predict:在DUT中的計數器是不斷累加的, 但是寄存器模型中的計數器則保持靜止。參考模型會不斷統計收到了多少包。如果想將參考模型中的數據傳遞給寄存器模型又不對dut進行操作,可以通過precict,predict操作會更新鏡像值和期望值。
其中第一個參數表示要預測的值, 第二個參數是byte_en, 默認-1的意思是全部有效, 第三個參數是預測的類型, 第四個參數是后門訪問或者是前門訪問。 要實現在參考模型中更新寄存器模型而又不影響DUT的值, 第三個參數需要使用UVM_PREDICT_DIRECT
,第三個參數預測類型有如下幾種可以選擇
UVM_PREDICT_DIRECT :Predicted value is as-is
UVM_PREDICT_READ :Predict based on the specified value having been read
UVM_PREDICT_WRITE :Predict based on the specified value having been written
extern virtual function bit predict (uvm_reg_data_t value, uvm_reg_byte_en_t be = -1, uvm_predict_e kind = UVM_PREDICT_DIRECT, uvm_path_e path = UVM_FRONTDOOR, uvm_reg_map map = null, string fname = "", int lineno = 0);
使用示例
p_rm.counter.predict(counter);
randomize:寄存器模型期望值隨機化,可以在uvm_reg_block級別調用randomize函數, 也可以在uvm_reg級別, 甚至可以在uvm_reg_field級別調用:可通過下面調用示例完成寄存器隨機化
assert(rm.randomize());
assert(rm.invert.randomize());
assert(rm.invert.reg_data.randomize());
只使用randomize是不能完成寄存器值隨機化的還要保證下面三點:
1.當在uvm_reg中定義此field時, 設置為rand類型。
2.在調用此field的configure函數時, 第八個參數設置為1。
3.寄存器類型是可寫的