搭建一个SV验证环境(1)

transaction

transaction是验证平台内传输信息的基本单元,transaction会将信息从一个验证组件发送到另一个验证组件里面;transaction结构通常与协议帧结构相关;
在这里我们定义一个最简单的帧,帧包含header、payload;

class simple_packet ;
    rand bit [31:0] header;
    rand bit [ 7:0] payload [];
    rand bit [31:0] payload_len ;  
    //****   随机约束  ****//
    constraints payload_le_cons {
        payload_len inside {[0,32'h80000000]};
    }
    constraints pay_load_cons {
        patload.size == payload_len;
        solve payload_len before payload ;   //要求先随机payload_len,后随机payload
    }
    //***    函数定义     ***//
    extern virtual function void pack (ref bit [7:0] bytes[]);
    extern virtual function void unpack (ref bit [7:0] bytes[]);   //与pack功能相反
endclass

function void simple_packet::pack(ref bit [7:0] bytes[]);   //将数据帧打包成为字节数据,方便组件处理
    bytes = new(8+payload_len);
    for(int i=0;i<4;i++) begin
        bytes[i] = header[i*8+:8];
        bytes[i+payload+4] = crc[i*8+:8];
    end
    for(int i=0;i

interface

interface时验证环境与DUT之间的桥梁,环境通过interface将激励输入DUT,同时又通过interface获取DUT输出;

interface simple_intf(input bit clk);
    logic              da_vld ;
    logic [7:0]        da        ;
    modport master (    //用来指定信号方向
        output da_valid,da;
    );
    modport slave(
        input da_valid,da;
    );
endinterface

driver

driver的功能就是将transaction转换为interface的时序信号,给DUT提供激励;

class driver;
    string inst_name;
    simple_packet in_pac[$];  //用来输入的transaction
    simple_packet tmp_pac;
    virtual simple_intf drv_intf;
    function new(strring drv_name,simple_intf  drv_in_intf);
        inst_name = drv_name  ;
        drv_intf      = drv_in_intf ;
    endfunction
    external function void run();
endcalss
function void driver::run()
    while(1) begin
        if(in_pac.size>0) begin
            tmp_pac = in_pac.pop_front();
            for(int i=0;i

monitor

monitor是用来监控DUT的输出数据,并将数据转换为transaction;

class monitor;
    string inst_name;
    simple_packet out_pac[$];  //用来存储要输出的transaction
    simple_packet tmp_pac;
    virtual simple_intf mon_intf;
    function new(strring mon_name,simple_intf  mon_in_intf);
        inst_name   = drv_name  ;
        mon_intf      = mon_in_intf ;
    endfunction
    external function void run();
endcalss
function void monitor::run()
    bit [31:0] pac_len ;
    bit [ 7:0] payload [$];
    while(1) begin
        @(posedge mon_intf.clk);
        if(mon_intf.slave.data_valid==1'b1) begin
            pac_len++;
            payload.push_back(mon_intf.slave.data);
        end
        else begin
            tmp_pac = new();
            tmp_pac.payload_len = pac_len;
            tmp_pac.payload        = new(pac_len);
            foreach(tmp_pac.payload[i]) tmp_pac.payload[i] = payload.pop_front();
            out_pac.push_back(tmp_pac);
            pack_len = 0;
            payload.delete();
        end
    end
endfunction

ref_model

参考模型需要完成与UVM相同的功能。本文由于不带DUT进行仿真,参考模型直接将输入数据输出。这样验证环境的能够正常运行;

class ref_model;
    string inst_name;
    simple_packet in_pac[$];  //用来存储要输入的transaction
    simple_packet out_pac[$];  //用来存储要输出的transaction
    simple_packet tmp_pac;
    function new(strring ref_name);
        inst_name   = drv_name  ;
    endfunction
    external function void run();
endcalss
function void monitor::run()
    while(1) begin
        if(in_pac.size>0)begin
            tmp_pac = in_pac.pop_front();
            out_pac.push_back(tmp_pac);
        end
        else begin
            #10ns;
        end
    end
endfunction

scoreboard

scoreboard进行数据比对,将DUT输出数据和ref输出数据进行比对;

class rscoreboard;
    string inst_name;
    simple_packet mon_pac[$];  //用来存储要mon的transaction
    simple_packet ref_pac[$];  //用来ref输出transaction
    simple_packet tmp_mon_pac;
    simple_packet tmp_ref_pac;
    function new(strring scb_name);
        inst_name   = scb_name  ;
    endfunction
    external function void run();
endcalss
function void monitor::run()
    bit payload_eq ;
    while(1) begin
        if(mon_pac.size>0)begin
            tmp_mon_pac = mon_pac.pop_front();
            if(ref_pac.size>0) begin
                tmp_ref_pac    = ref_pac.pop_front();
                if( tmp_mon_pac.payload_len != tmp_ref_pac.payload_len) $display("mon_pac's payload_len is not desired");
                else begin
                    payload_eq = 1 ;
                    foreach(tmp_mon_pac.payload[i]) begin
                        if(tmp_mon_pac.payload[i] != tmp_ref_pac.payload[i]) payload_eq = 0 ;
                    end
                    if(payload_eq==1) $display("compare pass");
                    else $display("compare error");
                 end
             end
             else begin
                 $display("mon_pac is not desired");
             end
        end
        else begin
            #10ns;
        end
    end
endfunction

env

env是验证环境的顶层,所以其他组件都例化在env内,且他们之间的连接需要在env内完成。本文没有使用sv的专用的连接组件,但是也可以通过task实现各个组件间的transaction传递。

class env;
    driver    drv;
    monitor mon;
    ref_model ref_model ;
    scoreboard scb ;
    event finish_event;
    virtual simple_intf drv_intf ;
    virtual simple_intf mon_intf;
    extern virtual function void new();
    extern virtual function void connect(); //连接env的intf和harness的intf,后面介绍
    extern virtual task ref2scb();
    extern virtual task mon2scb();
    extern virtual task run();
endclass
function void env::new();
    drv   = new("drv",drv_intf);
    mon = new("mon",mon_intf);
    ref_model = new("ref_model");
    scb = new("scb");
endfunction
task env::ref2scb();
    while(1) begin
        if(ref_mode.out_pac.size>0) begin
            scb.ref_pac.push_back(ref_model.out_pac.pop_front());
        end
        else begin
            #10ns;
        end
    end
endtask
task env::mon2scb();
    while(1) begin
        if(mon.out_pac.size>0) begin
            scb.mon_pac.push_back(mon.out_pac.pop_front());
        end
        else begin
            #10ns;
        end
    end
endtask
task run();
    fork
        ref2scb();
        mon2scb();
        drv.run();
        mon.run();
        ref_model.run();
        scb.run();
        begin
            @finish_event;
        end
        begin
            #1ms;   //超时时间,当仿真在1ms还没有结束直接结束仿真
        end
    join_any
endtask

使用这个验证环境需要在tc里面将要发送的transaction push_back到drv和ref_model的in_pac队列里面。这样整个验证环境就能运行起来。接下来介绍harness和tc的内容:

harness

在harness通常实现验证环境与DUT的连接,harness可以作为验证的顶层之一,以module的形式存在;

module dut(
    input [7:0] din       ,
    input         din_vld ,
    output [7:0] dout   ,
    output          dout_vld
)
    assign dout        = din;
    assign dout_vdl = din_vld ;
endmodule
module harness();
    simple_intf drv_intf ;
    simplre_intf mon_intf;
    dut dut_inst(
        .din         (drv_intf.master.da),
        .din_vld  (drv.intf.master.da_valif),
        dout       (mon_intf.slave.da),
        dout_vld(mon_intf.slave.da_valid)
    );
endmodule

tc

tc是控制测试环境进行测试的,例如控制发送的激励数据等。

program test;
    sim_transaction drv_pac;
    env             env    ;
    initial begin
       fork
       begin
           env = new();
           env.run() ;
       end
       begin
           drv_pac = new();
           env.drv.in_pac.push_back(drv_pac);
           env.ref_model.in_pac.push_back(drv_pac);
           #1000ns;
           ->env.finish_event;
       end
       join_any
                  
    end
endprogram

makefile

makefile如下:其中tb.vf为验证环境的filelist;dut.vf为dut的filelist;wave_fsdb.do为ucli的脚本文件,用例控制dump波形。
compile:
vcs -lca -kdb -full64 -debug_access+all +v2k -sverilog -notice -timescale 1ps/1fs -f tb.vf -f dut.vf …/tc/${tc}.sv -o ${tc}_simv -l ${tc}_compile.log

run: compile
./${tc}_simv +fsdbfile+${tc}.fsdb +dump_mda +fsdb+delta -ucli -do wave_fsdb.do -l ${tc}.log

ncrun:
./${tc}_simv +fsdbfile+${tc}.fsdb +dump_mda +fsdb+delta -ucli -do wave_fsdb.do -l ${tc}.log

.PHONY: compile run ncrun

你可能感兴趣的:(小记录,SV,验证环境)