AHB—SRAMC基于SV的Testbench之二(driver、monitor、scoreboard、environment)

2.5.数据驱动模块driver:driver.sv

generator与driver之间属于异步通信,二者之间要实现同步通信需要使用“握手机制”,即event事件。
需要注意的是,由generator产生进过agent发送来的数据并不具备时序,因此driver再将数据发送至DUT时,需要进行一定的时序处理

`ifndef DRIVER_SV
`define DRIVER_SV

class driver;
  mailbox         agt2drv_mbx = new();     //创建邮箱,以采集由agent发送至driver的数据包(无时序)
  transaction     tr;   
  virtaul  ahb_alv_if  slv_if;             //driver会将接收到的数据按照AHB时序处理后,通过接口interface发送至DUT
  int             tr_num;                  //发包数量
  logic  [31:0]   hwdata_ld;               //做时序用途,因为数据阶段会比地址阶段晚一拍发送

  extern function new(mailbox agt2drv_mbx,virtual ahb_slv_if slv_if,int tr_num);
  extern function build();                 //new函数主要将driver内部成员(从generator发送到agent,再到driver的
  extern task run();                       //tr数据包)与DUT的外部接口DUT成员连接起来,以便数据发送

endclass

function driver::new(mailbox agt2drv_mbx,virtual ahb_slv_if slv_if,int tr_num);
  this.agt2drv_mbx = agt2drv_mbx;          //new函数主要起连接作用,连接agent的邮箱,以获取agent发来的数据包
  this.slv_if = slv_if;
  this.tr_num = tr_num;
endfunction

function driver::build();
endfunction

task driver::run();                        //run将对agent发来的数据进行时序处理,再进过接口发送进入DUT
  @(posedge slv_if.hresetn);               //在run开始之前,先等待DUT的复位信号失效,有低变高
  @slv_if.drv_cb;
  @slv_if.drv_cb;                          //等待两个时钟周期
  repeat(tr_num)begin
    tr = new();                            //创建数据包对象
    agt2drv_mbx.get(tr);                   //将由agent发送来的数据从邮箱mailbox中取出
    wait(slv_if.hready_resp);              //master需要看到slave ready信号是才会发送数据,
      slv_if.drv_cb.hsel   <= tr.hsel;     //driver是去模拟master的行为时序,等slave ready为高时,才将数据发出
      slv_if.drv_cb.haddr  <= tr.haddr;
      slv_if.drv_cb.htrans <= tr.htrans;
      slv_if.drv_cb.hwrite <= tr.hwrite;
      slv_if.drv_cb.hsize  <= tr.hsize;    //右侧数据为generator进过agent发送来driver的数据
      slv_if.drv_cb.hready <= tr.hready;   //地址和控制信号相对于写数据信号寄存一拍
      hwdata_ld            <= tr.hwdata;
      @slv_if.drv_cb;                      //等待一个时钟周期,再发送写数据信号
      slv_if.drv_cb.hwdata <= tr.hwdata; 
    end
  
  repeat(10)begin
    @slv_if.drv_cb;                        //后续处理,等待几个周期,以防止尾巴上的时序数据丢失
  end

endtask

