ISP图像处理之Demosaic算法(UVM最后篇)

这一节将介绍用于验证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中找到

GitHub - Eyrehu/UVM_ISP

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
UVM 中,我们可以通过设置消息处理器(Message Handler)来控制 UVM 最后打印的消息格式。具体来说,我们可以在构建阶段(`build_phase`)中设置消息处理器的格式,例如: ```verilog class my_env extends uvm_env; // ... function void build_phase(uvm_phase phase); super.build_phase(phase); //获取默认消息处理uvm_default_handler default_h = uvm_default_factory.get_default_handler(); //设置默认消息处理器的格式 default_h.set_format_string("%m @ %t: %s"); endfunction // ... endclass ``` 在上述例子中,我们获取了默认的消息处理器,并设置了其格式为`"%m @ %t: %s"`。其中,`%m`表示消息类型,`%t`表示时间戳,`%s`表示消息内容。 除了设置默认消息处理器的格式外,我们还可以创建自定义消息处理器,并根据需要设置其格式。例如: ```verilog class my_msg_handler extends uvm_msg_handler; // ... function void report_phase(uvm_phase phase); super.report_phase(phase); //设置消息处理器的格式 set_format_string("%m @ %t: %s"); endfunction // ... endclass ``` 在上述例子中,我们创建了一个自定义消息处理器,并在报告阶段(`report_phase`)中设置了其格式为`"%m @ %t: %s"`。 最后,我们需要在测试用例或顶层中设置消息处理器,例如: ```verilog class my_test extends uvm_test; // ... function void build_phase(uvm_phase phase); super.build_phase(phase); //设置消息处理器 my_msg_handler msg_h = new(); uvm_report_info.set_msg_handler(msg_h); endfunction // ... endclass ``` 在上述例子中,我们创建了一个自定义消息处理器`msg_h`,并将其设置为消息处理器。 通过以上步骤,我们可以控制 UVM 最后打印的消息格式。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值