UVM - 12(driver and monitor 练习)
uvm exercise-1
- 实现apb_sequencer.sv,传输数据类型式abp_trans
- 实现virtual sequencer.sv,定义两个sub sequencer:mst_sqr,slv_sqr
class abp_sequencer extends uvm_sequencer #(apb_trans);
`uvm_component_utils(apb_sequencer);
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
endclass
class virtual_sequencer extends uvm_sequencer;
`uvm_component_utils(virtual_sequencer);
apb_sequencer mst_sqr;
apb_sequnecer slv_sqr;
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
endclass
driver的写法
- sequencer将数据传递给driver
- driver通过接口将数据传递给dut
- driver和sequencer的连接通过driver中的seq_item_port和sequencer的seq_item_export连接
- env --> agent --> driver/sequencer/monitor
- 在env中例化agent之后,可以通过uvm_config_db设置默认的sequence及执行位置
- 在agent中例化driver和sequencer之后可以在connect phase中调用driver.seq_item_port.connect(seqr.seq_item_export)进行连接
class gpio_driver extends uvm_driver #(gpio_transfer);
// 注册
`uvm_component_utils(gpio_driver);
// driver需要将接口信号传给dut,所以要用虚接口
virtual gpio_if gpio_if;
// 构造函数
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
// connect_phase中得到gpio_if接口
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(!uvm_config_db #(virtual gpio_if)::get(this,"","gpio_if",gpio_if))
`uvm_error("NOVIF",{"virtual interface must be set for:"},get_full_name(),".gpio_if");
endfunction
// 获取sequencer发送的数据,并将其传递给dut
virtual task run_phase(uvm_phase phase);
get_and_drive();
endtask
virtual protected task get_and_drive();
gpio_transfer this_trans; // 事务句柄
@(posedge gpio_if.n_p_reset); // 复位信号释放之后
forever begin // 用一个循环
@(posedge gpio_if.clk); // 等到时钟有效沿
seq.item_port.get_next_item(req); // 将得到的req转换为this_trans
if(!$cast(this_trans,req))
`uvm_fatal("CASTFL","Failed to cast req to this_trans in get_and_drive");
driver_data(this_trans); // 传递数据
seq_item_port.item_done(); // 传输完成
end
endtask
virtual protected task drive_data(gpio_transfer gpio_tr);
.............
// 将传入的tr的数据驱动到对应的接口上
endtask
endclass
driver的功能:
<1> 在connect_phase中通过uvm_config_db获取接口
<2> 在run_phase中获取数据和驱动接口
a>获取数据通过driver中的seq_item_port.get_next_item(req)
req就是获取出来的transaction对象
b>获取出来req对象之后,将其cast为对应的transaction对象
c>驱动接口,将transaction对象中的信号驱动给接口
d>完成数据传输
monitor写法
- monitor需要采集接口信号传输给scoreboard,需要将接口信号转换为transaction中的数据进行传递
class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor);
// 需要采集接口信号,所以要使用虚接口
virtual dut_if vif;
// monitor可以对采集到的信号进行check,所以定义使能信号
bit enable_check = 1;
// monitor信号传递给scoreboard需要用到事务传输
// 例化一个uvm事务传输接口句柄,uvm_analysis_port
umv_analysis_port #(my_data) mon_analysis_port;
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// 创建事务传输接口
mon_analysis_port = new("mon_analysis_port",this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(!uvm_config_db #(virtual dut_if)::get(this,"","vif",vif)) begin
`uvm_error(get_type_name(),"DUT interface not found");
end
endfunction
virtual task run_phase(uvm_phase phase);
// 需要将接口数据传递出去,所以定义transaction对象
my_data data_object = my_data::type_id::create("data_object",this);
forever begin
// 数据有效的时候,采样
@[Some event when data at DUT port is valid];
// 将接口信号给到transaction
data_object.data = vif.data;
data_object.addr = vif.addr;
// check使能,就调用检查协议的函数
if(check_enable) check_protocol();
// 采样功能概率
data.object.cg_trans.sample()
// 通过analysis_port发送数据
mon_analysis_port.write(data_object);
end
endtask
virtual function void check_protocol();
// Function to check basic protocol specs
endfunction
endclass
定义虚接口,在connectphase中获取接口
例化uvm_analysis_port对象
创建transaction对象,将接口数据给到transaction对象发送出去
定义check_protocol()函数
调用transaction中covergroup的sample()函数
uvm_exercise_2
- 实现apb_driver.sv,apb_monitor.sv
- drv:发送apb_trans事务给dut
- mon:将监控接口得到的事务通过apb_mon_port的analysis_port发送出去
- 符合apb接口协议
APB总线协议
- pclk
- presetn
- paddr[31:0]
- pselx
- pnable
- pwrite
- prdata [31:0]
- pwdata [31:0]
apb_driver
class apb_driver extends uvm_driver #(apb_trans)
virtual apb_interface apb_ifc;
`uvm_component_utils(apb_driver);
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
extern virtual function void connect_phase(uvm_phase phase);
extern virtual task run_phase(uvm_phase phase);
extern virtual task reset_phase(uvm_phase phase);
extern virtual task get_and_drive();
extern virtual task drive_data(apb_trans tr);
endclass
function void apb_driver::connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(!uvm_config_db #(apb_interface)::get(this,"","vif",apb_ifc))
`uvm_error("NOAPB_IFC",{"virtual interface must be set for:",get_full_name(),"apb_ifc"});
endfunction
task apb_driver::reset_phase(uvm_phase phase);
phase.raise_objection(this);
@(negedge apb_ifc.rst_n);
// 复位信号有效的时候,接口中的信号进行复位
apb_ifc.psel <= 1'b0;
apb_ifc.penable <= 1'b0;
apb_ifc.pwrite <= 1'b0;
apb_ifc.pwdata <= 32'b0;
apb_ifc.paddr <= 32'b0;
phase.drop_objection(this);
endtask
task apb_driver::run_phase(uvm_phase phase);
get_and_drive();
endtask
task apb_driver::get_and_drive();
apb_trans tr;
@(posedge apb_ifc.rst_n);
forever begin
@(posedge apb_ifc.clk);
seq_item_port.get_next_item(req);
if(!$cast(tr,req))
`uvm_fatal("CASTFL","Failed to cast req to apb_trans in get_and_drive");
// drive_data(apb_trans);
drive_data(req)
seq_item_port.item_done();
end
endfunction;
task apb_driver::drive_data(apb_trans tr);
if(tr.dir == apb_trans::WR)
// 写信号为高的时候
begin
@(posedge apb_ifc.clk);
apb_ifc.psel <= 1'b1;
apb_ifc.penable <= 1'b0;
apb_ifc.pwrite <= 1'b1;
apb_ifc.pwdata <= tr.data;
apb_ifc.paddr <= tr.addr;
@(posedge apb_ifc.clk);
apb_ifc.penable <= 1'b1;
@(posedge apb_ifc.clk);
apb_ifc.psel <= 1'b0;
apb_ifc.penable <= 1'b0;
end
else begin
@(posedge apb_ifc.clk);
apb_ifc.psel <= 1'b1;
apb_ifc.penable <= 1'b0;
apb_ifc.pwrite <= 1'b0;
apb_ifc.paddr <= tr.addr;
@(posedge apb_ifc.clk);
apb_ifc.penable <= 1'b1;
@(posedge apb_ifc.clk);
tr.data <= apb_ifc.prdata;
apb_ifc.psel <= 1'b0;
apb_ifc.penable <= 1'b0;
end
endtask
apb_monitor
class apb_monitor extends uvm_monitor #(apb_trans)
`uvm_component_utils(apb_monitor);
virtual apb_interface apb_ifc;
bit check_enable = 1;
uvm_analysis_port #(apb_trans) mon_analysis_port;
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual task run_phase(uvm_phase phase);
extern virtual check_protocol();
endclass
function void apb_monitor::build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db #(apb_interface)::get(this,"","vif",apb_ifc))
`uvm_error("NOAPB_IFC","No apb_ifc for monitor");
apb_mon_port = new("apb_mon_port",this);
endfunction
task apb_monitor::run_phase(uvm_phase phase);
super.run_phase(phase);
apb_trans tr = apb_trans::type_id::create("tr",this);
forever begin
@(posedge apb_ifc.clk);
if(apb_ifc.psel = 1'b1 && apb_ifc.penable == 1'b1) begin
tr.dir = (apb_ifc.pwrite) ? apb_trans::WR : apb_trans::RD;
tr.addr = apb_ifc.paddr;
tr.data = (apb_ifc.pwrite) ? apb_ifc.pwdata :apb_ifc.prdata;
end
mon_analysis_port.write(tr);
end
endtask