APB協議UVM驗證環境的搭建
一、編譯文件
只需編譯這兩個文件即可
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里面的數據進行比較。
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
驗證環境結構
寫操作:寫入地址和寫入數據相同,只有penable拉高才會寫入,數據之間有一個空閑。
讀操作:只有penable拉高才會讀數據,沒有寫入過數據的地址,讀出來的值為0。
先寫后讀:
寫完立即讀操作:
仿真結果:
覆蓋率:
//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文章
182瀏覽量
19196
原文標題:APB協議UVM驗證環境的搭建
文章出處:【微信號:ZYNQ,微信公眾號:ZYNQ】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論