将Synopsys AXI VIP集成到自己原有的验证环境
1 生成VIP
按照生成步骤生成VIP,这里生成svt_axi_system_env,生成后只需要include和src文件夹,放在…/tb/axi_vip路径下(可根据个人习惯修改)
2 Makefile
在compile步骤添加宏定义
+define+UVM_PACKER_MAX_BYTES=1500000 +define+UVM_DISABLE_AUTO_ITEM_RECORDING
3 filelist
filelist中添加路径(根据实际修改"…/tb/axi_vip"路径)
+incdir+../tb/axi_vip/include/verilog
+incdir+../tb/axi_vip/include/sverilog
+incdir+../tb/axi_vip/src/verilog/vcs
+incdir+../tb/axi_vip/src/sverilog/vcs
../tb/axi_vip/include/sverilog/svt_axi_if.svi
../tb/axi_vip/include/sverilog/svt_amba.uvm.pkg
原有验证环境将environment、top_test、sequence分别包成了package,所以在package中import VIP包即可
env_pkg中添加下段代码
import svt_uvm_pkg::*;
import svt_amba_uvm_pkg::*;
top_test_pkg中添加下段代码
import svt_uvm_pkg::*;
import svt_amba_uvm_pkg::*;
sequence_pkg中添加下段代码,两个sequence后续会描述
import svt_uvm_pkg::*;
import svt_amba_uvm_pkg::*;
axi_master_wr_sequence.sv
axi_slave_mem_response_sequence.sv
4 environment
在顶层environment中添加子AXI environment
声明部分添加下段代码
svt_axi_system_env axi_system_env;
build_phase添加下段代码
axi_system_env = svt_axi_system_env::type_id::create("axi_system_env", this);
5 sequence
(1)axi_slave_mem_response_sequence
直接从example复制过来的slave sequence
主要内容是接收来自monitor的transaction,添加信息后发送至driver,添加信息包括随机延时、bresp/rresp返回OKAY、读数据等,同时将AXI读写转换为svt_mem的读写
p_sequencer.response_request_port.peek(req_resp);
status=req_resp.randomize with {
bresp == svt_axi_slave_transaction::OKAY;
foreach (rresp[index]) {
rresp[index] == svt_axi_slave_transaction::OKAY;
}
};
if(req_resp.get_transmitted_channel() == svt_axi_slave_transaction::WRITE)
put_write_transaction_data_to_mem(req_resp);
if (req_resp.get_transmitted_channel() == svt_axi_slave_transaction::READ)
get_read_data_from_mem_to_transaction(req_resp);
(2)axi_master_wr_sequence
简单的master sequence,父类为svt_axi_master_base_sequence,只包含读写行为,通过wr_mode决定读/写,data_arr可以作为写数据也可以作为读数据,作读数据时需要预先分配长度,作为burst长度
int wr_mode; // 1:write 0:read
bit [127:0] data_arr [] ;
bit [31:0] addr ;
body()中配置AXI读写信息,包含读/写、地址、burst size、burst 长度、数据、地址和延时等
burst size小于端口宽度时,VIP会自动移位,strb和data低对齐即可
`uvm_do_with(req,
{
xact_type == wr_mode ? svt_axi_transaction::WRITE : svt_axi_transaction::READ ;
data_before_addr == 0;
addr == local::addr;
burst_type == svt_axi_transaction::INCR;
burst_size == svt_axi_transaction::BURST_SIZE_32BIT;
burst_length == data_arr.size();
if(wr_mode) foreach(data[fi]) data[fi] == data_arr[fi];
if(wr_mode) foreach(wstrb[fi]) wstrb[fi] == 4'hf;
})
get_response(rsp);
if(!wr_mode)
foreach(data_arr[fi])
data_arr[fi] = rsp.data[fi];
6 test
(1)top_base_test
将通用例化和配置加入top_base_test,主要包括:
a)将AXI配置发信至axi_system_env,具体配置是包含1主1从、AXI数据宽度256、slave地址全范围可读写
b)将AXI slave的sequence发送至VIP slave agent,行为是将AXI读写转换为对svt_mem的读写
声明部分添加下段代码
svt_axi_system_configuration cfg;
build_phase添加下段代码
cfg = svt_axi_system_configuration::type_id::create("cfg");
cfg.num_masters = 1;
cfg.num_slaves = 1;
cfg.system_monitor_enable = 1;
cfg.create_sub_cfgs(1,1);
cfg.master_cfg[0].data_width = 256;
cfg.slave_cfg[0].data_width = 256;
cfg.master_cfg[0].id_width = 8;
cfg.slave_cfg[0].id_width = 8;
cfg.set_addr_range(0,64'h0,64'hffff_ffff_ffff_ffff);
uvm_config_db#(svt_axi_system_configuration)::set(this, "top_env.axi_system_env", "cfg", cfg);
uvm_config_db#(uvm_object_wrapper)::set(this, "top_env.axi_system_env.slave*.sequencer.run_phase", "default_sequence", axi_slave_mem_response_sequence::type_id::get());
(2)axi_wr_test
将会执行的AXI读写test,是top_base_test的子类,test内容是:
a)通过AXI master发送写burst,地址32’h200,数据’{5,6,7,8}
b)后门读svt_mem,期待读到’{5,6,7,8}
c)后门写svt_mem,修改为’{9,10,11,12}
d)通过AXI master发送读burst,地址32’h200,期待读到’{9,10,11,12}
声明部分代码
axi_master_wr_sequence seq_axi_wr;
bit [31:0] rdata;
build_phase代码
// step a
seq_axi_wr = new();
seq_axi_wr.wr_mode = 1;
seq_axi_wr.data_arr = '{5,6,7,8};
seq_axi_wr.addr = 32'h200;
seq_axi_wr.start(top_env.axi_system_env.master[0].sequencer);
// step b
foreach(seq_axi_wr.data_arr[fi]) begin
top_env.axi_system_env.slave[0].read_byte(32'h200+4*fi, rdata);
`uvm_info("body", $sformatf("back-door rdata %32h",rdata), UVM_LOW);
top_env.axi_system_env.slave[0].write_byte(32'h200+4*fi, 9+fi);
end
// step c
seq_axi_wr = new();
seq_axi_wr.wr_mode = 0;
seq_axi_wr.data_arr = new[4];
seq_axi_wr.addr = 32'h200;
seq_axi_wr.start(top_env.axi_system_env.master[0].sequencer);
// step d
foreach(seq_axi_wr.data_arr[fi])
`uvm_info("body", $sformatf("front-door rdata %32h",seq_axi_wr.data_arr[fi]), UVM_LOW);
7 top_tb
tb中定义svt_axi_if接口,默认common模式下只需要连接common时钟,每个master/slave需要单独连接复位,为了便于验证将master和slave直接连接,只列举AW通道信号,其他略。最后再将接口传递至axi_system_env的虚接口
svt_axi_if axi_if();
assign axi_if.common_aclk = clk_200MHz;
assign axi_if.master_if[0].aresetn = rst_n;
assign axi_if.slave_if[0].aresetn = rst_n;
assign axi_if.slave_if[0].awvalid = axi_if.master_if[0].awvalid;
assign axi_if.slave_if[0].awaddr = axi_if.master_if[0].awaddr ;
assign axi_if.slave_if[0].awlen = axi_if.master_if[0].awlen ;
assign axi_if.slave_if[0].awsize = axi_if.master_if[0].awsize ;
assign axi_if.slave_if[0].awburst = axi_if.master_if[0].awburst;
assign axi_if.slave_if[0].awlock = axi_if.master_if[0].awlock ;
assign axi_if.slave_if[0].awcache = axi_if.master_if[0].awcache;
assign axi_if.slave_if[0].awprot = axi_if.master_if[0].awprot ;
assign axi_if.slave_if[0].awid = axi_if.master_if[0].awid ;
assign axi_if.master_if[0].awready = axi_if.slave_if[0].awready;
initial begin
uvm_config_db#(virtual svt_axi_if)::set(uvm_root::get(), "uvm_test_top.top_env.axi_system_env", "vif", axi_if);
run_test();
end
7 执行结果
(1)verdi波形
可以看到先发起写burst ‘{5,6,7,8},由于期间后门修改了数据,再读burst读到’{9,10,11,12}
(2)sim log,后门读取读到’{5,6,7,8},期间后门修改了数据,再通过AXI master前门读取读到’{9,10,11,12}