`endif

2.6.结果采样模块monitor:monitor.sv

monitor采集模块需要将采集的数据做时序还原处理,使其不再具备时序,然后再将其打包放入邮箱发送至scb。

`ifndef MONITOR_SV
`define MONITOR_SV

class monitor;
  mailbox          mon2scb_mbx = new();    //创建邮箱,采集DUT输出的数据,并发送至scb
  transaction      tr;                     //数据包对象句柄,其中数据无时序概念
  virtaul ahb_slv_if slv_if                //monitor与DUT之间通过接口相连
  int              tr_num;                 //发包个数

  logic  [31:0]    haddr_ld;               //从接口中采集的数据是按照AHB时序采集的
  logic            hwrite_ld;              //在将这些数据发送至scb之前需要将地址和控制信号放置一拍,与数据对齐
  logic  [1:0]     htrans_ld;              //然后一起打包成tr,放入邮箱,发送至scb
  logic  [1:0]     hsize_ld;
  logic            hsel_ld;

  extern function new(mailbox mon2scb_mbx,virtaul ahb_slv_if slv_if,int tr_num);
  extern function build();
  extern task run();

endclass

function monitor::new(mailbox mon2scb_mbx,virtaul ahb_slv_if slv_if,int tr_num);
  this.mon2scb_mbx = mon2scb_mbx;          //new函数主要起连接作用,将顶层邮箱与本地邮箱连接起来
  this.slv_if = slv_if;                    //将顶层DUT的interface与本地相连
  this.tr_num = tr_num;
endfunction

function driver::build();                  //初始化配置
endfunction

task monitor::run();                       //实时采集slv_if的数据,并将采集的数据进行时序处理,打包放入邮箱送至scb
  @(posedge slv_if.hresetn);               //在run开始之前,先等待DUT的复位信号失效,有低变高
  @slv_if.mon_cb;
  @slv_if.mon_cb;                          //等待两个时钟周期
  repeat(tr_num)begin
    tr = new();                            //创建数据包对象
    haddr_ld  <= slv_if.mon_cb.haddr;
    htrans_ld <= slv_if.mon_cb.htrans;
    hwrite_ld <= slv_if.mon_cb.hwrite;
    hsize_ld  <= slv_if.mon_cb.hsize;
    hsel_ld   <= slv_if.mon_cb.hsel;
    @slv_if.mon_cb;                //等待一个时钟周期,即将monitor采集的接口地址阶段信号寄存一拍
    tr.haddr  = haddr_ld;         
    tr.htrans = htrans_ld;         //由于tr对象里的数据都为随机化数据,故只能使用阻塞赋值
    tr.hwrite = hwrite_ld;
    tr.hsize  = hsize_ld;
    tr.hsel   = hsel_ld;           //地址和控制信号,slave选择信号寄存一拍,到来需等待数据信号
    tr.hwdata = slv_if.mon_cb.hwdata;
    tr.hrdata = slv_if.mon_cb.hrdata;
    mon2scb_mbx.put(tr);
  end

  repeat(10) begin
    @slv_if.mon_cb;                  //后续时钟延长处理,防止数据丢失
  end

endtask

`endif

2.7.结果比对模块scoreboard:scoreboard.sv

计分板scoreboard会将从agent发来的数据与由monitor收集的DUT输出数据进行一一比对。具体比对方法可参考文章:简单全加器验证—Testbench。

`ifndef SCOREBOARD_SV
`define SCOREBOARD_SV

class scoreboard;
  int         tr_num;
  mailbox     agt2scb_mbx = new();               //创建邮箱,接收由agent发来的数据,激励
  mailbox     mon2scb_mbx = new();               //创建邮箱,接收由monitor发来的数据,DUT输出
  transaction tr;

  parameter    ADDR_WIDTH = 16//系统地址空间64K=2^6×2^10=2^16
  parameter    SRAM_ADDR_WIDTH = ADDR_WIDTH -2;  //SRAM地址空间16K=2^4×2^10=2^14
  parameter    SRAM_DEPTH = 1 << (SRAM_ADDR_WIDTH);  //1左移14位,即为2^14

  logic [31:0] sram_gld[SRAM_DEPTH];              //golden sram, 初始值为x,未知态
   //sram_gld是一个由2^14个32位寄存器组成的存储器,即该存储器深度为2^14,数据位宽为32bit,地址位宽为14bit
  logic [31:0] sram_gld_rdata;                    //临时变量,用于存储golden model单个数据,便于一一比对
  logic [31:0] sram_cmp_rdata;                    //临时变量,用于存储DUT输出的单个数据,便于一一比对

  int          err_cnt = 0//记录比较的数据不一致的错误次数

  extern function new(mailbox agt2scb_mbx,mailbx mon2scb_mbx,int tr_num);
  extern function build();
  extern task check();
  extern task run();

