本篇完成對HDMI顯示代碼的UVM仿真,梳理一下在windows-modelsim工具下UVM仿真環境的建立,調試以及遇到的問題。
仿真架構
仿真的架構在上一篇已經做了簡要介紹,這部分做重點講解。整體結構包括五個功能模塊:image圖像的隨機化和驅動,軟件端配置,圖像監測,以及設計部分。如圖1.1所示。
圖1.1 UVM仿真驗證架構
img_seq
這部分主要是生成image數據,并發送給img_drv,由img_drv驅動給到DUT。Image的隨機化定義在img_trans中,這個類繼承了uvm_sequence_item,定義了img數組,這是一個隨機化的8bit數據組成的數組。在img_seq中通過randomize函數就可以產生隨機化數據。
class img_trans extends uvm_sequence_item;
rand logic[7:0] img[3*`MAX_RESLUTN_H*`MAX_RESLUTN_V];
…
Endclass
Img_seq中對整個仿真的啟動和結束做了控制,在這部分實現的原因是保證一幀圖像在仿真過程中的完整性。通過設置run_time來控制可以發送多少幀圖像,這個變量從命令行傳進去。Starting_phase可以使得在uvm_sequence中來啟動和終止仿真過程,只要指定了相應的sequence是某個sequencer的default_sequence,以及在sequencer中設置seq的starting_phase為sequencer的phase,這樣就可以用sequence來啟動仿真了。
if(starting_phase != null)
starting_phase.raise_objection(this);
else
`uvm_error(get_type_name(), "cannot start phase");
for(int i=0;i
if(trans.randomize() != 1)begin
`uvm_info(get_type_name(), "Failed to randomize image transaction", UVM_NONE)
end
trans.print();
`uvm_send(trans)
`uvm_info(get_type_name(), "one image have been recived", UVM_NONE)
wait(sw_inf_i.intr == 1);
end
#50;
starting_phase.drop_objection(this);
img_sqr.sv中:
virtual task main_phase(uvm_phase phase);
img_seq seq;
seq = img_seq::type_id::create("img_seq");
seq.starting_phase = phase;
seq.start(this);
endtask
img_drv
這個模塊是通過img_sqr從img_seq中獲得img的數據,然后通過axi接口發送給DUT。這部分主要是如何對接DUT的AXI接口,DUT通過AXI接口讀取image數據,因此接口功能只實現了讀操作。用一個task來實現axi_read,主要包括三個并行進程:一個是獲取axi讀指令,另一個是根據獲得的axi讀指令來發送數據,最后增加一個計數器用于處理axi等待時間過長的問題,如果超過一定時間,就報錯。指令獲取和發送數據的交互通過mailbox來實現,只要收到的axi指令不大于AXI接口可接收的最大transactions,就可以繼續接收,否則就不接受。然后發送數據進程從mailbox中獲得指令,根據指令發送對應的數據。
task img_drv::axi_read();
int abs_araddr;
int arlen_cnt;
ar_trans ar_req;
ar_trans ar_rep;
int ar_timer;
int i_size = img_size;
fork: AXI_READ_CTRL
forever begin: AR
if(ar_mbx.num() axi.s_axi_arready = 1'b1;
else
axi.s_axi_arready = 1'b0;
if(axi.s_axi_arready & axi.s_axi_arvalid)begin
ar_req = new("ar_trans");
ar_req.araddr = axi.s_axi_araddr;
ar_req.arlen = axi.s_axi_arlen;
ar_req.arid = axi.s_axi_arid;
ar_mbx.put(ar_req);
end
@(posedge axi.clk);
end
forever begin: RDATA
ar_timer = $urandom_range(`AXI_WAIT_TIMER, 0);
while(ar_timer--)
@(posedge axi.clk);
if(ar_mbx.num() == 0)begin
axi.s_axi_rvalid = 1'b0;
@(posedge axi.clk);
wait(ar_mbx.num() > 0);
end
else begin
while(!axi.s_axi_rready)
@(posedge axi.clk);
axi.s_axi_rvalid = 1'b1;
ar_mbx.get(ar_rep);
abs_araddr = ar_rep.araddr - img_start_addr;
arlen_cnt = ar_rep.arlen + 1;
if(ar_rep.araddr[10:0]+arlen_cnt*(`AXI_DATA_WD/8)>4096)begin
`uvm_error(this.get_type_name(), $sformatf("cross 4KB at address: ar_rep.araddr"));
end
while(arlen_cnt)begin
if(axi.s_axi_rready)begin
for(int i=0;i axi.s_axi_rdata[i*8 +: 8] = img_req.img[abs_araddr++];
end
arlen_cnt--;
end
if(arlen_cnt == 0)begin
axi.s_axi_rlast = 1'b1;
end
@(posedge axi.clk);
end
axi.s_axi_rlast = 1'b0;
axi.s_axi_rvalid = 1'b0;
@(posedge axi.clk);
end
end//RDATA
while(i_size)begin
if(axi.s_axi_rvalid & axi.s_axi_rready)begin
i_size--;
end
@(posedge axi.clk);
end
join_any
disable AXI_READ_CTRL;
endtask
sw_config
這部分主要是涉及到軟件端對寄存器的配置,包括圖像的行列大小,blank的行和列大小等。這些變量在sw_trans中隨機化,為了減少仿真時間,作者減小了圖像的大小約束。這應該不會影響仿真驗證結果。Sw_seq就是產生隨機化數據,然后發送給sw_drv。Sw_driver主要是實現axi4lite接口,發送數據給DUT。Axi4lite控制比較簡單,如下:
task sw_driver::axi4lite_write(logic [`AXI4LITE_DATA_WD-1:0] data, logic [`AXI4LITE_ADDR_WD-1:0] waddr);
int timer = 1000;
repeat($urandom_range(15, 5)) @(posedge vif.axi_if.clk);
fork: AXI4LITE_SEND
begin: WRITE_DATA
vif.axi_if.axi4lite_awprot = 0;
vif.axi_if.axi4lite_awaddr = waddr;
vif.axi_if.axi4lite_awvalid = 1'b1;
if(!vif.axi_if.axi4lite_awready)begin
while(!vif.axi_if.axi4lite_awready)
@(posedge vif.axi_if.clk);
end
else begin
@(posedge vif.axi_if.clk);
end
vif.axi_if.axi4lite_awvalid = 1'b0;
repeat($urandom_range(5, 0)) @(posedge vif.axi_if.clk);
vif.axi_if.axi4lite_wdata = data;
vif.axi_if.axi4lite_wstrb = {(`AXI4LITE_DATA_WD/8){1'b1}};
vif.axi_if.axi4lite_wvalid = 1'b1;
if(!vif.axi_if.axi4lite_wready)begin
while(!vif.axi_if.axi4lite_wready)
@(posedge vif.axi_if.clk);
end
else begin
@(posedge vif.axi_if.clk);
end
vif.axi_if.axi4lite_wvalid = 1'b0;
wait(vif.axi_if.axi4lite_bvalid);
repeat($urandom_range(5, 0)) @(posedge vif.axi_if.clk);
end
begin: TIMER_CNT
while(timer--)
@(posedge vif.axi_if.clk);
`uvm_error(get_type_name(), $sformatf("wait for axi ready for long: axi4lite_awaddr = %0h, axi4lite_awready = %0d",
vif.axi_if.axi4lite_awaddr, vif.axi_if.axi4lite_awready));
end
join_any
disable AXI4LITE_SEND;
`uvm_info(get_type_name(), $sformatf("have sent sw data at address: %0x", vif.axi_if.axi4lite_awaddr), UVM_MEDIUM);
Endtask
為了將sw_trans的數據發送給其他模塊,在sw_driver中通過定義:
uvm_analysis_port #(sw_trans) sw_trans_port;
在需要接收這個數據的類中聲明一個port:
uvm_analysis_imp_sw #(sw_trans, img_monitor) sw_imp;
并聲明后綴:
`uvm_analysis_imp_decl(_sw)
然后定義一個write函數:
function void img_monitor::write_sw(sw_trans sw_req);
img_h = sw_req.resl_h;
img_v = sw_req.resl_v;
endfunction
這樣就接收到了來自sw_trans的數據。
img_monitor
這部分主要是比對數據,包括兩方面,一個是比對接收到的image的圖像數據R,G,B。另外一個是比對經過物理編碼后的10bit的R,G,B數據。
task img_monitor::comp_rgb();
localparam AXI_DATA_BW = `AXI_DATA_WD/8;
logic [8*3-1:0] ref_rgb;
int err_cnt = 0;
int pixs;
int v=0;
int h=0;
while(v != img_v)begin
if(img_inf_i.rgb_valid && img_inf_i.rgb_ready && img_inf_i.h_sync && img_inf_i.v_sync)begin
pixs = 3 * (img_h * v + h);
ref_rgb = {img_data.img[pixs+2], img_data.img[pixs+1], img_data.img[pixs]};
if(ref_rgb != img_inf_i.rgb)begin
`uvm_error(this.get_type_name(), $sformatf("ref_rgb(%h, %h, %h) is diffrent with rgb(%h, %h, %h) at (%d, %d)", ref_rgb[8*2 +: 8], ref_rgb[8*1 +: 8], ref_rgb[7:0], img_inf_i.rgb[8*2 +: 8], img_inf_i.rgb[8*1 +: 8], img_inf_i.rgb[7:0], v, h));
err_cnt++;
end
@(posedge img_inf_i.clk);
v = (h >= img_h - 1) ? v+1 : v;
h = (h >= img_h - 1) ? h-img_h+1 : h+1;
end
else begin
@(posedge img_inf_i.clk);
end
end
if(err_cnt == 0)
`uvm_info(get_type_name(), "rgb comparison passed !", UVM_LOW)
Endtask
對物理編碼數據的比較,是UVM中生成對應的數據,放到隊列中,然后和DUT中的相應數據進行對比。
function void img_monitor::write_img(img_trans img_req);
localparam AXI_DATA_BW = `AXI_DATA_WD/8;
int pixs = 0;
tmds tmds_obj;
img_data.copy(img_req);
for(int i=0;i
pixs = 3 * (i * img_h + j);
tmds_obj.b = img_data.img[pixs];
tmds_obj.g = img_data.img[pixs+1];
tmds_obj.r = img_data.img[pixs+2];
tmds_obj.b = tmds_encode(tmds_obj.b, this.b_cnt);
tmds_obj.g = tmds_encode(tmds_obj.g, this.g_cnt);
tmds_obj.r = tmds_encode(tmds_obj.r, this.r_cnt);
tmds_obj.h = j;
tmds_obj.v = i;
tmds_i.push_back(tmds_obj);
end
this.b_cnt = 0;
this.g_cnt = 0;
this.r_cnt = 0;
end
endfunction
task img_monitor::comp_tmds();
localparam PREAMBLE_VIDEO = {10'b1101010100, 10'b0010101011, 10'b0101010100};//'h3542AD54
int v = 0;
int h = 0;
tmds ref_tmds_obj = new("tmds");
while(v != img_v)begin
wait(img_inf_i.tmds_valid && img_inf_i.tmds_data == PREAMBLE_VIDEO);
@(posedge img_inf_i.clk);
for(int i=0;i if(img_inf_i.tmds_data != PREAMBLE_VIDEO)
`uvm_error(get_type_name(), "video preamble is wrong!")
@(posedge img_inf_i.clk);
end
repeat(2) @(posedge img_inf_i.clk);
for(h=0;h
if(ref_tmds_obj.b != img_inf_i.tmds_data[9:0] || ref_tmds_obj.g != img_inf_i.tmds_data[19:10] || ref_tmds_obj.r != img_inf_i.tmds_data[29:20])
`uvm_error(this.get_type_name(), $sformatf("ref_tmds ( %h, %h, %h) != tmds (%h, %h, %h) at (%d, %d)", ref_tmds_obj.r, ref_tmds_obj.g, ref_tmds_obj.b, img_inf_i.tmds_data[29:20], img_inf_i.tmds_data[19:10], img_inf_i.tmds_data[9:0], v, h));
@(posedge img_inf_i.clk);
v = (h >= img_h - 1) ? v+1 : v;
end
end
`uvm_info(get_type_name(), "tmds data is compared for one frame!", UVM_LOW)
Endtask
結果
通過打印信息來判斷是否通過測試。
圖2.1 仿真結果
-
HDMI
+關注
關注
32文章
1734瀏覽量
152235 -
圖像處理
+關注
關注
27文章
1300瀏覽量
56841 -
UVM
+關注
關注
0文章
182瀏覽量
19208
發布評論請先 登錄
相關推薦
評論