transaction是验证平台内传输信息的基本单元,transaction会将信息从一个验证组件发送到另一个验证组件里面;transaction结构通常与协议帧结构相关;
在这里我们定义一个最简单的帧,帧包含header、payload;
class simple_packet ;
rand bit [31:0] header;
rand bit [ 7:0] payload [];
rand bit [31:0] payload_len ;
//**** 随机约束 ****//
constraints payload_le_cons {
payload_len inside {[0,32'h80000000]};
}
constraints pay_load_cons {
patload.size == payload_len;
solve payload_len before payload ; //要求先随机payload_len,后随机payload
}
//*** 函数定义 ***//
extern virtual function void pack (ref bit [7:0] bytes[]);
extern virtual function void unpack (ref bit [7:0] bytes[]); //与pack功能相反
endclass
function void simple_packet::pack(ref bit [7:0] bytes[]); //将数据帧打包成为字节数据,方便组件处理
bytes = new(8+payload_len);
for(int i=0;i<4;i++) begin
bytes[i] = header[i*8+:8];
bytes[i+payload+4] = crc[i*8+:8];
end
for(int i=0;i
interface时验证环境与DUT之间的桥梁,环境通过interface将激励输入DUT,同时又通过interface获取DUT输出;
interface simple_intf(input bit clk);
logic da_vld ;
logic [7:0] da ;
modport master ( //用来指定信号方向
output da_valid,da;
);
modport slave(
input da_valid,da;
);
endinterface
driver的功能就是将transaction转换为interface的时序信号,给DUT提供激励;
class driver;
string inst_name;
simple_packet in_pac[$]; //用来输入的transaction
simple_packet tmp_pac;
virtual simple_intf drv_intf;
function new(strring drv_name,simple_intf drv_in_intf);
inst_name = drv_name ;
drv_intf = drv_in_intf ;
endfunction
external function void run();
endcalss
function void driver::run()
while(1) begin
if(in_pac.size>0) begin
tmp_pac = in_pac.pop_front();
for(int i=0;i
monitor是用来监控DUT的输出数据,并将数据转换为transaction;
class monitor;
string inst_name;
simple_packet out_pac[$]; //用来存储要输出的transaction
simple_packet tmp_pac;
virtual simple_intf mon_intf;
function new(strring mon_name,simple_intf mon_in_intf);
inst_name = drv_name ;
mon_intf = mon_in_intf ;
endfunction
external function void run();
endcalss
function void monitor::run()
bit [31:0] pac_len ;
bit [ 7:0] payload [$];
while(1) begin
@(posedge mon_intf.clk);
if(mon_intf.slave.data_valid==1'b1) begin
pac_len++;
payload.push_back(mon_intf.slave.data);
end
else begin
tmp_pac = new();
tmp_pac.payload_len = pac_len;
tmp_pac.payload = new(pac_len);
foreach(tmp_pac.payload[i]) tmp_pac.payload[i] = payload.pop_front();
out_pac.push_back(tmp_pac);
pack_len = 0;
payload.delete();
end
end
endfunction
参考模型需要完成与UVM相同的功能。本文由于不带DUT进行仿真,参考模型直接将输入数据输出。这样验证环境的能够正常运行;
class ref_model;
string inst_name;
simple_packet in_pac[$]; //用来存储要输入的transaction
simple_packet out_pac[$]; //用来存储要输出的transaction
simple_packet tmp_pac;
function new(strring ref_name);
inst_name = drv_name ;
endfunction
external function void run();
endcalss
function void monitor::run()
while(1) begin
if(in_pac.size>0)begin
tmp_pac = in_pac.pop_front();
out_pac.push_back(tmp_pac);
end
else begin
#10ns;
end
end
endfunction
scoreboard进行数据比对,将DUT输出数据和ref输出数据进行比对;
class rscoreboard;
string inst_name;
simple_packet mon_pac[$]; //用来存储要mon的transaction
simple_packet ref_pac[$]; //用来ref输出transaction
simple_packet tmp_mon_pac;
simple_packet tmp_ref_pac;
function new(strring scb_name);
inst_name = scb_name ;
endfunction
external function void run();
endcalss
function void monitor::run()
bit payload_eq ;
while(1) begin
if(mon_pac.size>0)begin
tmp_mon_pac = mon_pac.pop_front();
if(ref_pac.size>0) begin
tmp_ref_pac = ref_pac.pop_front();
if( tmp_mon_pac.payload_len != tmp_ref_pac.payload_len) $display("mon_pac's payload_len is not desired");
else begin
payload_eq = 1 ;
foreach(tmp_mon_pac.payload[i]) begin
if(tmp_mon_pac.payload[i] != tmp_ref_pac.payload[i]) payload_eq = 0 ;
end
if(payload_eq==1) $display("compare pass");
else $display("compare error");
end
end
else begin
$display("mon_pac is not desired");
end
end
else begin
#10ns;
end
end
endfunction
env是验证环境的顶层,所以其他组件都例化在env内,且他们之间的连接需要在env内完成。本文没有使用sv的专用的连接组件,但是也可以通过task实现各个组件间的transaction传递。
class env;
driver drv;
monitor mon;
ref_model ref_model ;
scoreboard scb ;
event finish_event;
virtual simple_intf drv_intf ;
virtual simple_intf mon_intf;
extern virtual function void new();
extern virtual function void connect(); //连接env的intf和harness的intf,后面介绍
extern virtual task ref2scb();
extern virtual task mon2scb();
extern virtual task run();
endclass
function void env::new();
drv = new("drv",drv_intf);
mon = new("mon",mon_intf);
ref_model = new("ref_model");
scb = new("scb");
endfunction
task env::ref2scb();
while(1) begin
if(ref_mode.out_pac.size>0) begin
scb.ref_pac.push_back(ref_model.out_pac.pop_front());
end
else begin
#10ns;
end
end
endtask
task env::mon2scb();
while(1) begin
if(mon.out_pac.size>0) begin
scb.mon_pac.push_back(mon.out_pac.pop_front());
end
else begin
#10ns;
end
end
endtask
task run();
fork
ref2scb();
mon2scb();
drv.run();
mon.run();
ref_model.run();
scb.run();
begin
@finish_event;
end
begin
#1ms; //超时时间,当仿真在1ms还没有结束直接结束仿真
end
join_any
endtask
使用这个验证环境需要在tc里面将要发送的transaction push_back到drv和ref_model的in_pac队列里面。这样整个验证环境就能运行起来。接下来介绍harness和tc的内容:
在harness通常实现验证环境与DUT的连接,harness可以作为验证的顶层之一,以module的形式存在;
module dut(
input [7:0] din ,
input din_vld ,
output [7:0] dout ,
output dout_vld
)
assign dout = din;
assign dout_vdl = din_vld ;
endmodule
module harness();
simple_intf drv_intf ;
simplre_intf mon_intf;
dut dut_inst(
.din (drv_intf.master.da),
.din_vld (drv.intf.master.da_valif),
dout (mon_intf.slave.da),
dout_vld(mon_intf.slave.da_valid)
);
endmodule
tc是控制测试环境进行测试的,例如控制发送的激励数据等。
program test;
sim_transaction drv_pac;
env env ;
initial begin
fork
begin
env = new();
env.run() ;
end
begin
drv_pac = new();
env.drv.in_pac.push_back(drv_pac);
env.ref_model.in_pac.push_back(drv_pac);
#1000ns;
->env.finish_event;
end
join_any
end
endprogram
makefile如下:其中tb.vf为验证环境的filelist;dut.vf为dut的filelist;wave_fsdb.do为ucli的脚本文件,用例控制dump波形。
compile:
vcs -lca -kdb -full64 -debug_access+all +v2k -sverilog -notice -timescale 1ps/1fs -f tb.vf -f dut.vf …/tc/${tc}.sv -o ${tc}_simv -l ${tc}_compile.log
run: compile
./${tc}_simv +fsdbfile+${tc}.fsdb +dump_mda +fsdb+delta -ucli -do wave_fsdb.do -l ${tc}.log
ncrun:
./${tc}_simv +fsdbfile+${tc}.fsdb +dump_mda +fsdb+delta -ucli -do wave_fsdb.do -l ${tc}.log
.PHONY: compile run ncrun