transaction是一个抽象的概念。一般来说,物理协议中的数据交换都是以帧或者包为单位的,通常在一帧或者一个包中要定义 好各项参数,每个包的大小不一样。很少会有协议是以bit或者byte为单位来进行数据交换的。transaction就是用于模拟这种实际情况,一 笔transaction就是一个包。在不同的验证平台中,会有不同的transaction。一个简单的transaction的定义如下:
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(my_transaction)function new(string name = "my_transaction");super.new();endfunction
endclass
post_randomize是SystemVerilog中提供的一个函数,当某个类的实例的randomize函数被调用后,post_randomize会紧随其后无条件 地被调用。
在transaction定义中,有两点值得引起注意:一是my_transaction的基类是uvm_sequence_item。在UVM中,所有的transaction都 要从uvm_sequence_item派生,只有从uvm_sequence_item派生的transaction才可以使用UVM中强大的sequence机制。二是这里没有使用uvm_component_utils宏来实现factory机制,而是使用了uvm_object_utils。从本质上来说,my_transaction与 my_driver是有区别的,在整个仿真期间,my_driver是一直存在的,my_transaction不同,它有生命周期。它在仿真的某一时间产 生,经过driver驱动,再经过reference model处理,最终由scoreboard比较完成后,其生命周期就结束了。一般来说,这种类都是派 生自uvm_object或者uvm_object的派生类,uvm_sequence_item的祖先就是uvm_object。UVM中具有这种特征的类都要使用 uvm_object_utils宏来实现。
当完成transaction的定义后,就可以在my_driver中实现基于transaction的驱动:
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);bit [47:0] tmp_data;bit [7:0] data_q[$]; //push dmac to data_qtmp_data = tr.dmac;for(int i = 0; i < 6; i++) begindata_q.push_back(tmp_data[7:0]);tmp_data = (tmp_data >> 8);end//push smac to data_qtmp_data = tr.smac;for(int i = 0; i < 6; i++) begindata_q.push_back(tmp_data[7:0]);tmp_data = (tmp_data >> 8);end//push ether_type to data_qtmp_data = tr.ether_type;for(int i = 0; i < 2; i++) begindata_q.push_back(tmp_data[7:0]);tmp_data = (tmp_data >> 8);end//push payload to data_qfor(int i = 0; i < tr.pload.size; i++) begindata_q.push_back(tr.pload[i]);end//push crc to data_qtmp_data = tr.crc;for(int i = 0; i < 4; i++) begindata_q.push_back(tmp_data[7:0]);tmp_data = (tmp_data >> 8);end`uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);repeat(3) @(posedge vif.clk);while(data_q.size() > 0) begin@(posedge vif.clk);vif.valid <= 1'b1;vif.data <= data_q.pop_front(); end@(posedge vif.clk);vif.valid <= 1'b0;`uvm_info("my_driver", "end drive one pkt", UVM_LOW);
endtask