0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

APB协议UVM验证环境的搭建流程

ZYNQ 来源:csdn 2023-12-04 09:10 次阅读

APB协议UVM验证环境的搭建

一、编译文件

只需编译这两个文件即可

93b383b0-9239-11ee-939d-92fbcf53809c.png

apb_pkg.sv

里面包含了"apb.svh",即编译apb_pkg.sv这个文件的同时,也会编译所需要的所有的头文件。

`ifndefAPB_PKG_SV
`defineAPB_PKG_SV

packageapb_pkg;

importuvm_pkg::*;
`include"uvm_macros.svh"

`include"apb.svh"

endpackage:apb_pkg


`endif//`ifndefAPB_PKG_SV

apb.svh

`ifndefAPB_SVH
`defineAPB_SVH


`include"apb_transfer.sv"
`include"apb_config.sv"

//master所有的头文件
`include"apb_master_driver.svh"
`include"apb_master_monitor.svh"
`include"apb_master_sequencer.svh"
`include"apb_master_agent.svh"

//slave所有的头文件
`include"apb_slave_driver.svh"
`include"apb_slave_monitor.svh"
`include"apb_slave_sequencer.svh"
`include"apb_slave_agent.svh"

//master头文件里面具体的实现方法
`include"apb_master_driver.sv"
`include"apb_master_monitor.sv"
`include"apb_master_sequencer.sv"
`include"apb_master_agent.sv"
`include"apb_master_seq_lib.sv"

//slave头文件里面具体的实现方法
`include"apb_slave_driver.sv"
`include"apb_slave_monitor.sv"
`include"apb_slave_sequencer.sv"
`include"apb_slave_agent.sv"
`include"apb_slave_seq_lib.sv"




`endif//`ifndefAPB_SVH

再来编译apb_tb.sv文件

编译的同时,也会编译"apb_tests.svh"、"apb_if.sv"这两个文件。例化协议接口,配置顶层环境的master和slave,默认执行“apb_single_transaction_test”这个测试用例。

`timescale1ps/1ps
importuvm_pkg::*;
`include"uvm_macros.svh"
`include"apb_tests.svh"
`include"apb_if.sv"
moduleapb_tb;
bitclk,rstn;
initialbegin
fork
begin
forever#5nsclk=!clk;
end
begin
#100ns;
rstn<= 1'b1;
        #100ns;
        rstn <= 1'b0;
        #100ns;
        rstn <= 1'b1;
      end
    join_none
  end

  apb_if intf(clk, rstn);

  initial begin
    uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.mst", "vif", intf);
    uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.slv", "vif", intf);
    run_test("apb_single_transaction_test");
  end

endmodule

apb_tests.svh

`ifndefAPB_TESTS_SV
`defineAPB_TESTS_SV

importapb_pkg::*;

classapb_envextendsuvm_env;
apb_master_agentmst;
apb_slave_agentslv;
`uvm_component_utils(apb_env)
functionnew(stringname,uvm_componentparent);
super.new(name,parent);
endfunction
functionvoidbuild_phase(uvm_phasephase);
super.build_phase(phase);
mst=apb_master_agent::create("mst",this);
slv=apb_slave_agent::create("slv",this);
endfunction
endclass

classapb_base_testextendsuvm_test;
apb_envenv;
`uvm_component_utils(apb_base_test)
functionnew(stringname,uvm_componentparent);
super.new(name,parent);
endfunction
functionvoidbuild_phase(uvm_phasephase);
super.build_phase(phase);
env=apb_env::create("env",this);
endfunction
endclass

