日常记录(40)UVM框架构建要点
https://gitee.com/bai-mengwei/uvm_practive_first
顶层连接与通信
顶层,是top_tb.sv,包括了interface、dut、和testbatch,驱动时钟、复位。
并包含uvm的宏文件svh、导入uvm的包。以及包含其它各个模块的文件。
引入uvm
`include "uvm_macros.svh" import uvm_pkg::*;
interface
my_if input_if(clk, rst_n); my_if output_if(clk, rst_n);
连接DUT
dut my_dut(.clk(clk),
.rst_n(rst_n),
.rxd(input_if.data),
.rx_dv(input_if.valid),
.txd(output_if.data),
.tx_en(output_if.valid));
启动testbench与连接testbench
initial begin
/* run_test("my_env"); */
/* run_test("base_test"); */
/* run_test("my_case0"); */
run_test();
/* my_driver drv; */
/* drv=new("drv", null); */
/* drv.main_phase(null); */
/* $finish; */
end
initial begin
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.drv", "vif", input_if);
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.mon", "vif", input_if);
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.o_agt.mon", "vif", output_if);
end
启动一个test后
以base_test为例,可以使用default_sequence,自动完成sequence的启动过程。
report_phase为后续的处理过程
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
uvm_config_db#(uvm_object_wrapper)::set(
this, "env.i_agt.sqr.main_phase","default_sequence", my_sequence::type_id::get());
endfunction: build_phase
function void base_test::report_phase(uvm_phase phase);
uvm_report_server server;
int err_num;
server = get_report_server();
err_num = server.get_severity_count(UVM_ERROR);
if(err_num!=0) begin
$display("TEST CASE FAILED");
end else begin
$display("TEST CASE PASSED");
end
endfunction: report_phase
不使用而手动的启动(在env中)
手动指明了seq给一个i_agt.sqr发送trans
task main_phase(uvm_phase phase);
my_sequence seq;
phase.raise_objection(this);
seq = my_sequence::type_id::create("seq");
seq.start(i_agt.sqr);
phase.drop_objection(this);
endtask
在env中,
执行了config_db中的树形访问对象创建、包括agt、mdl、scb。
其中的fifo,为agt->mdl->scb与agt->scb,进行了通信的搭建。涉及的ap与port,需要在agt、mdl、scb中定义。
其中agt的ap设置为mon的ap(引用)。
default_sequence
定义过程中指明了sqr。
function void build_phase(uvm_phase phase);
super.build_phase(phase);
i_agt=my_agent::type_id::create("i_agt", this);
o_agt=my_agent::type_id::create("o_agt", this);
i_agt.is_active = UVM_ACTIVE;
o_agt.is_active = UVM_PASSIVE;
mdl=my_model::type_id::create("mdl", this);
scb=my_scoreboard::type_id::create("scb", this);
agt_mdl_fifo = new("agt_mdl_fifo", this);
agt_scb_fifo = new("agt_scb_fifo", this);
mdl_scb_fifo = new("mdl_scb_fifo", this);
uvm_config_db#(uvm_object_wrapper)::set(
this, "i_agt.sqr.main_phase", "default_sequence", my_sequence::type_id::get());
endfunction: build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
i_agt.ap.connect(agt_mdl_fifo.analysis_export);
mdl.port.connect(agt_mdl_fifo.blocking_get_export);
mdl.ap.connect(mdl_scb_fifo.analysis_export);
scb.exp_port.connect(mdl_scb_fifo.blocking_get_export);
o_agt.ap.connect(agt_scb_fifo.analysis_export);
scb.act_port.connect(agt_scb_fifo.blocking_get_export);
endfunction: connect_phase
Agent中的内容
有ap,用于发送信息,然后是drv、mon、sqr。
drv用于驱动dut,mon用于实现数据发送,sqr用于装载seq,seq用于生成trans。
drv和sqr只在前向送dut数据才有。
connect过程中,seq_item_port和seq_item_export数据内置变量。drv向sqr发起申请数据请求,sqr从seq给予数据。
class my_agent extends uvm_agent;
// data or class properties
`uvm_component_utils(my_agent)
my_driver drv;
my_monitor mon;
my_sequencer sqr;
uvm_analysis_port#(my_transation) ap;
function new(string name="my_agent", uvm_component parent);
super.new(name, parent);
endfunction : new
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
endclass : my_agent
function void my_agent::build_phase(uvm_phase phase);
super.build_phase(phase);
if(is_active==UVM_ACTIVE) begin
drv=my_driver::type_id::create("drv", this);
sqr=my_sequencer::type_id::create("sqr", this);
end
mon=my_monitor::type_id::create("mon", this);
endfunction
function void my_agent::connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(is_active==UVM_ACTIVE) begin
drv.seq_item_port.connect(sqr.seq_item_export);
end
ap = mon.ap;
endfunction
Driver的内容
driver构建链接关系
https://gitee.com/bai-mengwei/uvm_practive_first/blob/master/my_driver.sv#L12
function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("my_driver", "build_phase is called", UVM_LOW);
if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("my_driver", "interface set error!");
end
endfunction: build_phase
driver从sqr获取数据
https://gitee.com/bai-mengwei/uvm_practive_first/blob/master/my_driver.sv#L52
seq_item_port.try_next_item
task main_phase(uvm_phase phase);
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n) begin
@(posedge vif.clk);
end
while (1) begin
/* seq_item_port.get_next_item(req); */
seq_item_port.try_next_item(req);
if (req==null) begin
@(posedge vif.clk);
end else begin
drive_one_pkt(req);
seq_item_port.item_done();
end
end
endtask
driver驱动一个数据包
https://gitee.com/bai-mengwei/uvm_practive_first/blob/master/my_driver.sv#L71
task drive_one_pkt(my_transation tr);
byte unsigned data_q[];
int data_size;
data_size = tr.pack_bytes(data_q) / 8;
`uvm_info("drive_one_pkt", "start drive one packet", UVM_LOW);
repeat (3) begin
@(posedge vif.clk);
end
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("drive_one_pkt", "one packet send over", UVM_LOW);
endtask
Sequence
uvm_do:创建实例my_tr,将其随机化,送给sqr。然后drv从sqr获取数据,进行驱动。
class my_sequence extends uvm_sequence#(my_transation);
// data or class properties
`uvm_object_utils(my_sequence)
my_transation my_tr;
// initialization
function new(string name="my_sequence");
super.new(name);
endfunction : new
virtual task body();
if(starting_phase!=null) begin
starting_phase.raise_objection(this);
end
repeat (10) begin
`uvm_do(my_tr)
end
#1000;
if (starting_phase!=null) begin
starting_phase.drop_objection(this);
end
endtask
endclass : my_sequence
model
参考模型。给予输入数据,得到作为参考的正确输出数据,将其发送给scb
https://gitee.com/bai-mengwei/uvm_practive_first/blob/master/my_model.sv#L25
class my_model extends uvm_component;
// data or class properties
`uvm_component_utils(my_model)
uvm_blocking_get_port #(my_transation) port;
uvm_analysis_port #(my_transation) ap;
// initialization
function new(string name="my_model", uvm_component parent);
super.new(name, parent);
endfunction : new
extern function void build_phase(uvm_phase phase);
extern task main_phase(uvm_phase phase);
endclass : my_model
function void my_model::build_phase(uvm_phase phase);
super.build_phase(phase);
port = new("port", this);
ap = new("ap", this);
endfunction
task my_model::main_phase(uvm_phase phase);
my_transation tr;
my_transation new_tr;
super.main_phase(phase);
while (1) begin
port.get(tr);
new_tr = new("new_tr");
new_tr.copy(tr);
`uvm_info("main_phase", "tr copy finished.", UVM_LOW)
new_tr.print();
ap.write(new_tr);
end
endtask
Scoreboard
获取实际输出与期望输出,进行对比。
由于实际输出消耗仿真时间,期望输出不需要,因此常,if成立。
class my_scoreboard extends uvm_scoreboard;
// data or class properties
`uvm_component_utils(my_scoreboard)
my_transation expect_queue[$];
uvm_blocking_get_port #(my_transation) exp_port;
uvm_blocking_get_port #(my_transation) act_port;
// initialization
function new(string name="my_scoreboard", uvm_component parent);
super.new(name, parent);
endfunction : new
extern function void build_phase(uvm_phase phase);
extern task main_phase(uvm_phase phase);
endclass : my_scoreboard
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_transation get_exp, get_act, tmp_trans;
bit result;
super.main_phase(phase);
fork
while (1) begin
exp_port.get(get_exp);
expect_queue.push_back(get_exp);
end
while (1) begin
act_port.get(get_act);
if (expect_queue.size>0) begin
tmp_trans=expect_queue.pop_front;
result=get_act.compare(tmp_trans);
if(result) begin
`uvm_info("main_phase", "compare success", UVM_LOW)
end else begin
`uvm_info("main_phase", "compare failed!", UVM_LOW)
$display("actual is:");
get_act.my_print();
$display("expect is:");
tmp_trans.my_print();
end
end else begin
`uvm_error("main_phase", "expect queue is empty!")
$display("the unexpect pkt is");
get_act.my_print();
end
end
join
endtask
Transaction
注册factory时,采用begin。。。end,其中内容用field,可以更好的使用print、compare、pack_bytes、unpack_bytes函数。
`uvm_object_utils_begin(my_transation)
`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_end
Le vent se lève! . . . il faut tenter de vivre!
Le vent se lève! . . . il faut tenter de vivre!

浙公网安备 33010602011771号