这一节将介绍用于验证demosaic的UVM框架中的其他部分。
1. pkg中的内容:
文件夹自行创建哦,路径自行修改,且期望值图像自己整。
package param_pkg;
parameter BITS = 8 ; //支持8/16/24/32
parameter H_frontporch = 50 ;
parameter H_PULSE = 1 ;
parameter H_backporch = 50 ;
parameter WIDTH = 768;
parameter V_frontporch = 10 ;
parameter V_PULSE = 1 ;
parameter V_backporch = 10 ;
parameter HEIGHT = 512;
// 每个cycle内交给DUT的是 2行4列共8个pixel
parameter h_chan = 4 ;
parameter v_chan = 2 ;
parameter H_transfercycle = WIDTH / h_chan;
parameter V_transferline = HEIGHT / v_chan;
parameter H_totalcycle = H_frontporch + H_PULSE + H_transfercycle + H_backporch;
parameter V_totalline = V_frontporch + V_PULSE + V_transferline + V_backporch;
parameter totalcycle = H_totalcycle * V_totalline;
parameter string BAYER = "rggb";
parameter string IMAGE_IN = "../D01_input_img/raw_512x768_rggb.bin";
// 期望值图像路径
parameter string EXP_R = "../D02_exp_img/v2h4_dem_r.bin";
parameter string EXP_G = "../D02_exp_img/v2h4_dem_g.bin";
parameter string EXP_B = "../D02_exp_img/v2h4_dem_b.bin";
// DUT输出图像路径
parameter string ACT_R = "../D03_act_img/v2h4_dem_r.bin";
parameter string ACT_G = "../D03_act_img/v2h4_dem_g.bin";
parameter string ACT_B = "../D03_act_img/v2h4_dem_b.bin";
endpackage
2.接口
组合电路中,某一输入变量经过不同的途径传输之后,到达电路中某一汇合点的时间有先有后,这种现象称为竞争,由于竞争而使电路输出发生瞬间错误的现参叫做冒险。为了避免在RTL仿真行为中发生信号的竞争问题,建议通过非阻塞赋值来解决同步的问题。
同样的在TB--DUT的仿真行为中,也会出现时序电路中时钟和驱动信号的时序竞争问题。为了避免可能的采样竞争问题,应该在验证环境的驱动模块(driver)添加固定延迟,一是保证数据稳定后再发送给DUT,如TB把数据发送给DUT时,应该在时钟上升沿后延迟几个ns再发送数据;二是数据稳定后再采样信号,如采样从DUT送出的数据,在时钟与被驱动信号之间存在delta-cycle时,应该考虑在时钟采样沿的前几个ns采样。这种方式可以消除竞争问题。
上述可以通过如下实现,default input #1ns output #1ns指出了在clocking中所有信号默认情况下会在locking事件(clock上升沿)的前1ns来对其进行输入采样,在事件的后1ns对其输出驱动
`timescale 1 ps/ 1 ps
import param_pkg::*;
interface demo_intf (input clk, input rst_n);
bit i_data_en ;
bit o_data_en ;
bit i_pvsync ;
bit o_pvsync ;
bit i_phsync ;
bit o_phsync ;
bit done ;
logic [BITS-1:0] i_data_0, i_data_1, i_data_2, i_data_3, i_data_4, i_data_5, i_data_6, i_data_7;
logic [BITS-1:0] o_data_r_0, o_data_g_0, o_data_b_0;
logic [BITS-1:0] o_data_r_1, o_data_g_1, o_data_b_1;
logic [BITS-1:0] o_data_r_2, o_data_g_2, o_data_b_2;
logic [BITS-1:0] o_data_r_3, o_data_g_3, o_data_b_3;
logic [BITS-1:0] o_data_r_4, o_data_g_4, o_data_b_4;
logic [BITS-1:0] o_data_r_5, o_data_g_5, o_data_b_5;
logic [BITS-1:0] o_data_r_6, o_data_g_6, o_data_b_6;
logic [BITS-1:0] o_data_r_7, o_data_g_7, o_data_b_7;
clocking driver_cb @(posedge clk);
default input #1 output #1;
output i_data_en ;
input o_data_en ;
output i_phsync ;
input o_phsync ;
output i_pvsync ;
input o_pvsync ;
output i_data_0, i_data_1, i_data_2, i_data_3, i_data_4, i_data_5, i_data_6, i_data_7;
input o_data_r_0, o_data_g_0, o_data_b_0;
input o_data_r_1, o_data_g_1, o_data_b_1;
input o_data_r_2, o_data_g_2, o_data_b_2;
input o_data_r_3, o_data_g_3, o_data_b_3;
input o_data_r_4, o_data_g_4, o_data_b_4;
input o_data_r_5, o_data_g_5, o_data_b_5;
input o_data_r_6, o_data_g_6, o_data_b_6;
input o_data_r_7, o_data_g_7, o_data_b_7;
input done ;
endclocking
clocking monitor_cb @(posedge clk);
default input #1 output #1;
input i_data_en ;
input o_data_en ;
input i_phsync ;
input o_phsync ;
input i_pvsync ;
input o_pvsync ;
input i_data_0 , i_data_1, i_data_2, i_data_3, i_data_4, i_data_5, i_data_6, i_data_7;
input o_data_r_0, o_data_g_0, o_data_b_0;
input o_data_r_1, o_data_g_1, o_data_b_1;
input o_data_r_2, o_data_g_2, o_data_b_2;
input o_data_r_3, o_data_g_3, o_data_b_3;
input o_data_r_4, o_data_g_4, o_data_b_4;
input o_data_r_5, o_data_g_5, o_data_b_5;
input o_data_r_6, o_data_g_6, o_data_b_6;
input o_data_r_7, o_data_g_7, o_data_b_7;
input done ;
endclocking
modport DRIVER (clocking driver_cb , input rst_n);
modport MONITOR (clocking monitor_cb , input rst_n);
endinterface
3. top层
一是例化DUV,二是通过config_db::set 把接口广播给各个component.
其中,接口在非module内部(如class)中,必须加virtual修饰。
`timescale 1ns/1ns
`include "uvm_macros.svh"
import uvm_pkg::*;
import param_pkg::*;
module uvm_demosaic_top;
reg xclk = 0 ;
reg rst_n = 0 ;
demo_intf vifdem(xclk, rst_n);
isp_demosaic #(
.BITS (BITS ),
.H_frontporch (H_frontporch ),
.H_PULSE (H_PULSE ),
.H_backtporch (H_backporch ),
.HEIGHT (HEIGHT ),
.V_frontporch (V_frontporch ),
.V_PULSE (V_PULSE ),
.V_backtporch (V_backporch ),
.WIDTH (WIDTH ),
.h_chan (h_chan ),
.v_chan (v_chan ),
.BAYER (BAYER ))
demosaic_i0 (
.xclk (xclk ),
.rst_n (rst_n ),
.i_data_en (vifdem.i_data_en),
.i_phsync (vifdem.i_phsync ),
.i_pvsync (vifdem.i_pvsync ),
.i_data_0 (vifdem.i_data_0 ),
.i_data_1 (vifdem.i_data_1 ),
.i_data_2 (vifdem.i_data_2 ),
.i_data_3 (vifdem.i_data_3 ),
.i_data_4 (vifdem.i_data_4 ),
.i_data_5 (vifdem.i_data_5 ),
.i_data_6 (vifdem.i_data_6 ),
.i_data_7 (vifdem.i_data_7 ),
.o_data_en (vifdem.o_data_en),
.o_phsync (vifdem.o_phsync ),
.o_pvsync (vifdem.o_pvsync ),
.o_data_r_0(vifdem.o_data_r_0), .o_data_g_0(vifdem.o_data_g_0), .o_data_b_0(vifdem.o_data_b_0),
.o_data_r_1(vifdem.o_data_r_1), .o_data_g_1(vifdem.o_data_g_1), .o_data_b_1(vifdem.o_data_b_1),
.o_data_r_2(vifdem.o_data_r_2), .o_data_g_2(vifdem.o_data_g_2), .o_data_b_2(vifdem.o_data_b_2),
.o_data_r_3(vifdem.o_data_r_3), .o_data_g_3(vifdem.o_data_g_3), .o_data_b_3(vifdem.o_data_b_3),
.o_data_r_4(vifdem.o_data_r_4), .o_data_g_4(vifdem.o_data_g_4), .o_data_b_4(vifdem.o_data_b_4),
.o_data_r_5(vifdem.o_data_r_5), .o_data_g_5(vifdem.o_data_g_5), .o_data_b_5(vifdem.o_data_b_5),
.o_data_r_6(vifdem.o_data_r_6), .o_data_g_6(vifdem.o_data_g_6), .o_data_b_6(vifdem.o_data_b_6),
.o_data_r_7(vifdem.o_data_r_7), .o_data_g_7(vifdem.o_data_g_7), .o_data_b_7(vifdem.o_data_b_7),
.done (vifdem.done ));
save2bin #( .FILE0(ACT_R), .FILE1(ACT_G), .FILE2(ACT_B), .BITS(BITS), .WIDTH(WIDTH) )
save2bin_t (
.xclk (xclk ),
.rst_n (rst_n ),
.data_en (vifdem.o_data_en),
.done (vifdem.done ),
.i_data_r_0(vifdem.o_data_r_0), .i_data_g_0(vifdem.o_data_g_0), .i_data_b_0(vifdem.o_data_b_0) ,
.i_data_r_1(vifdem.o_data_r_1), .i_data_g_1(vifdem.o_data_g_1), .i_data_b_1(vifdem.o_data_b_1) ,
.i_data_r_2(vifdem.o_data_r_2), .i_data_g_2(vifdem.o_data_g_2), .i_data_b_2(vifdem.o_data_b_2) ,
.i_data_r_3(vifdem.o_data_r_3), .i_data_g_3(vifdem.o_data_g_3), .i_data_b_3(vifdem.o_data_b_3) ,
.i_data_r_4(vifdem.o_data_r_4), .i_data_g_4(vifdem.o_data_g_4), .i_data_b_4(vifdem.o_data_b_4) ,
.i_data_r_5(vifdem.o_data_r_5), .i_data_g_5(vifdem.o_data_g_5), .i_data_b_5(vifdem.o_data_b_5) ,
.i_data_r_6(vifdem.o_data_r_6), .i_data_g_6(vifdem.o_data_g_6), .i_data_b_6(vifdem.o_data_b_6) ,
.i_data_r_7(vifdem.o_data_r_7), .i_data_g_7(vifdem.o_data_g_7), .i_data_b_7(vifdem.o_data_b_7)
);
// Clock generator
always #5 xclk <= ~xclk;
initial begin
rst_n <= 0;
#10
rst_n <= 1;
//#84_245;
//$finish();
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
end
initial begin
uvm_config_db #(virtual demo_intf)::set(null, "*", "demo_intf", vifdem);
uvm_config_db #(uvm_active_passive_enum)::set(null, "*", "is_active", UVM_ACTIVE);
run_test("demo_test1");
end
endmodule
4. test_case
这里没有太多内容需要介绍。
class demo_test1 extends demo_base_test;
demo_sequence demo_seq_i;
`uvm_component_utils(demo_test1)
function new(string name = "demo_test1", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
demo_seq_i = demo_sequence::type_id::create("demo_seq_i");
endfunction
virtual task main_phase(uvm_phase phase);
phase.raise_objection(this);
super.main_phase(phase);
#10ns;
demo_seq_i.start(env_i.agt.seqr);
phase.drop_objection(this);
phase.phase_done.set_drain_time(this, 100000); // 控制结束结束。等效于在top层时间 $finish();
endtask
endclass
5. sequencer
class demo_sequencer extends uvm_sequencer #(demo_transaction) ;
`uvm_component_utils(demo_sequencer)
function new(input string name, uvm_component parent=null);
super.new(name, parent);
endfunction : new
endclass : demo_sequencer
6. agent
例化sequencer、driver和monitor,通过TLM通信分别连接到monitor中的真实值(DUT的输出交给scoreboard)和预期值(剥离出reference model,reference model输出pixel写入到的bin文件中,然后加载其数据并交给scoreboard)两部分。
class demo_agent extends uvm_agent;
demo_sequencer seqr;
demo_driver driv;
demo_mon mon ;
virtual demo_intf vif ;
// act --- 真实值
// exp --- 期望值
uvm_analysis_port#(demo_transaction) aport_act;
uvm_analysis_port#(demo_transaction) aport_exp;
`uvm_component_utils(demo_agent)
function new(string name="demo_agent",uvm_component parent=null);
super.new(name, parent);
aport_act = new("aport_act", this);
aport_exp = new("aport_exp", this);
endfunction:new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual demo_intf)::get(this, "", "demo_intf", vif)) begin
`uvm_fatal("interface", "No virtual interface specified for this env instance")
end
if(!uvm_config_db#(uvm_active_passive_enum)::get(this, "", "is_active", is_active))
`uvm_fatal("demo_agent", "No is_active")
if(is_active == UVM_ACTIVE) begin
seqr = demo_sequencer::type_id::create("seqr", this);
driv = demo_driver::type_id::create("driv", this);
end
mon = demo_mon::type_id::create("mon",this);
endfunction
function void connect_phase(uvm_phase phase);
if(is_active == UVM_ACTIVE) begin
driv.seq_item_port.connect( seqr.seq_item_export);
end
mon.act_port.connect(aport_act);
mon.exp_port.connect(aport_exp);
endfunction
endclass
7. driver
介绍driver之前,先介绍如何从bin文件中加载数据,并按照每cycle驱动h_chan x v_chan个pixel给DUT。
step1:先把第i行全部pixel存入到数组buffer_up中,第i+1行全部pixel存入到数组buffer_down中;
step2:根据把buffer_up 和 buffer_down中索引为idx ~ idx+4中的数据赋值给数组data,然后idx = idx + 4;当idx超过图像的宽度时,idx赋值为0.
step3:driver在i_data_en为1的情况下,重复执行step2;当idx超过图像的宽度时,重复执行step1和step2,直到遍历整张图像。
可参考下面代码进行理解。
// 取出1行数据,放在 buff里
class readbin #(int BITS = 8, int HEIGHT = 768, int WIDTH = 768, int h_chan = 4, int v_chan = 2);
string bin_path ;
integer height = HEIGHT ;
integer width = WIDTH ;
bit [BITS-1:0] buff_up [0:WIDTH-1] ; // 暂存一行 pixel values
bit [BITS-1:0] buff_down[0:WIDTH-1] ; // 暂存一行 pixel values
integer fd, idx = 0;
integer row_num = 0;
function set(input string BIN_path);
bin_path = BIN_path;
fd = $fopen(bin_path, "rb");
if (!fd) begin
$error("binfile could not be open: ", bin_path);
return;
end
endfunction
// 从 bin文件中读取 pixel。 每次调用 gen_pic 只能获得一行数据
function void gen_pic(output bit [BITS-1:0] data[0:7]) ;
if (idx % WIDTH == 0) begin
// 取出上面一行全部数据
for (integer j = 0; j < WIDTH; j++) begin
for (integer c = 0; c < BITS/8; c = c + 1) begin
$fscanf(fd, "%c", buff_up[j][(c*8)+:8]);
end
end
// 取出下面一行全部数据
for (integer k = 0; k < WIDTH; k++) begin
for (integer c = 0; c < BITS/8; c = c + 1) begin
$fscanf(fd, "%c", buff_down[k][(c*8)+:8]);
end
end
row_num = row_num + 1;
idx = 0;
end
data[0] = buff_up [idx + 0]; data[1] = buff_up [idx + 1]; data[4] = buff_up [idx + 2]; data[5] = buff_up [idx + 3];
data[2] = buff_down[idx + 0]; data[3] = buff_down[idx + 1]; data[6] = buff_down[idx + 2]; data[7] = buff_down[idx + 3];
idx = idx + 4;
if ((row_num == height/v_chan) && (idx == width) ) begin
$display("\nAll pixels from such path [%0s] has been read. \n", bin_path);
$fclose(fd);
end
endfunction
endclass
driver中,在i_data_en为高时,读取bin文件加载pixel值,并把读取到的pixel值交给DUT;在i_data_en为低时,把0交给DUT。
`define DRV_IF vif.DRIVER.driver_cb
//import param_pkg::*;
typedef readbin#(.BITS(BITS), .HEIGHT(HEIGHT), .WIDTH(WIDTH)) pic;
class demo_driver extends uvm_driver #(demo_transaction);
`uvm_component_utils(demo_driver)
virtual demo_intf vif ;
integer rows = 0 ;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
if( !uvm_config_db #(virtual demo_intf)::get(this, "", "demo_intf", vif) )
`uvm_error("", "uvm_config_db::get failed")
endfunction
extern virtual task drive_rx(demo_transaction req);
task run_phase(uvm_phase phase);
demo_transaction req;
drive_rx(req);
endtask
endclass: demo_driver
task demo_driver::drive_rx(demo_transaction req);
logic [BITS-1:0] data[0:h_chan*v_chan-1]; // 一次取1cycle个数据
readbin#(.BITS(BITS), .HEIGHT(HEIGHT), .WIDTH(WIDTH), .h_chan(h_chan), .v_chan(v_chan)) pic;
pic = new();
pic.set(IMAGE_IN);
forever begin
@(posedge vif.rst_n);
@(posedge vif.clk);
seq_item_port.get_next_item(req);
foreach(req.i_data_en[i]) begin
`DRV_IF.i_data_en <= req.i_data_en[i];
`DRV_IF.i_pvsync <= req.i_pvsync[i];
`DRV_IF.i_phsync <= req.i_phsync[i];
/* 1cycle 向DUT 传递 h_chan*v_chan 个 pixels*/
if (req.i_data_en[i]) begin
pic.gen_pic(data);
`DRV_IF.i_data_0 <= data[0];
`DRV_IF.i_data_1 <= data[1];
`DRV_IF.i_data_2 <= data[2];
`DRV_IF.i_data_3 <= data[3];
`DRV_IF.i_data_4 <= data[4];
`DRV_IF.i_data_5 <= data[5];
`DRV_IF.i_data_6 <= data[6];
`DRV_IF.i_data_7 <= data[7];
end else begin
`DRV_IF.i_data_0 <= {BITS{1'b0}};
`DRV_IF.i_data_1 <= {BITS{1'b0}};
`DRV_IF.i_data_2 <= {BITS{1'b0}};
`DRV_IF.i_data_3 <= {BITS{1'b0}};
`DRV_IF.i_data_4 <= {BITS{1'b0}};
`DRV_IF.i_data_5 <= {BITS{1'b0}};
`DRV_IF.i_data_6 <= {BITS{1'b0}};
`DRV_IF.i_data_7 <= {BITS{1'b0}};
end
if (req.i_phsync[i]) begin // 等价于 @ (posedge req.i_data_en)
$display("[%0d]Image visiable area: the current rows is %0d ...", $time, rows);
rows = rows + 1;
end
@(posedge vif.clk);
end
seq_item_port.item_done();
end
endtask
8. monitor
一是i_data_en为1时,从期望值bin文件 中加载数据,并交给scoreboard;
二是o_data_en为1时,把DUT的输出值交给scoreboard;
可以判断出,期望值比真实值更先被scoreboard先接收。
注意点:由于期望值是不停的通过TLM通信连接到scoreboard,在创建对象:exp_item = demo_transaction::type_id::create("exp_item");时,这语句一定要放在forever里,否则scoreboard接收到的期望值队列中的数据将全部是最新transaction。【其原因是:如果把一个对象push进队列,存入的是对象的句柄(浅拷贝),而不是把对象的数据复制进队列(深拷贝);如果要连续将monitor中采集到的期望值数据送入scoreboard,那么每采集一个期望值数据都要在monitor中new/create一个对象用来存储,不new/create的话,数据会始终存在第一个对象里,原来push进队列的dut数据会被对象中新采集的数据覆盖】
`define MONAPB_IF intf.MONITOR.monitor_cb
import param_pkg::*;
class demo_mon extends uvm_monitor;
virtual demo_intf intf;
demo_transaction act_item; // 真实值 构成的transaction,交给scoreboard
demo_transaction exp_item; // 预期值 构成的transaction,交给scoreboard
uvm_analysis_port #(demo_transaction) act_port;
uvm_analysis_port #(demo_transaction) exp_port;
`uvm_component_utils(demo_mon)
logic [BITS-1:0] exp_data_r[0:h_chan*v_chan-1];
logic [BITS-1:0] exp_data_g[0:h_chan*v_chan-1];
logic [BITS-1:0] exp_data_b[0:h_chan*v_chan-1];
readbin#(.BITS(BITS), .HEIGHT(HEIGHT), .WIDTH(WIDTH), .h_chan(h_chan), .v_chan(v_chan)) img_exp_r, img_exp_g, img_exp_b;
function new(string name="", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
act_port = new("act_port", this);
exp_port = new("exp_port", this);
act_item = demo_transaction::type_id::create("act_item");
//exp_item = demo_transaction::type_id::create("exp_item");
if(!uvm_config_db #(virtual demo_intf)::get(this, "", "demo_intf", intf)) begin
`uvm_error("ERROR::", "UVM_CONFIG_DB FAILED in demo_mon")
end
img_exp_r = new();
img_exp_g = new();
img_exp_b = new();
endfunction
virtual task run_phase(uvm_phase phase);
img_exp_r.set(EXP_R ); // 设置预期值输出图像
img_exp_g.set(EXP_G ); // 设置预期值输出图像
img_exp_b.set(EXP_B ); // 设置预期值输出图像
fork
// 1.根据 i_data_en 处理 预期 pixel
forever begin
@(posedge intf.clk);
if (`MONAPB_IF.i_data_en) begin // data_en为1,则读取数据
exp_item = demo_transaction::type_id::create("exp_item");
img_exp_r.gen_pic(exp_data_r ); // data_en上升沿时,从预期图形中加载1cycle个数据
img_exp_g.gen_pic(exp_data_g ); // data_en上升沿时,从预期图形中加载1cycle个数据
img_exp_b.gen_pic(exp_data_b ); // data_en上升沿时,从预期图形中加载1cycle个数据
for (integer i = 0; i < h_chan*v_chan; i++) begin
exp_item.o_data_r[i] = exp_data_r[i];
exp_item.o_data_g[i] = exp_data_g[i];
exp_item.o_data_b[i] = exp_data_b[i];
end
// data_en 为高,则通过TLM把数据交给 scoreboard
exp_port.write(exp_item);
end
end
// 2.根据 o_data_en 处理 真实 pixel,同时把 输出pixel 写入到 对应路径下。
forever begin
@(posedge intf.clk);
if (`MONAPB_IF.o_data_en) begin
act_item.o_data_en = `MONAPB_IF.o_data_en;
act_item.o_data_r[0] = `MONAPB_IF.o_data_r_0;
act_item.o_data_r[1] = `MONAPB_IF.o_data_r_1;
act_item.o_data_r[2] = `MONAPB_IF.o_data_r_2;
act_item.o_data_r[3] = `MONAPB_IF.o_data_r_3;
act_item.o_data_r[4] = `MONAPB_IF.o_data_r_4;
act_item.o_data_r[5] = `MONAPB_IF.o_data_r_5;
act_item.o_data_r[6] = `MONAPB_IF.o_data_r_6;
act_item.o_data_r[7] = `MONAPB_IF.o_data_r_7;
act_item.o_data_g[0] = `MONAPB_IF.o_data_g_0;
act_item.o_data_g[1] = `MONAPB_IF.o_data_g_1;
act_item.o_data_g[2] = `MONAPB_IF.o_data_g_2;
act_item.o_data_g[3] = `MONAPB_IF.o_data_g_3;
act_item.o_data_g[4] = `MONAPB_IF.o_data_g_4;
act_item.o_data_g[5] = `MONAPB_IF.o_data_g_5;
act_item.o_data_g[6] = `MONAPB_IF.o_data_g_6;
act_item.o_data_g[7] = `MONAPB_IF.o_data_g_7;
act_item.o_data_b[0] = `MONAPB_IF.o_data_b_0;
act_item.o_data_b[1] = `MONAPB_IF.o_data_b_1;
act_item.o_data_b[2] = `MONAPB_IF.o_data_b_2;
act_item.o_data_b[3] = `MONAPB_IF.o_data_b_3;
act_item.o_data_b[4] = `MONAPB_IF.o_data_b_4;
act_item.o_data_b[5] = `MONAPB_IF.o_data_b_5;
act_item.o_data_b[6] = `MONAPB_IF.o_data_b_6;
act_item.o_data_b[7] = `MONAPB_IF.o_data_b_7;
act_port.write(act_item);
end
end
join
endtask
endclass
9. environment
通过TLM通信,把monitor和 scoreboard连接到一起。
class demo_env extends uvm_env;
`uvm_component_utils(demo_env)
demo_agent agt;
demo_scoreboard scb;
function new(string name = "demo_env", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
agt = demo_agent::type_id::create("agt",this);
scb = demo_scoreboard::type_id::create("scb",this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
agt.aport_act.connect(scb.monitor_imp);
agt.aport_exp.connect(scb.model_imp);
endfunction: connect_phase
endclass: demo_env
10. base_test
import uvm_pkg::*;
`include "uvm_macros.svh"
class demo_base_test extends uvm_test;
demo_env env_i;
`uvm_component_utils(demo_base_test)
extern function new(string name = "demo_base_test", uvm_component parent);
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void start_of_simulation_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
extern virtual function void report_phase(uvm_phase phase);
extern function int num_uvm_errors();
endclass
//Constructor
function demo_base_test::new(string name = "demo_base_test", uvm_component parent);
super.new(name, parent);
endfunction
//Build_Phase
function void demo_base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env_i = demo_env::type_id::create("env_i", this);
endfunction
//start_of_simulation_phase
function void demo_base_test::start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
uvm_top.print_topology();
endfunction
//Main_Phase
task demo_base_test::main_phase(uvm_phase phase);
phase.phase_done.set_drain_time(this, 15ms);
endtask
function void demo_base_test::report_phase(uvm_phase phase);
super.report_phase(phase);
if(num_uvm_errors == 0)begin
`uvm_info(get_type_name(), "Simulation Passed!", UVM_NONE)
end else begin
`uvm_info(get_type_name(), "Simulation Failed!", UVM_NONE)
end
endfunction
function int demo_base_test::num_uvm_errors();
uvm_report_server server;
if(server == null)
server = get_report_server();
return server.get_severity_count(UVM_ERROR);
endfunction
11. scoreboard
// -----------------------------------------------------------------------------------
// Using the `uvm_analysis_imp_decl() macro allows the construction of two analysis
// implementation ports with corresponding, uniquely named, write methods
// -----------------------------------------------------------------------------------
`uvm_analysis_imp_decl(_monitor)
`uvm_analysis_imp_decl(_model)
import param_pkg::*;
class demo_scoreboard extends uvm_scoreboard;
`uvm_component_utils(demo_scoreboard)
demo_transaction exp_que[$] ; // 期望队列,存的是transaction的句柄
demo_transaction act_que[$] ; // 实际队列
demo_transaction act_value ;
demo_transaction exp_value ;
logic [BITS-1:0] act_data_r[0 : h_chan*v_chan-1]; // 实际值
logic [BITS-1:0] act_data_g[0 : h_chan*v_chan-1];
logic [BITS-1:0] act_data_b[0 : h_chan*v_chan-1];
logic [BITS-1:0] exp_data_r[0 : h_chan*v_chan-1];
logic [BITS-1:0] exp_data_g[0 : h_chan*v_chan-1];
logic [BITS-1:0] exp_data_b[0 : h_chan*v_chan-1];
int packets_passed ;
int packets_failed ;
int index ;
uvm_analysis_imp_monitor#(demo_transaction, demo_scoreboard) monitor_imp;
uvm_analysis_imp_model #(demo_transaction, demo_scoreboard) model_imp;
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
monitor_imp = new("monitor_imp", this);
model_imp = new( "model_imp", this);
endfunction: build_phase
virtual function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
endfunction: connect_phase
extern function void write_monitor (demo_transaction tr);
extern function void write_model (demo_transaction tr);
extern task check_output_inorder();
virtual task run_phase(uvm_phase phase);
demo_transaction act_value;
super.run_phase(phase);
check_output_inorder();
$display("packets_passed = %0d, packets_failed = %0d", packets_passed, packets_failed);
//check_output_outorder();
if (packets_failed == 0) begin
$display( "\n\n\tresult: \t\tSUCCESS\n\n");
end else begin
$display( "\n\n\tresult: \t\tFAILED\n\n");
end
endtask
endclass
function void demo_scoreboard::write_monitor(demo_transaction tr);
act_que.push_back(tr); // 插入元素到queue($)(队尾)
endfunction
function void demo_scoreboard::write_model(demo_transaction tr);
exp_que.push_back(tr); // 插入元素到queue($)(队尾)
endfunction
// DUT 按照 顺序 输出
task demo_scoreboard::check_output_inorder();
int index_total = WIDTH/h_chan * HEIGHT/v_chan; // data_en 为1区域 的宽度
forever begin
wait ((exp_que.size() > 0) && (act_que.size() > 0)) ;
// 真实值
act_value = act_que.pop_front();
for (integer i = 0; i < h_chan*v_chan; i++) begin
act_data_r[i] = act_value.o_data_r[i] ;
act_data_g[i] = act_value.o_data_g[i] ;
act_data_b[i] = act_value.o_data_b[i] ;
end
// 期望值
exp_value = exp_que.pop_front();
for (integer i = 0; i < h_chan*v_chan; i++) begin
exp_data_r[i] = exp_value.o_data_r[i] ;
exp_data_g[i] = exp_value.o_data_g[i] ;
exp_data_b[i] = exp_value.o_data_b[i] ;
end
for (integer i = 0; i < h_chan*v_chan; i++) begin
if ((act_data_r[i] == exp_data_r[i]) &&
(act_data_g[i] == exp_data_g[i]) &&
(act_data_b[i] == exp_data_b[i]) ) begin
packets_passed = packets_passed + 1;
end else begin
packets_failed = packets_failed + 1;
$display("Mismatch: index = %0d", index);
$display("act_data_r = %0d, \tact_data_g = %0d, \tact_data_b = %0d", act_data_r[i], act_data_g[i], act_data_b[i]);
$display("exp_data_r = %0d, \texp_data_g = %0d, \texp_data_b = %0d", exp_data_r[i], exp_data_g[i], exp_data_b[i]);
end
end
index = index + 1;
// 无法退出forever,所以增加了这个if函数
if (index == index_total) begin
break;
end
end
endtask
至此,搭建用于验证ISP的UVM环境已经完成。
12. 运行结果:
通过matlab 查看bin文件,得到如下两张图。
13 参考代码
所有的代码可在github中找到