classapb_base_test_sequenceextendsuvm_sequence#(apb_transfer);
bit[31:0]mem[bit[31:0]];//关联数组mem,用来master和slave之间的数据比对,test和slave中都有一个mem
`uvm_object_utils(apb_base_test_sequence)
functionnew(stringname="");
super.new(name);
endfunction:new
functionbitcheck_mem_data(bit[31:0]addr,bit[31:0]data);
if(mem.exists(addr))begin
if(data!=mem[addr])begin
`uvm_error("CMPDATA",$sformatf("addr32'h%8x,READDATAexpected32'h%8x!=actual32'h%8x",addr,mem[addr],data))
return0;
end
elsebegin
`uvm_info("CMPDATA",$sformatf("addr32'h%8x,READDATA32'h%8xcomparingsuccess!",addr,data),UVM_LOW)
return1;
end
end
elsebegin
if(data!=0)begin
`uvm_error("CMPDATA",$sformatf("addr32'h%8x,READDATAexpected32'h00000000!=actual32'h%8x",addr,data))
return0;
end
elsebegin
`uvm_info("CMPDATA",$sformatf("addr32'h%8x,READDATA32'h%8xcomparingsuccess!",addr,data),UVM_LOW)
return1;
end
end
endfunction:check_mem_data

taskwait_reset_release();
@(negedgeapb_tb.rstn);
@(posedgeapb_tb.rstn);
endtask

taskwait_cycles(intn);
repeat(n)@(posedgeapb_tb.clk);
endtask

functionbit[31:0]get_rand_addr();
bit[31:0]addr;
void'(std::randomize(addr)with{addr[31:12]==0;addr[1:0]==0;});
returnaddr;
endfunction
endclass

classapb_single_transaction_sequenceextendsapb_base_test_sequence;
apb_master_single_write_sequencesingle_write_seq;
apb_master_single_read_sequencesingle_read_seq;
apb_master_write_read_sequencewrite_read_seq;
randinttest_num=100;
constraintcstr{
softtest_num==100;
}
`uvm_object_utils(apb_single_transaction_sequence)
functionnew(stringname="");
super.new(name);
endfunction:new
taskbody();
bit[31:0]addr;
this.wait_reset_release();
this.wait_cycles(10);

//TESTcontinouswritetransaction
`uvm_info(get_type_name(),"TESTcontinouswritetransaction...",UVM_LOW)
repeat(test_num)begin
addr=this.get_rand_addr();
`uvm_do_with(single_write_seq,{addr==local::addr;data==local::addr;})
mem[addr]=addr;
end

//TESTcontinousreadtransaction
`uvm_info(get_type_name(),"TESTcontinousreadtransaction...",UVM_LOW)
repeat(test_num)begin
addr=this.get_rand_addr();
`uvm_do_with(single_read_seq,{addr==local::addr;})
void'(this.check_mem_data(addr,single_read_seq.data));
end

//TESTreadtransactionafterwritetransaction
`uvm_info(get_type_name(),"TESTreadtransactionafterwritetransaction...",UVM_LOW)
repeat(test_num)begin
addr=this.get_rand_addr();
`uvm_do_with(single_write_seq,{addr==local::addr;data==local::addr;})
mem[addr]=addr;
`uvm_do_with(single_read_seq,{addr==local::addr;})
void'(this.check_mem_data(addr,single_read_seq.data));
end


//TESTreadtransactionimmediatelyafterwritetransaction
`uvm_info(get_type_name(),"TESTreadtransactionimmediatelyafterwritetransaction",UVM_LOW)
repeat(test_num)begin
addr=this.get_rand_addr();
`uvm_do_with(write_read_seq,{addr==local::addr;data==local::addr;})
mem[addr]=addr;
void'(this.check_mem_data(addr,write_read_seq.data));
end

this.wait_cycles(10);
endtask
endclass:apb_single_transaction_sequence

classapb_single_transaction_testextendsapb_base_test;
`uvm_component_utils(apb_single_transaction_test)
functionnew(stringname,uvm_componentparent);
super.new(name,parent);
endfunction
taskrun_phase(uvm_phasephase);
apb_single_transaction_sequenceseq=new();
phase.raise_objection(this);
super.run_phase(phase);
seq.start(env.mst.sequencer);
phase.drop_objection(this);
endtask
endclass:apb_single_transaction_test

