(1)uvm_field宏实现field_automation机制
UVM中需要逐字段地对transaction进行某些操作。这就是UVM中的 field_automation机制,使用uvm_field系列宏实现:
class my_transaction extends uvm_sequence_item;rand bit[47:0] dmac;rand bit[47:0] smac;rand bit[15:0] ether_type;rand byte pload[];rand bit[31:0] crc;constraint pload_cons{pload.size >= 46;pload.size <= 1500;}function bit[31:0] calc_crc();return 32'h0;endfunctionfunction void post_randomize();crc = calc_crc;endfunction`uvm_object_utils_begin(my_transaction)`uvm_field_int(dmac, UVM_ALL_ON)`uvm_field_int(smac, UVM_ALL_ON)`uvm_field_int(ether_type, UVM_ALL_ON)`uvm_field_array_int(pload, UVM_ALL_ON)`uvm_field_int(crc, UVM_ALL_ON)`uvm_object_utils_endfunction new(string name = "my_transaction");super.new();endfunctionendclass
这里使用uvm_object_utils_begin和uvm_object_utils_end来实现my_transaction的factory注册,在这两个宏中间,使用uvm_field宏 注册所有字段。uvm_field系列宏随着transaction成员变量的不同而不同。
当使用上述宏注册之后,可以直接调用copy、compare、print等函数,而无需自己定义。这极大地简化了验证平台的搭建,提 高了效率:
(2) 引入field_automation机制的另外一大好处是简化了driver和monitor
my_model.svclass my_model extends uvm_component;uvm_blocking_get_port #(my_transaction) port;uvm_analysis_port #(my_transaction) ap;extern function new(string name, uvm_component parent);extern function void build_phase(uvm_phase phase);extern virtual task main_phase(uvm_phase phase);`uvm_component_utils(my_model)
endclass function my_model::new(string name, uvm_component parent);super.new(name, parent);
endfunction function void my_model::build_phase(uvm_phase phase);super.build_phase(phase);port = new("port", this);ap = new("ap", this);
endfunctiontask my_model::main_phase(uvm_phase phase);my_transaction tr;my_transaction new_tr;super.main_phase(phase);while(1) beginport.get(tr);new_tr = new("new_tr");new_tr.copy(tr);`uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)new_tr.print();ap.write(new_tr);end
endtask
my_scoreboard.sv class my_scoreboard extends uvm_scoreboard;my_transaction expect_queue[$];uvm_blocking_get_port #(my_transaction) exp_port;uvm_blocking_get_port #(my_transaction) act_port;`uvm_component_utils(my_scoreboard)extern function new(string name, uvm_component parent = null);extern virtual function void build_phase(uvm_phase phase);extern virtual task main_phase(uvm_phase phase);
endclass function my_scoreboard::new(string name, uvm_component parent = null);super.new(name, parent);
endfunction function void my_scoreboard::build_phase(uvm_phase phase);super.build_phase(phase);exp_port = new("exp_port", this);act_port = new("act_port", this);
endfunction task my_scoreboard::main_phase(uvm_phase phase);my_transaction get_expect, get_actual, tmp_tran;bit result;super.main_phase(phase);fork while (1) beginexp_port.get(get_expect);expect_queue.push_back(get_expect);endwhile (1) beginact_port.get(get_actual);if(expect_queue.size() > 0) begintmp_tran = expect_queue.pop_front();result = get_actual.compare(tmp_tran);//直接调用compare函数,不需要自己定义compare函数if(result) begin `uvm_info("my_scoreboard", "Compare SUCCESSFULLY", UVM_LOW);endelse begin`uvm_error("my_scoreboard", "Compare FAILED");$display("the expect pkt is");tmp_tran.print();//直接调用print函数,不需要自己定义print函数$display("the actual pkt is");get_actual.print();//直接调用print函数,不需要自己定义print函数endendelse begin`uvm_error("my_scoreboard", "Received from DUT, while Expect Queue is empty");$display("the unexpected pkt is");get_actual.print();end endjoin
endtask
(3)使用field_automation机制后,可以 简化drv_one_pkt任务
class my_driver extends uvm_driver;virtual my_if vif;`uvm_component_utils(my_driver)function new(string name = "my_driver", uvm_component parent = null);super.new(name, parent);endfunctionvirtual function void build_phase(uvm_phase phase);super.build_phase(phase);if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")endfunctionextern task main_phase(uvm_phase phase);extern task drive_one_pkt(my_transaction tr);
endclasstask my_driver::main_phase(uvm_phase phase);my_transaction tr;phase.raise_objection(this);vif.data <= 8'b0;vif.valid <= 1'b0;while(!vif.rst_n)@(posedge vif.clk);for(int i = 0; i < 2; i++) begin tr = new("tr");assert(tr.randomize() with {pload.size == 200;});drive_one_pkt(tr);endrepeat(5) @(posedge vif.clk);phase.drop_objection(this);
endtasktask my_driver::drive_one_pkt(my_transaction tr);byte unsigned data_q[];int data_size;data_size = tr.pack_bytes(data_q) / 8; //调用pack_bytes将tr中所有的字段变成byte流放入data_q中`uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);repeat(3) @(posedge vif.clk);for ( int i = 0; i < data_size; i++ ) begin@(posedge vif.clk);vif.valid <= 1'b1;vif.data <= data_q[i]; end@(posedge vif.clk);vif.valid <= 1'b0;`uvm_info("my_driver", "end drive one pkt", UVM_LOW);
endtask
第42行调用pack_bytes将tr中所有的字段变成byte流放入data_q中。 pack_bytes极大地减少了代码量。在把所有的字段变成byte流放入data_q中时,字段按照uvm_field系列宏书写的顺序排列。在上述 代码中是先放入dmac,再依次放入smac、ether_type、pload、crc。假如my_transaction定义时各个字段的顺序如下:
`uvm_object_utils_begin(my_transaction)
`uvm_field_int(smac, UVM_ALL_ON)
`uvm_field_int(dmac, UVM_ALL_ON)
`uvm_field_int(ether_type, UVM_ALL_ON)
`uvm_field_array_int(pload, UVM_ALL_ON)
`uvm_field_int(crc, UVM_ALL_ON)
`uvm_object_utils_end
那么将会先放入smac,再依次放入dmac、ether_type、pload、crc。
my_monitor的collect_one_pkt可以简化成:
`ifndef MY_MONITOR__SV
`define MY_MONITOR__SV
class my_monitor extends uvm_monitor;virtual my_if vif;uvm_analysis_port #(my_transaction) ap;`uvm_component_utils(my_monitor)function new(string name = "my_monitor", uvm_component parent = null);super.new(name, parent);endfunctionvirtual function void build_phase(uvm_phase phase);super.build_phase(phase);if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))`uvm_fatal("my_monitor", "virtual interface must be set for vif!!!")ap = new("ap", this);endfunctionextern task main_phase(uvm_phase phase);extern task collect_one_pkt(my_transaction tr);
endclasstask my_monitor::main_phase(uvm_phase phase);my_transaction tr;while(1) begintr = new("tr");collect_one_pkt(tr);ap.write(tr);end
endtasktask my_monitor::collect_one_pkt(my_transaction tr);byte unsigned data_q[$];byte unsigned data_array[];logic [7:0] data;logic valid = 0;int data_size;while(1) begin@(posedge vif.clk);if(vif.valid) break;end`uvm_info("my_monitor", "begin to collect one pkt", UVM_LOW);while(vif.valid) begindata_q.push_back(vif.data);@(posedge vif.clk);enddata_size = data_q.size(); data_array = new[data_size];for ( int i = 0; i < data_size; i++ ) begindata_array[i] = data_q[i]; endtr.pload = new[data_size - 18]; //da sa, e_type, crcdata_size = tr.unpack_bytes(data_array) / 8; `uvm_info("my_monitor", "end collect one pkt", UVM_LOW);
endtask`endif
这里使用unpack_bytes函数将data_q中的byte流转换成tr中的各个字段。unpack_bytes函数的输入参数必须是一个动态数组,所 以需要先把收集到的、放在data_q中的数据复制到一个动态数组中。由于tr中的pload是一个动态数组,所以需要在调用 unpack_bytes之前指定其大小,这样unpack_bytes函数才能正常工作。