endclass

function scoreboard::new(mailbox agt2scb_mbx,mailbx mon2scb_mbx,int tr_num);
  this.agt2scb_mbx = agt2scb_mbx;                //将env的mailbox与tr_num传进来,建立连接
  this.mon2scb_mbx = mon2scb_mbx;
  this.tr_num = tr_num;
endfunction

function scoreboard::build();
endfunction

task scoreboard::check();
  repeat(tr_num)begin
    mon2scb_mbx.get(tr);            //从monitor到scoreboard的邮箱中取出一个数据包,用于做数据比对
    $display("**@%0t:SCB::tr.haddr(%0h),tr.hwdata(%0h)",$time,tr.haddr,tr.hwdata);  //打印,用于检查错误
    if(tr.hsel  && tr.htrans[1]) begin           //检查数据是否为有效传输
      if(tr.hwrite) begin                        //如果为写数据命令
        case({tr.hsize[1:0],tr.haddr[1:0]})begin //根据地址低两位来判断传输带宽8/16/32bit
          4'b00_00: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][7:0]   = tr.hwdata[7:0]; //sram_gld地址位宽14bit,写8bit
          4'b00_01: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][15:8]  = tr.hwdata[15:8];      //写8bit
          4'b00_10: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][23:16] = tr.hwdata[23:16];     //写8bit
          4'b00_11: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:24] = tr.hwdata[31:24];     //写8bit
          4'b01_00: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][15:0]  = tr.hwdata[15:0];      //写低16bit
          4'b01_10: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:16] = tr.hwdata[31:16];     //写高16bit
          4'b10_00: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:0]  = tr.hwdata[31:0];      //写32bit
          default: begin
              sram_gld[tr.haddr[ADDR_WIDTH-1:2]] = 32'hx;
              $display("**@%0t:ERROR::WRONG hsize (%0h) and haddr[1:0](%0h)",$time,tr.hsize,tr.haddr[1:0]);
              err_cnt++;
              end
        endcase    
      end
     else begin                           //读数据命令,进行比较
       sram_gld_rdata = 32'h0000_0000;    //初始化临时变量,用于存储DUT输出的读数据
       sram_cmp_rdata = 32'h0000_0000;    //初始化临时变量,用于存储agent发来的数据
       case({tr.hsize[1:0],tr.haddr[1:0]})
         4'b00_00: begin
              sram_cmp_rdata[7:0] = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][7:0];  //DUT输出的比对数据
              sram_gld_rdata[7:0] = tr.hrdata[7:0];                           //agent发来的比对数据 
         4'b00_01: begin
              sram_cmp_rdata[15:8] = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][15:8];  //DUT输出的比对数据
              sram_gld_rdata[15:8] = tr.hrdata[15:8];                           //agent发来的比对数据 
         4'b00_10: begin
              sram_cmp_rdata[23:16] = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][23:16];  //DUT输出的比对数据
              sram_gld_rdata[23:16] = tr.hrdata[23:16];                           //agent发来的比对数据 
         4'b00_11: begin
              sram_cmp_rdata[31:24] = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:24];  //DUT输出的比对数据
              sram_gld_rdata[31:24] = tr.hrdata[31:24];                           //agent发来的比对数据 
         4'b01_00: begin
              sram_cmp_rdata[15:0] = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][15:0];  //DUT输出的比对数据
              sram_gld_rdata[15:0] = tr.hrdata[15:0];                           //agent发来的比对数据 
         4'b01_10: begin
              sram_cmp_rdata = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:24];  //DUT输出的比对数据
              sram_gld_rdata = tr.hrdata;                                //agent发来的比对数据 ,32bit可省略bit位
         default: begin
              sram_gld[tr.haddr[ADDR_WIDTH-1:2]] = 32'hx;
              $display("**@%0t:ERROR::WRONG hsize (%0h) and haddr[1:0](%0h)",$time,tr.hsize,tr.haddr[1:0]);
              err_cnt++;
              end
         endcase    
     
    if(sram_gld_rdata !== sram_cmp_rdata)begin            //golden model定义为logic类型,存在未知态x,故采用!==
       $display("**@%0t:ERROR::sram_gld_hrdata(%0h) !== sram_cmp_hrdata(%0h) in haddr(%0h)",      \
                                $time,sram_gld_rdata,sram_cmp_rdata,tr.haddr[ADDR_WIDTH-1:0]); 
       err_cnt++;                                                    //地址不进行移位处理,便于观察
       end
     end     //else begin   
    end    //if(tr.hsel  && tr.htrans[1]) begin
  end    // repeat(tr_num)begin

