UVM-demo
overview
整个项目结构如下:
有一些空格亮度问题,不一一解决了 code见代码地址
interface
定义接口,并针对不同的模块确定方向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
`ifndef GCD_IF__SV
`define GCD_IF__SV
interface gcd_if(input clk);
logic [31:0] opa;
logic [31:0] opb;
logic start;
logic reset;
logic [31:0] result;
logic done;
clocking drv_cb @(posedge clk);
output opa;
output opb;
output start;
output reset;
input result;
input done;
endclocking
clocking mon_cb @(posedge clk);
input opa;
input opb;
input start;
input reset;
input result;
input done;
endclocking
endinterface //gcd_if
`endif
定义uvm_sequence_item
,即传输的类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
`ifndef GCD_TR__SV
`define GCD_TR__SV
class gcd_tr extends uvm_sequence_item;
rand bit[31:0] opa;
rand bit[31:0] opb;
rand bit[31:0] result;
`uvm_object_utils_begin(gcd_tr)
`uvm_field_int(opa, UVM_ALL_ON | UVM_NOPACK);
`uvm_field_int(opb, UVM_ALL_ON | UVM_NOPACK);
`uvm_field_int(result, UVM_ALL_ON | UVM_NOPACK);
`uvm_object_utils_end
function new(string name = "gcd_tr");
super.new(name);
endfunction: new
function void post_randomize();
super.post_randomize();
endfunction: post_randomize
endclass: gcd_tr
`endif
uvm_field_int
在类中注册 int
类型的成员变量,UVM_ALL_ON
启用所有自动化方法对该变量的处理,包括 copy
、compare
、print
、pack
、unpack
等。UVM_NOPACK
排除 pack
和 unpack
方法对该变量的处理
这里只关心传输的类型 有这三个数据要在组件之间传输
driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
`ifndef GCD_DRIVER__SV
`define GCD_DRIVER__SV
class gcd_driver extends uvm_driver#(gcd_tr);//gcd_tr表示transaction的类型
virtual gcd_if sigs;
int send_num;//记录传输的数量
`uvm_component_utils(gcd_driver)
function new(string name = "gcd_driver", uvm_component parent = null);
super.new(name, parent);
endfunction: new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual gcd_if)::get(this, "", "drv_if", sigs))
`uvm_fatal("GCD/DRIVER/BUILD", "Driver cannot get the interface");
`uvm_info("GCD/DRIVER/BUILD", "Driver has been built", UVM_LOW);
endfunction: build_phase
extern task main_phase(uvm_phase phase);
extern task reset_dut();
extern task send_tr(gcd_tr tr);
extern function void check_phase(uvm_phase phase);
endclass: gcd_driver
task gcd_driver::main_phase(uvm_phase phase);
gcd_tr tr;
reset_dut();
send_num = 0;
while(1) begin
seq_item_port.get_next_item(tr);
send_tr(tr);
seq_item_port.item_done();
send_num++;
end
endtask: main_phase
task gcd_driver::reset_dut();
@(sigs.drv_cb);
sigs.reset = 1;
sigs.start = 0;
sigs.opa = 0;
sigs.opb = 0;
@(sigs.drv_cb);
sigs.reset = 0;
endtask: reset_dut
task gcd_driver::send_tr(gcd_tr tr);
@(sigs.drv_cb);
sigs.start = 1;
sigs.opa = tr.opa;
sigs.opb = tr.opb;
@(sigs.drv_cb);
sigs.start = 0;
while(!sigs.done)
@(sigs.drv_cb);
repeat(10)
@(sigs.drv_cb);
endtask: send_tr
function void gcd_driver::check_phase(uvm_phase phase);
super.check_phase(phase);
`uvm_info("GCD/DRIVE/POST", $psprintf("%0d item(s) have(has) been sent", send_num), UVM_LOW);
endfunction
`endif
uvm_info("GCD/DRIVER/BUILD", "Driver has been built", UVM_LOW);
- 第一个参数
"GCD/DRIVER/BUILD"
:消息的标识符或标签,方便分类和过滤。 - 第三个参数
UVM_LOW
:消息的冗余级别UVM_NONE
(0):无冗余,最高优先级,始终显示。UVM_LOW
(100):低冗余级别。UVM_MEDIUM
(200):中等冗余级别。UVM_HIGH
(300):高冗余级别。UVM_FULL
(400):完整冗余级别。UVM_DEBUG
(500):调试级别,最详细的信息。
if (!uvm_config_db#(virtual gcd_if)::get(this, "", "drv_if", sigs))
从 UVM 配置数据库中获取名为 drv_if
的interface gcd_if
,并将其赋值给驱动器内部的变量 sigs
。如果获取失败,则报告一个致命错误,终止仿真。
uvm_config_db
:UVM 提供的配置数据库类,用于在组件之间传递配置信息。
driver主要完成复位,随机数据的生成,等待计算结束
monitor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
`ifndef GCD_MONITOR__SV
`define GCD_MONITOR__SV
class gcd_monitor extends uvm_monitor;
int mon_num;
virtual gcd_if sigs;
uvm_analysis_port #(gcd_tr) mon_ap;
`uvm_component_utils(gcd_monitor)
function new(string name = "gcd_monitor", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
if(!uvm_config_db#(virtual gcd_if)::get(this, "", "mon_if", sigs))
`uvm_fatal("GCD/MONITOR/BUILD", "Monitor cannot get interface");
mon_ap = new("mon_ap", this);
`uvm_info("GCD/MON/BUILD", "Monitor has been built", UVM_LOW);
endfunction: build_phase
extern task main_phase(uvm_phase phase);
extern task collect_tr(gcd_tr tr);
extern function void check_phase(uvm_phase phase);
endclass: gcd_monitor
task gcd_monitor::main_phase(uvm_phase phase);
gcd_tr tr;
mon_num = 0;
while(1) begin
tr = new("tr");//新的tr,要和旧的做比较
collect_tr(tr);
mon_ap.write(tr);
mon_num++;
end
endtask: main_phase
task gcd_monitor::collect_tr(gcd_tr tr);
while(1) begin
@(sigs.mon_cb);
if(sigs.start)
break;
end
`uvm_info("GCD/MON/COLLECT", "Get the input data", UVM_LOW);
tr.opa = sigs.opa;
tr.opb = sigs.opb;
while(1) begin
@(sigs.mon_cb);
if(sigs.done)
break;
end
tr.result = sigs.result;
`uvm_info("GCD/MON/COLLECT", "Get the result data", UVM_LOW);
endtask: collect_tr
function void gcd_monitor::check_phase(uvm_phase phase);
super.check_phase(phase);
`uvm_info("GCD/MON/CHECK", $psprintf("%0d item(s) have(has) been monitored", mon_num), UVM_LOW);
endfunction: check_phase
`endif
sequencer
1
2
3
4
5
6
7
8
9
`ifndef GCD_SEQUENCER__SV
`define GCD_SEQUENCER__SV
class gcd_sequencer extends uvm_sequencer #(gcd_tr);
function new(string name = "gcd_sequencer", uvm_component parent = null);
super.new(name, parent);
endfunction: new
`uvm_component_utils(gcd_sequencer)
endclass: gcd_sequencer
`endif
没什么可说的,套模板
agent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class gcd_agent extends uvm_agent;
gcd_driver drv;
gcd_monitor mon;
gcd_sequencer sqr;
uvm_analysis_port #(gcd_tr) ap;//ap在代理(agent)内部。而mon_ap在monitor内部
function new(string name = "gcd_agent", uvm_component parent = null);//组件的父级,`null`表示顶级组件
super.new(name, parent);
endfunction: new
extern virtual function void build_phase(uvm_phase phase);//外部函数,在class外提供
extern virtual function void connect_phase(uvm_phase phase);//virtual:允许子类重写此方法。
`uvm_component_utils(gcd_agent)
endclass: gcd_agent
function void gcd_agent::build_phase(uvm_phase phase);
super.build_phase(phase);
if(is_active == UVM_ACTIVE) begin
sqr = gcd_sequencer::type_id::create("sqr", this);
drv = gcd_driver::type_id::create("drv", this);
end
mon = gcd_monitor::type_id::create("mon", this);
endfunction: build_phase
function void gcd_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.mon_ap;
endfunction: connect_phase
`endif
agent是用于封装驱动器(driver)、监视器(monitor)和序列器(sequencer)组件的顶层模块。
uvm_analysis_port用于发布(publish)事务的分析端口,指定分析端口传输的数据类型为 gcd_tr
利用 if(is_active == UVM_ACTIVE)
来去除部分component
连接驱动器的 seq_item_port
与序列器的 seq_item_export
,以便驱动器能够从序列器获取事务。
将monitor的分析端口连接到agent的分析端口,使得monitor捕获的事务可以传递给agent的外部。
上述完成了基本的UV Component
在此基础上进一步的有:i_agt、o_agt、re_model、scb
env封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
`ifndef GCD_ENV__SV
`define GCD_ENV__SV
class gcd_env extends uvm_env;
gcd_agent i_agt;
gcd_agent o_agt;
gcd_re_model mdl;
gcd_scb scb;
uvm_tlm_analysis_fifo #(gcd_tr) agt_scb_fifo;
uvm_tlm_analysis_fifo #(gcd_tr) agt_mdl_fifo;
uvm_tlm_analysis_fifo #(gcd_tr) mdl_scb_fifo;
function new(string name = "gcd_env", uvm_component parent = null);
super.new(name, parent);
endfunction: new
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
`uvm_component_utils(gcd_env);
endclass: gcd_env
function void gcd_env::build_phase(uvm_phase phase);
super.build_phase(phase);
i_agt = gcd_agent::type_id::create("i_agt", this);
o_agt = gcd_agent::type_id::create("o_agt", this);
i_agt.is_active = UVM_ACTIVE;
o_agt.is_active = UVM_PASSIVE;
mdl = gcd_re_model::type_id::create("mdl", this);
scb = gcd_scb::type_id::create("scb", this);
agt_scb_fifo = new("agt_scb_fifo", this);
agt_mdl_fifo = new("agt_mdl_fifo", this);
mdl_scb_fifo = new("mdl_scb_fifo", this);
endfunction: build_phase
function void gcd_env::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.mdl_port.connect(mdl_scb_fifo.blocking_get_export);//④
o_agt.ap.connect(agt_scb_fifo.analysis_export);//⑤
scb.mon_port.connect(agt_scb_fifo.blocking_get_export);//⑥
endfunction: connect_phase
`endif
连接关系:
- ①
i_agt
所捕获到的事务数据发送到 agt_mdl_fifo,供后续模块使用 - ②
agt_mdl_fifo
中以阻塞方式获取输入事务数据 - ③将模型模块生成的事务数据传递到
mdl_scb_fifo
中 - ④从
mdl_scb_fifo
获取模型模块的输出事务数据,通常用于对比检查(验证设计行为是否符合预期)。 - ⑤输出代理将其监控的事务数据发布到
agt_scb_fifo
- ⑥从
agt_scb_fifo
获取输出代理的数据,以便与期望结果进行比较
下面是scoreboard,从两个fifo中拿数据对比
1
2
uvm_blocking_get_port #(gcd_tr) mon_port;
uvm_blocking_get_port #(gcd_tr) mdl_port;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
`ifndef GCD_SCB__SV
`define GCD_SCB__SV
class gcd_scb extends uvm_scoreboard;
uvm_blocking_get_port #(gcd_tr) mon_port;
uvm_blocking_get_port #(gcd_tr) mdl_port;
int check_num;
`uvm_component_utils(gcd_scb)
function new(string name = "gcd_scb", uvm_component parent = null);
super.new(name, parent);
endfunction: new
extern virtual function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
extern virtual function void check_phase(uvm_phase phase);
endclass: gcd_scb
function void gcd_scb::build_phase(uvm_phase phase);
super.build_phase(phase);
mon_port = new("mon_port", this);
mdl_port = new("model_port", this);
`uvm_info("GCD/SCB/BUILD", "The scoreboard has been built", UVM_LOW);
endfunction: build_phase
function void gcd_scb::check_phase(uvm_phase phase);
super.check_phase(phase);
`uvm_info("GCD/SCB/CEHCK", $psprintf("%0d item(s) have(has) been checked by scoreboard", check_num), UVM_LOW);
endfunction: check_phase
task gcd_scb::main_phase(uvm_phase phase);
gcd_tr mdl_tr, mon_tr;
bit result;
check_num = 0;
super.main_phase(phase);
while(1)begin
mon_port.get(mon_tr);
mdl_port.get(mdl_tr);
result = mon_tr.compare(mdl_tr);
if(result) begin
`uvm_info("GCD/SCB/MAIN", "Compare SUCCESSFUL", UVM_LOW);
end else begin
`uvm_error("GCD/SCB/MAIN", "Compare FATILED");
$display("The model pkt is");
mdl_tr.print();
$display("The mon pkt is");
mon_tr.print();
end
check_num++;
end
endtask: main_phase
`endif
下面是refrence_model,有两个fifo接口
1
2
uvm_blocking_get_port #(gcd_tr) port;//从agt_mdl_fifo获取输入的数
uvm_analysis_port #(gcd_tr) ap;//往mdl_scb_fifo写golden值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
`ifndef GCD_RE_MODEL__SV
`define GCD_RE_MODEL__SV
class gcd_re_model extends uvm_component;
int re_num;
uvm_blocking_get_port #(gcd_tr) port;
uvm_analysis_port #(gcd_tr) ap;
function new(string name = "gcd_re_model", uvm_component parent = null);
super.new(name, parent);
endfunction: new
extern function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
extern function void check_phase(uvm_phase phase);
extern virtual task gcd_re_proc(gcd_tr tr);
`uvm_component_utils(gcd_re_model)
endclass: gcd_re_model
function void gcd_re_model::build_phase(uvm_phase phase);
super.build_phase(phase);
port = new("port", this);
ap = new("ap", this);
`uvm_info("GCD/RE_MODEL/BUILD", "The reference model has been built", UVM_LOW);
endfunction: build_phase
function void gcd_re_model::check_phase(uvm_phase phase);
super.check_phase(phase);
`uvm_info("GCD/RE_MODEL/CEHCK", $psprintf("%0d item(s) have(has) been processed by the re-model", re_num), UVM_LOW);
endfunction: check_phase
task gcd_re_model::main_phase(uvm_phase phase);
gcd_tr tr;
super.main_phase(phase);
re_num = 0;
while(1) begin
port.get(tr);
`uvm_info("GCD/RE_MODEL/MAIN", "get one tr", UVM_LOW);
gcd_re_proc(tr);
re_num++;
end
endtask: main_phase
task gcd_re_model::gcd_re_proc(gcd_tr tr);
gcd_tr after_tr;
bit [31:0] devidend;
bit [31:0] divisor;
bit [31:0] remainder;
after_tr = new("after_tr");
after_tr.copy(tr);
after_tr.result = 0;
if(tr.opa > tr.opb) begin
devidend = tr.opa;
divisor = tr.opb;
end else begin
devidend = tr.opb;
divisor = tr.opa;
end
while(1) begin
remainder = devidend % divisor;
if(remainder == 0)
break;
devidend = divisor;
divisor = remainder;
end
after_tr.result = divisor;
ap.write(after_tr);
`uvm_info("GCD/RE_MODEL/PROC", "processed one tr and sent", UVM_LOW);
endtask: gcd_re_proc
`endif
port.get(tr)
:port的值赋给tr
env搭建完了,接下来是testcase
testcase
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
`ifndef BASE_TEST__SV
`define BASE_TEST__SV
class base_test extends uvm_test;
gcd_env env;
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void report_phase(uvm_phase phase);
`uvm_component_utils(base_test);
endclass: base_test
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = gcd_env::type_id::create("env", this);
endfunction: build_phase
function void base_test::report_phase(uvm_phase phase);
uvm_report_server server;
int err_num;
super.report_phase(phase);
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
`endif
没什么好说的,套模板。需要在上述基础上构建自己项目的testcase
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
`ifndef GCD_RANDOM_TC__SV
`define GCD_RANDOM_TC__SV
class gcd_random_seq extends uvm_sequence #(gcd_tr);
gcd_tr tr;
function new(string name = "gcd_random_seq");
super.new(name);
endfunction: new
virtual task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat(100000) begin
`uvm_do(tr);
end
#100
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask: body
`uvm_object_utils(gcd_random_seq)
endclass: gcd_random_seq
class gcd_random_tc extends base_test;
function new(string name = "gcd_random_tc", uvm_component parent = null);
super.new(name, parent);
endfunction: new
extern virtual function void build_phase(uvm_phase phase);
`uvm_component_utils(gcd_random_tc)
endclass: gcd_random_tc
function void gcd_random_tc::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence",
gcd_random_seq::type_id::get());
endfunction: build_phase
`endif
uvm_sequence
,gcd_random_seq
在body中用于产生随机激励。它包含激励生成并执行激励。
raise_objection()
和 drop_objection()
用于控制仿真过程,表示仿真在运行这个序列时需要延长时间。raise_objection()
通常在开始激励时调用,drop_objection()
在激励完成后调用,以表示当前的激励完成。
uvm_do(tr)
宏用于生成一个transaction,并将其发送到与序列器相连的 driver 中
testbench
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "gcd_if.sv"
`include "gcd_tr.sv"
module top_tb;
reg clk;
wire reset;
wire [31:0] opa;
wire [31:0] opb;
wire start;
wire done;
wire [31:0] result;
gcd_if input_if(clk);
gcd_if output_if(clk);
GCD dut(
.CLK(clk),
.RESET(reset),
.OPA(opa),
.OPB(opb),
.START(start),
.DONE(done),
.RESULT(result)
);
assign reset = input_if.reset;
assign opa = input_if.opa;
assign opb = input_if.opb;
assign start = input_if.start;
assign input_if.done = done;
assign input_if.result = 0;
assign output_if.opa = opa;
assign output_if.opb = opb;
assign output_if.start = start;
assign output_if.result = result;
assign output_if.done = done;
initial begin
clk = 0;
forever begin
#10 clk = ~clk;
end
end
initial begin
run_test();
#1000000
$display("TIMEOUT");
$finish();
end
initial begin
uvm_config_db#(virtual gcd_if)::set(null, "uvm_test_top.env.i_agt.drv", "drv_if", input_if);
uvm_config_db#(virtual gcd_if)::set(null, "uvm_test_top.env.i_agt.mon", "mon_if", input_if);
uvm_config_db#(virtual gcd_if)::set(null, "uvm_test_top.env.o_agt.mon", "mon_if", output_if);
end
initial begin
$fsdbDumpfile("gcd.fsdb");
$fsdbDumpvars;
end
endmodule