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
![946705d4-9239-11ee-939d-92fbcf53809c.png](https://file1.elecfans.com/web2/M00/B4/2C/wKgZomVtJ7CAXnXtAAJ28FSHNmg837.png)
审核编辑:刘清
-
UVM
+关注
关注
0文章
182浏览量
19240
原文标题:APB协议UVM验证环境的搭建
文章出处:【微信号:ZYNQ,微信公众号:ZYNQ】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
IC验证"一个简单的UVM验证平台"是如何搭建的(六)
IC验证"UVM验证平台加入factory机制"(六)
IC验证“UVM验证平台加入objection机制和virtual interface机制“(七)
数字IC验证之“什么是UVM”“UVM的特点”“UVM提供哪些资源”(2)连载中...
用于SoC验证的(UVM)开源参考流程使EDA360的SoC
参数化UVM IP验证环境(上)
![参数化<b class='flag-5'>UVM</b> IP<b class='flag-5'>验证</b><b class='flag-5'>环境</b>(上)](https://file.elecfans.com/web2/M00/49/C3/pYYBAGKhvFqAOwxhAAAYNMm6LZg592.jpg)
UVM验证平台执行硬件加速
![<b class='flag-5'>UVM</b><b class='flag-5'>验证</b>平台执行硬件加速](https://file.elecfans.com/web2/M00/49/C4/pYYBAGKhvFyAOQbqAAAjvXtJt6c115.jpg)
评论