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);endfunctionvirtual function void build_phase(uvm_phase phase);super.build_phase(phase);endfunctionendclass
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);endfunctionendclass
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发送的数据,并将其传递给dutvirtual task run_phase(uvm_phase phase);get_and_drive();endtaskvirtual 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_transif(!$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(); // 传输完成endendtaskvirtual protected task drive_data(gpio_transfer gpio_tr);.............// 将传入的tr的数据驱动到对应的接口上endtaskendclass
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_portumv_analysis_port #(my_data) mon_analysis_port;function new(string name,uvm_component parent);super.new(name,parent);endfunctionvirtual function void build_phase(uvm_phase phase);super.build_phase(phase);// 创建事务传输接口mon_analysis_port = new("mon_analysis_port",this);endfunctionvirtual 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");endendfunctionvirtual 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];// 将接口信号给到transactiondata_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);endendtaskvirtual function void check_protocol();// Function to check basic protocol specsendfunction 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);endfunctionextern 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);endclassfunction 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"});endfunctiontask 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);endtasktask apb_driver::run_phase(uvm_phase phase);get_and_drive();endtasktask 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(); endendfunction;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;endendtask
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);endfunctionextern virtual function void build_phase(uvm_phase phase);extern virtual task run_phase(uvm_phase phase);extern virtual check_protocol();
endclassfunction 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);endfunctiontask 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) begintr.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;endmon_analysis_port.write(tr);endendtask