classapb_burst_transaction_sequenceextendsapb_base_test_sequence;
apb_master_burst_write_sequenceburst_write_seq;
apb_master_burst_read_sequenceburst_read_seq;
randinttest_num=100;
constraintcstr{
softtest_num==100;
}
`uvm_object_utils(apb_burst_transaction_sequence)
functionnew(stringname="");
super.new(name);
endfunction:new
taskbody();
bit[31:0]addr;
this.wait_reset_release();
this.wait_cycles(10);

//TESTcontinouswritetransaction
repeat(test_num)begin
addr=this.get_rand_addr();
`uvm_do_with(burst_write_seq,{addr==local::addr;})
foreach(burst_write_seq.data[i])begin
mem[addr+(i<<2)] = burst_write_seq.data[i];
      end
      `uvm_do_with(burst_read_seq, {addr == local::addr; data.size() == burst_write_seq.data.size();})
      foreach(burst_read_seq.data[i]) begin
        void'(this.check_mem_data(addr+(i<<2), burst_write_seq.data[i]));
      end
    end

    this.wait_cycles(10);
  endtask
endclass: apb_burst_transaction_sequence

class apb_burst_transaction_test extends apb_base_test;
  `uvm_component_utils(apb_burst_transaction_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  task run_phase(uvm_phase phase);
    apb_burst_transaction_sequence seq = new();
    phase.raise_objection(this);
    super.run_phase(phase);
    seq.start(env.mst.sequencer);
    phase.drop_objection(this);
  endtask
endclass: apb_burst_transaction_test



`endif // APB_TESTS_SV

apb_if.sv

`ifndefAPB_IF_SV
`defineAPB_IF_SV

interfaceapb_if(inputclk,inputrstn);

logic[31:0]paddr;
logicpwrite;
logicpsel;
logicpenable;
logic[31:0]pwdata;
logic[31:0]prdata;

//Controlflags
bithas_checks=1;
bithas_coverage=1;

//ActualSignals
//USER:Addinterfacesignals

clockingcb_mst@(posedgeclk);
//USER:Addclockingblockdetail
defaultinput#1psoutput#1ps;
outputpaddr,pwrite,psel,penable,pwdata;
inputprdata;
endclocking:cb_mst

clockingcb_slv@(posedgeclk);
//USER:Addclockingblockdetail
defaultinput#1psoutput#1ps;
inputpaddr,pwrite,psel,penable,pwdata;
outputprdata;
endclocking:cb_slv

clockingcb_mon@(posedgeclk);
//USER:Addclockingblockdetail
defaultinput#1psoutput#1ps;
inputpaddr,pwrite,psel,penable,pwdata,prdata;
endclocking:cb_mon

//Coverageandassertionstobeimplementedhere.
//USER:Addassertions/coveragehere

//APBcommandcovergroup
covergroupcg_apb_command@(posedgeclkiffrstn);
pwrite:coverpointpwrite{
type_option.weight=0;
binswrite={1};
binsread={0};

}
psel:coverpointpsel{
type_option.weight=0;
binssel={1};
binsunsel={0};
}
cmd:crosspwrite,psel{
binscmd_write=binsof(psel.sel)&&binsof(pwrite.write);
binscmd_read=binsof(psel.sel)&&binsof(pwrite.read);
binscmd_idle=binsof(psel.unsel);
}
endgroup

//APBtransactiontiminggroup
covergroupcg_apb_trans_timing_group@(posedgeclkiffrstn);
psel:coverpointpsel{
binssingle=(0=>1=>1=>0);
binsburst_2=(0=>1[*4]=>0);
binsburst_4=(0=>1[*8]=>0);
binsburst_8=(0=>1[*16]=>0);
binsburst_16=(0=>1[*32]=>0);
binsburst_32=(0=>1[*64]=>0);
}
penable:coverpointpenable{
binssingle=(0=>1=>0[*2:10]=>1);
binsburst=(0=>1=>0=>1);
}
endgroup

//APBwrite&readordergroup
covergroupcg_apb_write_read_order_group@(posedgeclkiff(rstn&&penable));
write_read_order:coverpointpwrite{
binswrite_write=(1=>1);
binswrite_read=(1=>0);
binsread_write=(0=>1);
binsread_read=(0=>0);
}
endgroup

initialbegin
automaticcg_apb_commandcg0=new();
automaticcg_apb_trans_timing_groupcg1=new();
automaticcg_apb_write_read_order_groupcg2=new();
end

endinterface:apb_if

`endif//APB_IF_SV

二、apb_tests.sv代码分析

apb_base_test_sequence类

check_mem_data()方法原理结构框图:

关联数组mem,用来master和slave之间的数据比对,test和slave中都有一个mem,master通过接口发送数据给slave,slave中的mem和test中的mem都会存储这个数据,等从slave读回数据时,就可以和test中mem里面的数据进行比较。

93c463ba-9239-11ee-939d-92fbcf53809c.png

apb_single_transaction_sequence类

随机化addr,测试连续写操作

//TESTcontinouswritetransaction
`uvm_info(get_type_name(),"TESTcontinouswritetransaction...",UVM_LOW)
repeat(test_num)begin
addr=this.get_rand_addr();
`uvm_do_with(single_write_seq,{addr==local::addr;data==local::addr;})
mem[addr]=addr;
end

随机化addr,测试连续读操作,并比较数据是否一致

//TESTcontinousreadtransaction
`uvm_info(get_type_name(),"TESTcontinousreadtransaction...",UVM_LOW)
repeat(test_num)begin
addr=this.get_rand_addr();
`uvm_do_with(single_read_seq,{addr==local::addr;})
void'(this.check_mem_data(addr,single_read_seq.data));
end

随机化addr,先进行写操作,再进行读操作,并比较读取的数据是否一致

//TESTreadtransactionafterwritetransaction
`uvm_info(get_type_name(),"TESTreadtransactionafterwritetransaction...",UVM_LOW)
repeat(test_num)begin
addr=this.get_rand_addr();
`uvm_do_with(single_write_seq,{addr==local::addr;data==local::addr;})
mem[addr]=addr;
`uvm_do_with(single_read_seq,{addr==local::addr;})
void'(this.check_mem_data(addr,single_read_seq.data));
end

随机化addr,写完立即读,中间没有idle空闲,并检查读取数据是否一致

//TESTreadtransactionimmediatelyafterwritetransaction
`uvm_info(get_type_name(),"TESTreadtransactionimmediatelyafterwritetransaction",UVM_LOW)
repeat(test_num)begin
addr=this.get_rand_addr();
`uvm_do_with(write_read_seq,{addr==local::addr;data==local::addr;})
mem[addr]=addr;
void'(this.check_mem_data(addr,write_read_seq.data));
end

例化并挂载

classapb_single_transaction_testextendsapb_base_test;
`uvm_component_utils(apb_single_transaction_test)
functionnew(stringname,uvm_componentparent);
super.new(name,parent);
endfunction
taskrun_phase(uvm_phasephase);
apb_single_transaction_sequenceseq=new();
phase.raise_objection(this);
super.run_phase(phase);
seq.start(env.mst.sequencer);
phase.drop_objection(this);
endtask
endclass:apb_single_transaction_test

apb_burst_transaction_sequence类

先全部写操作完毕,在完全读出来,地址是连续增长的

//TESTcontinouswritetransaction
repeat(test_num)begin
addr=this.get_rand_addr();
`uvm_do_with(burst_write_seq,{addr==local::addr;})
foreach(burst_write_seq.data[i])begin
mem[addr+(i<<2)] = burst_write_seq.data[i];
      end
      `uvm_do_with(burst_read_seq, {addr == local::addr; data.size() == burst_write_seq.data.size();})
      foreach(burst_read_seq.data[i]) begin
        void'(this.check_mem_data(addr+(i<<2), burst_write_seq.data[i]));
      end
    end

例化并挂载

classapb_burst_transaction_testextendsapb_base_test;
`uvm_component_utils(apb_burst_transaction_test)
functionnew(stringname,uvm_componentparent);
super.new(name,parent);
endfunction
taskrun_phase(uvm_phasephase);
apb_burst_transaction_sequenceseq=new();
phase.raise_objection(this);
super.run_phase(phase);
seq.start(env.mst.sequencer);
phase.drop_objection(this);
endtask
endclass:apb_burst_transaction_test

三、apb_master_agent.sv代码分析

agent包括三个组件driver、sequencer、monitor,以及config和interface。

例化monitor,根据配置决定是否例化driver和sequencer

functionvoidapb_master_agent::build();
super.build();
//getconfig
if(!uvm_config_db#(apb_config)::get(this,"","cfg",cfg))begin
`uvm_warning("GETCFG","cannotgetconfigobjectfromconfigDB")
cfg=apb_config::create("cfg");
end
//getvirtualinterface
if(!uvm_config_db#(virtualapb_if)::get(this,"","vif",vif))begin
`uvm_fatal("GETVIF","cannotgetvifhandlefromconfigDB")
end
monitor=apb_master_monitor::create("monitor",this);
monitor.cfg=cfg;
if(cfg.is_active==UVM_ACTIVE)begin
sequencer=apb_master_sequencer::create("sequencer",this);
sequencer.cfg=cfg;
driver=apb_master_driver::create("driver",this);
driver.cfg=cfg;
end
endfunction:build

根据配置决定是否连接driver和sequencer

functionvoidapb_master_agent::connect();
assign_vi(vif);

if(is_active==UVM_ACTIVE)begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end

endfunction:connect

根据配置决定是否vif和driver、sequencer之间的连接

functionvoidapb_master_agent::assign_vi(virtualapb_ifvif);
monitor.vif=vif;
if(is_active==UVM_ACTIVE)begin
sequencer.vif=vif;
driver.vif=vif;
end
endfunction:assign_vi

四、apb_master_driver.sv代码分析

并行触发get_and_drive()、reset_listener()

taskapb_master_driver::run();
fork
get_and_drive();
reset_listener();
join_none
endtask:run

捕捉到复位信号以后,所以信号清零

taskapb_master_driver::reset_listener();
`uvm_info(get_type_name(),"reset_listener...",UVM_HIGH)
fork
foreverbegin
@(negedgevif.rstn);//ASYNCreset
vif.paddr<= 0;
      vif.pwrite <= 0;
      vif.psel <= 0;
      vif.penable <= 0;
      vif.pwdata <= 0;
    end
  join_none
endtask

sequence和sequencer需要握手,获取transaction以后调用driver_transfer()发送。发送成功以后克隆request生成新的response,作为响应发送回去。

taskapb_master_driver::get_and_drive();
foreverbegin
seq_item_port.get_next_item(req);
`uvm_info(get_type_name(),"sequencergotnextitem",UVM_HIGH)
drive_transfer(req);
void'($cast(rsp,req.clone()));
rsp.set_sequence_id(req.get_sequence_id());
seq_item_port.item_done(rsp);
`uvm_info(get_type_name(),"sequenceritem_done_triggered",UVM_HIGH)
end
endtask:get_and_drive

taskapb_master_driver::drive_transfer(apb_transfert);
`uvm_info(get_type_name(),"drive_transfer",UVM_HIGH)
case(t.trans_kind)
IDLE:this.do_idle();
WRITE:this.do_write(t);
READ:this.do_read(t);
default:`uvm_error("ERRTYPE","unrecognizedtransactiontype")
endcase
endtask:drive_transfer

根据trans_kind判断操作命令,分别调用相对应的方法。

taskapb_master_driver::do_write(apb_transfert);
`uvm_info(get_type_name(),"do_write...",UVM_HIGH)
//写操作一共分为两个周期,根据协议第一个周期setup准备阶段需要如下操作
@(vif.cb_mst);
vif.cb_mst.paddr<= t.addr;
  vif.cb_mst.pwrite <= 1;
  vif.cb_mst.psel <= 1;
  vif.cb_mst.penable <= 0;
  vif.cb_mst.pwdata <= t.data;
  //第二个阶段拉高penable信号,发送数据
  @(vif.cb_mst);
  vif.cb_mst.penable <= 1;
  repeat(t.idle_cycles) this.do_idle();  //取决于transaction里面的idle
endtask: do_write

task apb_master_driver::do_read(apb_transfer t);
  `uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
  //第一个阶段
  @(vif.cb_mst);
  vif.cb_mst.paddr <= t.addr;
  vif.cb_mst.pwrite <= 0;
  vif.cb_mst.psel <= 1;
  vif.cb_mst.penable <= 0;
  //第二个阶段
  @(vif.cb_mst);
  vif.cb_mst.penable <= 1;
  #100ps; //需要采样数据,人为添加100ps的delay,是为了避免delta-cycle
  t.data = vif.prdata;  //采样数据
  repeat(t.idle_cycles) this.do_idle();
endtask: do_read

task apb_master_driver::do_idle();
  `uvm_info(get_type_name(), "do_idle ...", UVM_HIGH)
  @(vif.cb_mst);
  //根据协议,paddr、pwrite可以保持不变,等待下一次的传输,这是为了省电
  //vif.cb_mst.paddr <= 0;
  //vif.cb_mst.pwrite <= 0;
  vif.cb_mst.psel <= 0;
  vif.cb_mst.penable <= 0;
  vif.cb_mst.pwdata <= 0;
endtask:do_idle

五、apb_master_monitor.sv代码分析

collect_transfer()方法

时钟上升沿,同时psel=1和penabl=0的时候,判断当前情况下pwrite信号,在第二个周期进行读或者写操作。

taskapb_master_monitor::collect_transfer();
apb_transfert;
//Advanceclock
@(vif.cb_mon);
if(vif.cb_slv.psel===1'b1&&vif.cb_slv.penable===1'b0)begin
t=apb_transfer::create("t");
case(vif.cb_slv.pwrite)
1'b1:begin
@(vif.cb_mon);
t.addr=vif.cb_mon.paddr;
t.data=vif.cb_mon.pwdata;
t.trans_kind=WRITE;
end
1'b0:begin
@(vif.cb_mon);
t.addr=vif.cb_mon.paddr;
t.data=vif.cb_mon.prdata;
t.trans_kind=READ;
end
default:`uvm_error(get_type_name(),"ERRORpwritesignalvalue")
endcase
item_collected_port.write(t);
end
endtask:collect_transfer

六、apb_master_seq_lib.sv代码分析

apb_master_single_write_sequence类

使用宏'uvm_do_with发送数据。

classapb_master_single_write_sequenceextendsapb_master_base_sequence;
randbit[31:0]addr;
randbit[31:0]data;

`uvm_object_utils(apb_master_single_write_sequence)
functionnew(stringname="");
super.new(name);
endfunction:new

virtualtaskbody();
`uvm_info(get_type_name(),"Startingsequence",UVM_HIGH)
`uvm_do_with(req,{trans_kind==WRITE;addr==local::addr;data==local::data;})
get_response(rsp);
`uvm_info(get_type_name(),$psprintf("Donesequence:%s",req.convert2string()),UVM_HIGH)
endtask:body

endclass:apb_master_single_write_sequence

apb_master_single_read_sequence类

读操作,拿到返回的rsp的数据后存储在成员变量data里。

classapb_master_single_read_sequenceextendsapb_master_base_sequence;
randbit[31:0]addr;
randbit[31:0]data;

`uvm_object_utils(apb_master_single_read_sequence)
functionnew(stringname="");
super.new(name);
endfunction:new

virtualtaskbody();
`uvm_info(get_type_name(),"Startingsequence",UVM_HIGH)
`uvm_do_with(req,{trans_kind==READ;addr==local::addr;})
get_response(rsp);
data=rsp.data;
`uvm_info(get_type_name(),$psprintf("Donesequence:%s",req.convert2string()),UVM_HIGH)
endtask:body

endclass:apb_master_single_read_sequence

apb_master_write_read_sequence类

写操作后进行读操作,所以idle_cycles == 0

classapb_master_write_read_sequenceextendsapb_master_base_sequence;
randbit[31:0]addr;
randbit[31:0]data;
randintidle_cycles;
constraintcstr{
idle_cycles==0;
}

`uvm_object_utils(apb_master_write_read_sequence)
functionnew(stringname="");
super.new(name);
endfunction:new

virtualtaskbody();
`uvm_info(get_type_name(),"Startingsequence",UVM_HIGH)
`uvm_do_with(req,{trans_kind==WRITE;
addr==local::addr;
data==local::data;
idle_cycles==local::idle_cycles;
})
get_response(rsp);
`uvm_do_with(req,{trans_kind==READ;addr==local::addr;})
get_response(rsp);
data=rsp.data;
`uvm_info(get_type_name(),$psprintf("Donesequence:%s",req.convert2string()),UVM_HIGH)
endtask:body

endclass:apb_master_write_read_sequence

apb_master_burst_write_sequence类

连续的写操作,按照地址增长的顺序,把所有的数据写到data数组中。因为是连续写操作,所以idle_cycles == 0,即数据之间没有空闲周期。

classapb_master_burst_write_sequenceextendsapb_master_base_sequence;
randbit[31:0]addr;
randbit[31:0]data[];
constraintcstr{
softdata.size()inside{4,8,16,32};
foreach(data[i])softdata[i]==addr+(i<< 2);
  }

  `uvm_object_utils(apb_master_burst_write_sequence)    
  function new(string name=""); 
    super.new(name);
  endfunction : new

  virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    foreach(data[i]) begin
     `uvm_do_with(req, {trans_kind == WRITE; 
                         addr == local::addr + (i<<2); 
                         data == local::data[i];
                         idle_cycles == 0;
                        })
      get_response(rsp);
    end
    `uvm_do_with(req, {trans_kind == IDLE;})
    get_response(rsp);
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body
endclass: apb_master_burst_write_sequence

apb_master_burst_read_sequence类

连续的读操作,每次读取回来的数据,从rsp中拿出来放到data数组中。全部读取完成之后,将总线置为IDLE。

classapb_master_burst_read_sequenceextendsapb_master_base_sequence;
randbit[31:0]addr;
randbit[31:0]data[];
constraintcstr{
softdata.size()inside{4,8,16,32};
}
`uvm_object_utils(apb_master_burst_read_sequence)
functionnew(stringname="");
super.new(name);
endfunction:new

virtualtaskbody();
`uvm_info(get_type_name(),"Startingsequence",UVM_HIGH)
foreach(data[i])begin
`uvm_do_with(req,{trans_kind==READ;
addr==local::addr+(i<<2); 
                         idle_cycles == 0;
                        })
      get_response(rsp);
      data[i] = rsp.data;
    end
    `uvm_do_with(req, {trans_kind == IDLE;})
    get_response(rsp);
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body
endclass: apb_master_burst_read_sequence

七、apb_slave_driver.sv代码分析

slave要接收master发送过来的数据,所以要模拟一个存储功能,即关联数组mem。

bit[31:0]mem[bit[31:0]];

run()方法

三个方法并行执行

taskapb_slave_driver::run();
fork
get_and_drive();
reset_listener();
drive_response();
join_none
endtask:run

get_and_drive()方法

taskapb_slave_driver::get_and_drive();
foreverbegin
seq_item_port.get_next_item(req);
`uvm_info(get_type_name(),"sequencergotnextitem",UVM_HIGH)
void'($cast(rsp,req.clone()));
rsp.set_sequence_id(req.get_sequence_id());
seq_item_port.item_done(rsp);
`uvm_info(get_type_name(),"sequenceritem_done_triggered",UVM_HIGH)
end
endtask:get_and_drive

reset_listener()方法

等待复位信号,将prdata <= 0,同时清空mem里面的数据。

taskapb_slave_driver::reset_listener();
`uvm_info(get_type_name(),"reset_listener...",UVM_HIGH)
fork
foreverbegin
@(negedgevif.rstn);//ASYNCreset
vif.prdata<= 0;
      this.mem.delete(); // reset internal memory
    end
  join_none
endtask: reset_listener

drive_response()方法

如果当前这一周期是SETUP阶段,即psel = 1 && penable = 0,进而判断是写操作还是读操作,然后调用相对应的方法。

taskapb_slave_driver::drive_response();
`uvm_info(get_type_name(),"drive_response",UVM_HIGH)
foreverbegin
@(vif.cb_slv);
if(vif.cb_slv.psel===1'b1&&vif.cb_slv.penable===1'b0)begin
case(vif.cb_slv.pwrite)
1'b1:this.do_write();
1'b0:this.do_read();
default:`uvm_error(get_type_name(),"ERRORpwritesignalvalue")
endcase
end
elsebegin
this.do_idle();
end
end
endtask:drive_response

do_write()方法

如果是写操作,那么等待时钟下一拍,拿到addr和data并放到mem中。

taskapb_slave_driver::do_write();
bit[31:0]addr;
bit[31:0]data;
`uvm_info(get_type_name(),"do_write",UVM_HIGH)
@(vif.cb_slv);
addr=vif.cb_slv.paddr;
data=vif.cb_slv.pwdata;
mem[addr]=data;
endtask:do_write

do_read()方法

如果是读操作,等待penable=1,并且判断mem中是否写过该addr,如果有则写入data,没有则将data置为0,即还是初始化的数据。等待一个延迟后,将data驱动到总线上面。

taskapb_slave_driver::do_read();
bit[31:0]addr;
bit[31:0]data;
`uvm_info(get_type_name(),"do_read",UVM_HIGH)
wait(vif.penable===1'b1);
addr=vif.cb_slv.paddr;
if(mem.exists(addr))
data=mem[addr];
else
data=0;
#1ps;
vif.prdata<= data;
  @(vif.cb_slv);
endtask: do_read

八、运行仿真

执行命令

run-all

验证环境结构

93ea33b0-9239-11ee-939d-92fbcf53809c.png

写操作:写入地址和写入数据相同,只有penable拉高才会写入,数据之间有一个空闲。

93ffdc38-9239-11ee-939d-92fbcf53809c.png

读操作:只有penable拉高才会读数据,没有写入过数据的地址,读出来的值为0。

941bf8fa-9239-11ee-939d-92fbcf53809c.png

先写后读:

942f78da-9239-11ee-939d-92fbcf53809c.png

写完立即读操作:

943bed40-9239-11ee-939d-92fbcf53809c.png

仿真结果:

944b06fe-9239-11ee-939d-92fbcf53809c.png

覆盖率:

//APBcommandcovergroup
covergroupcg_apb_command@(posedgeclkiffrstn);
pwrite:coverpointpwrite{
type_option.weight=0;
binswrite={1};
binsread={0};

}
psel:coverpointpsel{
type_option.weight=0;
binssel={1};
binsunsel={0};
}
cmd:crosspwrite,psel{
binscmd_write=binsof(psel.sel)&&binsof(pwrite.write);
binscmd_read=binsof(psel.sel)&&binsof(pwrite.read);
binscmd_idle=binsof(psel.unsel);
}
endgroup

//APBtransactiontiminggroup
covergroupcg_apb_trans_timing_group@(posedgeclkiffrstn);
psel:coverpointpsel{
binssingle=(0=>1=>1=>0);
binsburst_2=(0=>1[*4]=>0);
binsburst_4=(0=>1[*8]=>0);
binsburst_8=(0=>1[*16]=>0);
binsburst_16=(0=>1[*32]=>0);
binsburst_32=(0=>1[*64]=>0);
}
penable:coverpointpenable{
binssingle=(0=>1=>0[*2:10]=>1);
binsburst=(0=>1=>0=>1);
}
endgroup

//APBwrite&readordergroup
covergroupcg_apb_write_read_order_group@(posedgeclkiff(rstn&&penable));
write_read_order:coverpointpwrite{
binswrite_write=(1=>1);
binswrite_read=(1=>0);
binsread_write=(0=>1);
binsread_read=(0=>0);
}
endgroup


946705d4-9239-11ee-939d-92fbcf53809c.png








审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • UVM
    UVM
    +关注

    关注

    0

    文章

    181

    浏览量

    18973

原文标题:APB协议UVM验证环境的搭建

文章出处:【微信号:ZYNQ,微信公众号:ZYNQ】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    IC验证"为什么要学习UVM呢"

    什么?验证工程师验证工程师能够从本书学会如下内容:如何用UVM搭建验证平台,包括如何使用sequence机制、factory机制、callb
    发表于 12-01 15:09

    IC验证"一个简单的UVM验证平台"是如何搭建的(六)

    的组件,是整个验证平台数据流的源泉。本节以一个简单的DUT为例,说明一个只有driver的UVM验 证平台是如何搭建的。最简单的验证平台,假设有如下的DUT定义:这个DUT的功能非常简
    发表于 12-04 15:48

    IC验证"UVM验证平台加入factory机制"(六)

      加入factory机制 上一节《IC验证"一个简单的UVM验证平台"是如何搭建的(五)》给出了一个只有driver、使用UVM
    发表于 12-08 12:07

    IC验证UVM验证平台加入objection机制和virtual interface机制“(七)

    机制来控制验证平台的关闭。细心的读者可能发现,在上节的例子中,并没有如**《IC验证"一个简单的UVM验证平台"是如何搭建的(五)》**所示
    发表于 12-09 18:28

    数字IC验证之“什么是UVM”“UVM的特点”“UVM提供哪些资源”(2)连载中...

    搭建平台结构上省去了不少时间,而且在使用其他工程师提供的uvm平台时显得得心应手。uvm为用户提供了一个标准的验证平台的模板,因此,所有基于uvm
    发表于 01-21 16:00

    用于SoC验证的(UVM)开源参考流程使EDA360的SoC

    全球电子设计创新领先企业Cadence设计系统公司,今天宣布了业界最全面的用于系统级芯片(SoC)验证的通用验证方法学(UVM)开源参考流程。为了配合Cadence EDA360中So
    发表于 06-28 08:29 2269次阅读

    基于UVM验证平台设计研究

    基于UVM验证平台设计研究_王国军
    发表于 01-07 19:00 4次下载

    一种基于UVM的混合信号验证环境

    一种基于UVM的混合信号验证环境_耿睿
    发表于 01-07 21:39 1次下载

    参数化UVM IP验证环境(上)

    参数化的IP是可配置的,这意味着在不同的SOC中IP设计可以有不同的设计参数,设计参数可以对应到协议、端口号、端口名称、以及内部逻辑。大量的IP设计参数非常影响验证环境的构建,比如testbench
    发表于 09-15 14:37 6次下载
    参数化<b class='flag-5'>UVM</b> IP<b class='flag-5'>验证</b><b class='flag-5'>环境</b>(上)

    UVM验证平台执行硬件加速

    UVM已经成为了一种高效率的、从模块级到系统级完整验证环境开发标准,其中一个关键的原则是UVM可以开发出可重用的验证组件。获得重用动力的一个
    发表于 09-15 17:08 14次下载
    <b class='flag-5'>UVM</b><b class='flag-5'>验证</b>平台执行硬件加速

    利用Systemverilog+UVM搭建soc验证环境

    利用Systemverilog+UVM搭建soc验证环境
    发表于 08-08 14:35 5次下载

    ASIC芯片设计之UVM验证

    百度百科对UVM的释义如下:通用验证方法学(Universal Verification Methodology, UVM)是一个以SystemVerilog类库为主体的验证平台开发框
    发表于 11-30 12:47 1115次阅读

    UVM验证平台顶层有什么作用

    因为DUT是一个静态的内容,所以testbench理应也是静态的,其作为uvm验证环境和DUT的全局根结点。
    的头像 发表于 03-21 11:33 1019次阅读

    Easier UVM Code Generator Part 4:生成层次化的验证环境

    本文使用Easier UVM Code Generator生成包含多个agent和interface的uvm验证环境
    的头像 发表于 06-06 09:13 658次阅读

    基于UVM验证环境开发测试流程

    验证环境用户需要创建许多测试用例来验证一个DUT的功能是否正确,验证环境开发者应该通过以下方式提高测试用例的开发效率
    的头像 发表于 06-09 11:11 640次阅读
    基于<b class='flag-5'>UVM</b><b class='flag-5'>验证</b><b class='flag-5'>环境</b>开发测试<b class='flag-5'>流程</b>