1 Adder Design
加法器设计在时钟的上升沿产生两个变量的加法。复位信号用于clear out信号。
注:加法器可以很容易地用组合逻辑开发。引入时钟和重置,使其具有测试台代码中时钟和重置的样子/风格。
module adder(input clk, reset, input [7:0] in1, in2, output reg [8:0] out);
always@(posedge clk or posedge reset) begin
if(reset) out <= 0;
else out <= in1 + in2;
end
endmodule
2 Testbench Code
2.1 Sequence Item
sequence item类包含必要的激励产生数据成员。
class seq_item extends uvm_sequence_item;
rand bit [7:0] ip1, ip2;
bit [8:0] out;
function new(string name = "seq_item");
super.new(name);
endfunction
`uvm_object_utils_begin(seq_item)
`uvm_field_int(ip1,UVM_ALL_ON)
`uvm_field_int(ip2,UVM_ALL_ON)
`uvm_object_utils_end
constraint ip_c {ip1 < 100; ip2 < 100;}
endclass
2.2 Sequence
sequence创建激励并通过sequencer驱动到driver。
class base_seq extends uvm_sequence#(seq_item);
seq_item req;
`uvm_object_utils(base_seq)
function new (string name = "base_seq");
super.new(name);
endfunction
task body();
`uvm_info(get_type_name(), "Base seq: Inside Body", UVM_LOW);
`uvm_do(req);
endtask
endclass
2.3 Sequencer
sequencer是在sequence和driver之间建立连接的中介。
class seqcr extends uvm_sequencer#(seq_item);
`uvm_component_utils(seqcr)
function new(string name = "seqcr", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
endclass
2.4 Driver
driver驱动随机后的事务transactions或者sequence item给pin-level接口的DUT。
class driver extends uvm_driver#(seq_item);
virtual add_if vif;
`uvm_component_utils(driver)
function new(string name = "driver", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual add_if) :: get(this, "", "vif", vif))
`uvm_fatal(get_type_name(), "Not set at top level");
endfunction
task run_phase (uvm_phase phase);
forever begin
// Driver to the DUT
seq_item_port.get_next_item(req);
`uvm_info(get_type_name, $sformatf("ip1 = %0d, ip2 = %0d", req.ip1, req.ip2), UVM_LOW);
vif.ip1 <= req.ip1;
vif.ip2 <= req.ip2;
seq_item_port.item_done();
end
endtask
2.5 Monitor
UVM monitor是一个passive component,用于使用虚拟接口捕获DUT信号并将其转换为序列项sequence item格式。
class monitor extends uvm_monitor;
virtual add_if vif;
uvm_analysis_port #(seq_item) item_collect_port;
seq_item mon_item;
`uvm_component_utils(monitor)
function new(string name = "monitor", uvm_component parent = null);
super.new(name, parent);
item_collect_port = new("item_collect_port", this);
mon_item = new();
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual add_if) :: get(this, "", "vif", vif))
`uvm_fatal(get_type_name(), "Not set at top level");
endfunction
task run_phase (uvm_phase phase);
forever begin
wait(!vif.reset);
@(posedge vif.clk);
mon_item.ip1 = vif.ip1;
mon_item.ip2 = vif.ip2;
`uvm_info(get_type_name, $sformatf("ip1 = %0d, ip2 = %0d", mon_item.ip1, mon_item.ip2), UVM_HIGH);
@(posedge vif.clk);
mon_item.out = vif.out;
item_collect_port.write(mon_item);
end
endtask
endclass
2.6 Agent
agent是包含并连接driver,monitor和sequencer实例的容器。
class agent extends uvm_agent;
`uvm_component_utils(agent)
driver drv;
seqcr seqr;
monitor mon;
function new(string name = "agent", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(get_is_active == UVM_ACTIVE) begin
drv = driver::type_id::create("drv", this);
seqr = seqcr::type_id::create("seqr", this);
end
mon = monitor::type_id::create("mon", this);
endfunction
function void connect_phase(uvm_phase phase);
if(get_is_active == UVM_ACTIVE) begin
drv.seq_item_port.connect(seqr.seq_item_export);
end
endfunction
endclass
2.7 Scoreboard
UVM scoreboard是检查DUT功能的组件。它使用analysis port从monitor接收事务以进行检查。
class scoreboard extends uvm_scoreboard;
uvm_analysis_imp #(seq_item, scoreboard) item_collect_export;
seq_item item_q[$];
`uvm_component_utils(scoreboard)
function new(string name = "scoreboard", uvm_component parent = null);
super.new(name, parent);
item_collect_export = new("item_collect_export", this);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
function void write(seq_item req);
item_q.push_back(req);
endfunction
task run_phase (uvm_phase phase);
seq_item sb_item;
forever begin
wait(item_q.size > 0);
if(item_q.size > 0) begin
sb_item = item_q.pop_front();
$display("----------------------------------------------------------------------------------------------------------");
if(sb_item.ip1 + sb_item.ip2 == sb_item.out) begin
`uvm_info(get_type_name, $sformatf("Matched: ip1 = %0d, ip2 = %0d, out = %0d", sb_item.ip1, sb_item.ip2, sb_item.out),UVM_LOW);
end
else begin
`uvm_error(get_name, $sformatf("NOT matched: ip1 = %0d, ip2 = %0d, out = %0d", sb_item.ip1, sb_item.ip2, sb_item.out));
end
$display("----------------------------------------------------------------------------------------------------------");
end
end
endtask
endclass
2.8 Environment
env提供了包含agent,scoreboard,和其他验证组件的容器。
class env extends uvm_env;
`uvm_component_utils(env)
agent agt;
scoreboard sb;
function new(string name = "env", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
agt = agent::type_id::create("agt", this);
sb = scoreboard::type_id::create("sb", this);
endfunction
function void connect_phase(uvm_phase phase);
agt.mon.item_collect_port.connect(sb.item_collect_export);
endfunction
endclass
2.9 Test
test位于组件层次顶部。
class base_test extends uvm_test;
env env_o;
base_seq bseq;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env_o = env::type_id::create("env_o", this);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
bseq = base_seq::type_id::create("bseq");
repeat(10) begin
#5; bseq.start(env_o.agt.seqr);
end
phase.drop_objection(this);
`uvm_info(get_type_name, "End of testcase", UVM_LOW);
endtask
endclass
2.10 Testbench Top
testbench是一个静态容器,实例化DUT和接口。
module tb_top;
bit clk;
bit reset;
always #2 clk = ~clk;
initial begin
//clk = 0;
reset = 1;
#5;
reset = 0;
end
add_if vif(clk, reset);
adder DUT(.clk(vif.clk),.reset(vif.reset),.in1(vif.ip1),.in2(vif.ip2),.out(vif.out));
initial begin
// set interface in config_db
uvm_config_db#(virtual add_if)::set(uvm_root::get(), "*", "vif", vif);
end
initial begin
run_test("base_test");
end
endmodule
2.11 Execute Complete Code
Output:
UVM_INFO base_seq.sv(10) @ 5: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(20) @ 5: uvm_test_top.env_o.agt.drv [driver] ip1 = 22, ip2 = 14
----------------------------------------------------------------------------------------------------------
UVM_INFO scoreboard.sv(28) @ 10: uvm_test_top.env_o.sb [scoreboard] Matched: ip1 = 22, ip2 = 14, out = 36
----------------------------------------------------------------------------------------------------------
UVM_INFO base_seq.sv(10) @ 10: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(20) @ 10: uvm_test_top.env_o.agt.drv [driver] ip1 = 92, ip2 = 70
UVM_INFO base_seq.sv(10) @ 15: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(20) @ 15: uvm_test_top.env_o.agt.drv [driver] ip1 = 4, ip2 = 62
----------------------------------------------------------------------------------------------------------
UVM_INFO scoreboard.sv(28) @ 18: uvm_test_top.env_o.sb [scoreboard] Matched: ip1 = 92, ip2 = 70, out = 162
----------------------------------------------------------------------------------------------------------
UVM_INFO base_seq.sv(10) @ 20: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(20) @ 20: uvm_test_top.env_o.agt.drv [driver] ip1 = 57, ip2 = 60
UVM_INFO base_seq.sv(10) @ 25: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(20) @ 25: uvm_test_top.env_o.agt.drv [driver] ip1 = 47, ip2 = 1
----------------------------------------------------------------------------------------------------------
UVM_INFO scoreboard.sv(28) @ 26: uvm_test_top.env_o.sb [scoreboard] Matched: ip1 = 57, ip2 = 60, out = 117
----------------------------------------------------------------------------------------------------------
UVM_INFO base_seq.sv(10) @ 30: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(20) @ 30: uvm_test_top.env_o.agt.drv [driver] ip1 = 74, ip2 = 84
----------------------------------------------------------------------------------------------------------
UVM_INFO scoreboard.sv(28) @ 34: uvm_test_top.env_o.sb [scoreboard] Matched: ip1 = 47, ip2 = 1, out = 48
----------------------------------------------------------------------------------------------------------
UVM_INFO base_seq.sv(10) @ 35: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(20) @ 35: uvm_test_top.env_o.agt.drv [driver] ip1 = 42, ip2 = 70
UVM_INFO base_seq.sv(10) @ 40: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(20) @ 40: uvm_test_top.env_o.agt.drv [driver] ip1 = 48, ip2 = 36
----------------------------------------------------------------------------------------------------------
UVM_INFO scoreboard.sv(28) @ 42: uvm_test_top.env_o.sb [scoreboard] Matched: ip1 = 42, ip2 = 70, out = 112
----------------------------------------------------------------------------------------------------------
UVM_INFO base_seq.sv(10) @ 45: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(20) @ 45: uvm_test_top.env_o.agt.drv [driver] ip1 = 91, ip2 = 40
----------------------------------------------------------------------------------------------------------
UVM_INFO scoreboard.sv(28) @ 50: uvm_test_top.env_o.sb [scoreboard] Matched: ip1 = 91, ip2 = 40, out = 131
----------------------------------------------------------------------------------------------------------
UVM_INFO base_seq.sv(10) @ 50: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(20) @ 50: uvm_test_top.env_o.agt.drv [driver] ip1 = 75, ip2 = 55
UVM_INFO base_test.sv(25) @ 50: uvm_test_top [base_test] End of testcase