APB協(xié)議UVM驗證環(huán)境的搭建
一、編譯文件
只需編譯這兩個文件即可
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頭文件里面具體的實現(xiàn)方法 `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頭文件里面具體的實現(xiàn)方法 `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"這兩個文件。例化協(xié)議接口,配置頂層環(huán)境的master和slave,默認執(zhí)行“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]];//關聯(lián)數(shù)組mem,用來master和slave之間的數(shù)據(jù)比對,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()方法原理結(jié)構(gòu)框圖:
關聯(lián)數(shù)組mem,用來master和slave之間的數(shù)據(jù)比對,test和slave中都有一個mem,master通過接口發(fā)送數(shù)據(jù)給slave,slave中的mem和test中的mem都會存儲這個數(shù)據(jù),等從slave讀回數(shù)據(jù)時,就可以和test中mem里面的數(shù)據(jù)進行比較。
apb_single_transaction_sequence類
隨機化addr,測試連續(xù)寫操作
//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,測試連續(xù)讀操作,并比較數(shù)據(jù)是否一致
//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,先進行寫操作,再進行讀操作,并比較讀取的數(shù)據(jù)是否一致
//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空閑,并檢查讀取數(shù)據(jù)是否一致
//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類
先全部寫操作完畢,在完全讀出來,地址是連續(xù)增長的
//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,根據(jù)配置決定是否例化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
根據(jù)配置決定是否連接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
根據(jù)配置決定是否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代碼分析
并行觸發(fā)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以后調(diào)用driver_transfer()發(fā)送。發(fā)送成功以后克隆request生成新的response,作為響應發(fā)送回去。
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
根據(jù)trans_kind判斷操作命令,分別調(diào)用相對應的方法。
taskapb_master_driver::do_write(apb_transfert); `uvm_info(get_type_name(),"do_write...",UVM_HIGH) //寫操作一共分為兩個周期,根據(jù)協(xié)議第一個周期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信號,發(fā)送數(shù)據(jù) ??@(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;?//需要采樣數(shù)據(jù),人為添加100ps的delay,是為了避免delta-cycle ??t.data?=?vif.prdata;??//采樣數(shù)據(jù) ??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); ??//根據(jù)協(xié)議,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發(fā)送數(shù)據(jù)。
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的數(shù)據(jù)后存儲在成員變量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類
連續(xù)的寫操作,按照地址增長的順序,把所有的數(shù)據(jù)寫到data數(shù)組中。因為是連續(xù)寫操作,所以idle_cycles == 0,即數(shù)據(jù)之間沒有空閑周期。
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類
連續(xù)的讀操作,每次讀取回來的數(shù)據(jù),從rsp中拿出來放到data數(shù)組中。全部讀取完成之后,將總線置為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發(fā)送過來的數(shù)據(jù),所以要模擬一個存儲功能,即關聯(lián)數(shù)組mem。
bit[31:0]mem[bit[31:0]];
run()方法
三個方法并行執(zhí)行
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里面的數(shù)據(jù)。
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,進而判斷是寫操作還是讀操作,然后調(diào)用相對應的方法。
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,即還是初始化的數(shù)據(jù)。等待一個延遲后,將data驅(qū)動到總線上面。
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
八、運行仿真
執(zhí)行命令
run-all
驗證環(huán)境結(jié)構(gòu)
寫操作:寫入地址和寫入數(shù)據(jù)相同,只有penable拉高才會寫入,數(shù)據(jù)之間有一個空閑。
讀操作:只有penable拉高才會讀數(shù)據(jù),沒有寫入過數(shù)據(jù)的地址,讀出來的值為0。
先寫后讀:
寫完立即讀操作:
仿真結(jié)果:

覆蓋率:
//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

審核編輯:劉清
-
UVM
+關注
關注
0文章
183瀏覽量
20014
原文標題:APB協(xié)議UVM驗證環(huán)境的搭建
文章出處:【微信號:ZYNQ,微信公眾號:ZYNQ】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
IC驗證"一個簡單的UVM驗證平臺"是如何搭建的(六)
IC驗證"UVM驗證平臺加入factory機制"(六)
IC驗證“UVM驗證平臺加入objection機制和virtual interface機制“(七)
數(shù)字IC驗證之“什么是UVM”“UVM的特點”“UVM提供哪些資源”(2)連載中...
用于SoC驗證的(UVM)開源參考流程使EDA360的SoC
參數(shù)化UVM IP驗證環(huán)境(上)
UVM驗證平臺執(zhí)行硬件加速
ASIC芯片設計之UVM驗證
盤點UVM不同機制的調(diào)試功能
Easier UVM Code Generator Part 4:生成層次化的驗證環(huán)境
基于UVM驗證環(huán)境開發(fā)測試流程
APB協(xié)議UVM驗證環(huán)境的搭建流程
評論