endtask

task scoreboard::run();
  check();                                                      //检查错误
  if(err_cnt)begin
     $display("**********************************************");
     $display("**********************************************");
     $display("******************TEST  PASS******************");
     $display("**********************************************");
     $display("**********************************************");
   else begin
     $display("**********************************************");
     $display("**********************************************");
     $display("**************TEST FAILED with %d errors******",err_cnt);
     $display("**********************************************");
     $display("**********************************************");
   end
 
 endtask

`endif

2.8.验证环境environment:environment.sv

Environment验证环境通过定义公共邮箱,将其内部的各个子组件进行连接,从而实现数据在各组件之间的传输;同时需要定义接口slv_if,将内部子组件与DUT进行连接;此外还需传递具体的发包数目tr_num。

class environment;
  generator               gen;            //environment中包含了generator、agent、driver、monitor和scoreboard
  agent                   agt             //等子组件,并且会对其进行调用
  driver                  drv;
  monitor                 mon;
  scoreboard              scb;

  int                     tr_num;        //定义发包数目,发挥指挥作用
   
  mailbox       gen2agt_mbx = new();      //environment顶层会定义公共邮箱,进行邮箱连接,传递数据
  mailbox       agt2drv_mbx = new();
  mailbox       agt2scb_mbx = new();
  mailbox       mon2scb_mbx = new();

  virtaul    ahb_slv_if      slv_if;      //env顶层通过接口interface与子组件之间进行连接
   
  extern function new(virtaul ahb_slv_if slv_if,int tr_num);
  extern task build();
  extern task run();
  
endclass

function environment::new(virtaul ahb_slv_if slv_if,int tr_num);
  this.slv_if = slv_if;            //new函数执行时,会将env顶层外部(DUT传递来的)接口信号与其子组件连接
  this.tr_num = tr_num;
endfunction

task environment::build();
  gen = new(gen2agt_mbx, tr_num);         //env层公共邮箱的建立是为了实现数据包tr在组件之间的传递
  agt = new(gen2agt_mbx,agt2drv_mbx,agt2scb_mbx,tr_num);
  drv = new(agt2drv_mbx,slv_if,tr_num);   //slv_if负责连接DUT
  mon = new(mon2scb_mbx,slv_if,tr_num);   //tr_num的具体数值有testcase层给出
  scb = new(agt2scb_mbx,mon2scb_mbx,tr_num);
endtask

task environment::task();
  fork
     gen.run();                           //产生数据,给到agent
     agt.run();                           //将接受自generator的数据通过邮箱发送至drv,scb
     drv.run();                           //将接受自generator的数据,按照AHB时序协议通过接口slv_if送至DUT
     mon.run();                           //采集DUT输出,将其转化为无时序状态,打包发送至scb
     scb.run();                           //将golden model与无时序状态的DUT输出进行一一比较
  join
endtask

`endif

发布了21 篇原创文章 · 获赞 10 · 访问量 1830

猜你喜欢

转载自blog.csdn.net/weixin_46022434/article/details/105103231