加入transaction
transaction
txd <= rxd;
tx_en <= rx_dv;
- 之前driver中的操作都是信号级的。在reference model、monitor、scoreboard等验证平台组件之间,信息的传递是基于transaction的
- 物理协议中的数据交换都是以帧或者包为单位的,transaction就是用于模拟这种实际情况
- 通常在一帧或者一个包中要定义好各项参数,每个包的大小不一样
transaction代码
以以太网为例,每个包的大小至少是64byte。包中要包括源地址、目的地址、包的类型、整个包的CRC校验数据等
`ifndef MY_TRANSACTION__SV
`define MY_TRANSACTION__SV
class my_transaction extends uvm_sequence_item;
//my_transaction的基类是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;
endfunction
function void post_randomize();
crc = calc_crc;
endfunction
//当某个类的实例的randomize函数被调用后,post_randomize会紧随其后无条件地被调用
`uvm_object_utils(my_transaction)
//这里没有使用uvm_component_utils宏来实现factory机制
function new(string name = "my_transaction");
super.new();
endfunction
endclass
`endif
- 在UVM中,所有的transaction都要从uvm_sequence_item派生,只有从uvm_sequence_item派生的transaction才可以sequence机制
- 在整个仿真周期,driver一直存在,是uvm_component,使用uvm_component_utils宏来实现factory机制
- my_transaction有生命周期,这种类都是派生自uvm_object或者uvm_object的派生类,uvm_sequence_item的祖先就是uvm_object。都要使用uvm_object_utils宏来实现。
my_driver中使用transaction
`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV
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);
endfunction
virtual 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!!!")
endfunction
extern task main_phase(uvm_phase phase);
extern task drive_one_pkt(my_transaction tr);
endclass
task my_driver::main_phase(uvm_phase phase);
my_transaction tr;
//声明my_transaction的句柄
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);
//重复两次,先随机化tr,然后调用task drive_one_pkt将随机化之后的tr发送出去
end
repeat(5) @(posedge vif.clk);
phase.drop_objection(this);
endtask
task my_driver::drive_one_pkt(my_transaction tr);
bit [47:0] tmp_data;
bit [7:0] data_q[$];
//先将tr所有的数据压入队列data_q中
//push dmac to data_q
tmp_data = tr.dmac;
for(int i = 0; i < 6; i++) begin
data_q.push_back(tmp_data[7:0]);
tmp_data = (tmp_data >> 8);
end
//push smac to data_q
tmp_data = tr.smac;
//tr中的各项参数,每次8bit压入队列data_q中,相当于打包成一个byte流的过程
for(int i = 0; i < 6; i++) begin
data_q.push_back(tmp_data[7:0]);
tmp_data = (tmp_data >> 8);
end
//push ether_type to data_q
tmp_data = tr.ether_type;
for(int i = 0; i < 2; i++) begin
data_q.push_back(tmp_data[7:0]);
tmp_data = (tmp_data >> 8);
end
//push payload to data_q
for(int i = 0; i < tr.pload.size; i++) begin
data_q.push_back(tr.pload[i]);
end
//push crc to data_q
tmp_data = tr.crc;
for(int i = 0; i < 4; i++) begin
data_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();
//将data_q中的所有数据驱动到dut中
end
@(posedge vif.clk);
vif.valid <= 1'b0;
`uvm_info("my_driver", "end drive one pkt", UVM_LOW);
endtask
`endif
仿真结果
此次变化主要将driver内部驱动的由原先的信号转化为包transaction