在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

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<

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

    文章

    182

    瀏覽量

    19196

原文標題:APB協議UVM驗證環境的搭建

文章出處:【微信號:ZYNQ,微信公眾號:ZYNQ】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    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 ?2598次閱讀

    基于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 ?8次下載
    參數化<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 ?1543次閱讀

    盤點UVM不同機制的調試功能

    基于UVM搭建驗證環境和構造驗證激勵,調試的工作總是繞不開的。實際上,對驗證
    的頭像 發表于 04-06 09:36 ?977次閱讀

    Easier UVM Code Generator Part 4:生成層次化的驗證環境

    本文使用Easier UVM Code Generator生成包含多個agent和interface的uvm驗證環境
    的頭像 發表于 06-06 09:13 ?1197次閱讀

    基于UVM驗證環境開發測試流程

    驗證環境用戶需要創建許多測試用例來驗證一個DUT的功能是否正確,驗證環境開發者應該通過以下方式提高測試用例的開發效率
    的頭像 發表于 06-09 11:11 ?997次閱讀
    基于<b class='flag-5'>UVM</b><b class='flag-5'>驗證</b><b class='flag-5'>環境</b>開發測試<b class='flag-5'>流程</b>

    fpga驗證uvm驗證的區別

    FPGA驗證UVM驗證在芯片設計和驗證過程中都扮演著重要的角色,但它們之間存在明顯的區別。
    的頭像 發表于 03-15 15:00 ?1698次閱讀
    主站蜘蛛池模板: 精品伊人久久大线蕉色首页| 国产高清视频在线播放www色| 一二三区乱码一区二区三区码| xxⅹ丰满妇女与善交| 国产高清在线播放免费观看| 主人扒开腿揉捏花蒂调教cfh| 一级特黄aa大片免费| 性欧美黑人xxxx| 日本特黄a级高清免费大片18| 男人的天堂天堂网| 国产免费美女| 亚洲看黄| 国产精品久久久亚洲第一牛牛| 操美女免费视频| 亚洲欧美视频在线播放| 中文在线天堂网| 色午夜视频| 黄色v片| 亚洲乱亚洲乱妇13p| 两人性潮高免费视频看| 欧美中出| xxⅹ丰满妇女与善交| 色狠狠综合网| 很色视频| 色淫阁色九九| 啪啪网站免费观看| 国产精品国产三级在线高清观看| 天堂中文字幕在线| 久久综合五月开心婷婷深深爱| mide-776中文字幕在线| 人人草人人| 中文字幕一区二区三区永久| 一级做a爱片在线播放| 日本天天色| videosgratis欧美另类老太| 久久精品国产福利| 色综合视频一区二区三区| 天堂福利视频在线观看| 国内真实实拍伦视频在线观看| 天天草综合网| 又粗又长又色又爽视频|