AHB_SRAM UVM验证(5)完善monitor与scoreboard

完善monitor与scoreboard

  • monitor。负责监测interface上的数据,并将其送入scoreboard进行数据比对。目前的数据发送都是SINGLE,所以只需要将写的数据送回即可,但是当存在NSEQ、SEQ时,数据需要堆积起来,放入数组中。这也是之前在dirver中将数据放入的是数组,而不是一个变量。虽然目前还未用到SEQ传输,但是需要数据来存放。所以在lvc_ahb_transaction中需要加入一个函数:
  function void increase_data(int n = 1);//如果新加入data,将data宽度+1,再把之前的data放入
    data = new[data.size + 1] (data);
    all_beat_response = new[all_beat_response.size + 1] (all_beat_response);
  endfunction

完善lvc_ahb_monitor:

  • 在monitor_transaction中执行收集任务,并item_observed_port.write(t)。这句write是内建函数。使用这种方式将monitor采集到的seq写入,在被广播的对象处拿到。
  • 先在SEQ处,采集AHB的驱动信号,根据采集到的xact来判断需要收集读还是写数据。forever的目的是一直采集数据知道本次burst结束
`ifndef LVC_AHB_MONITOR_SV
`define LVC_AHB_MONITOR_SV
 
class lvc_ahb_monitor extends uvm_monitor;
  lvc_ahb_agent_configuration cfg;//添加配置、virtual接口
  virtual lvc_ahb_if vif;
  uvm_analysis_port #(lvc_ahb_transaction) item_observed_port;//广播到cov、scb
 
  `uvm_component_utils(lvc_ahb_monitor)
 
  function new(string name = "lvc_ahb_monitor", uvm_component parent = null);
    super.new(name, parent);
    item_observed_port = new("item_observed_port", this);
  endfunction
 
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  endfunction
 
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
  endfunction
 
  task run_phase(uvm_phase phase);
    super.run_phase(phase);
    fork
      monitor_transactions();
    join_none
  endtask
 
  task monitor_transactions();
    lvc_ahb_transaction t;
    forever begin
      collect_transfer(t);
      item_observed_port.write(t);//collect采样到transfer后通过analysis_port广播出去
    end
  endtask
 
  task collect_transfer(output lvc_ahb_transaction t);
    // collect transfer from interface收集interface来的trans
    t = lvc_ahb_transaction::type_id::create("t");//创建一个trans
    @(vif.cb_mon iff vif.cb_mon.htrans == NSEQ);//等到NSEQ开始(trans是向量和左边枚举类型有隐式转化)
    t.trans_type = trans_type_enum'(vif.cb_mon.htrans);//把状态记录下来
    t.xact_type = xact_type_enum'(vif.cb_mon.hwrite);//记录是write or read
    t.burst_type = burst_type_enum'(vif.cb_mon.hburst);
    t.burst_size = burst_size_enum'(vif.cb_mon.hsize);
    t.addr = vif.cb_mon.haddr;
    forever begin
      monitor_valid_data(t);
      if(t.trans_type == IDLE) 
        break;
    end
    t.response_type = t.all_beat_response[t.current_data_beat_num];//记录数据的最后一个rsp
  endtask
 
  task monitor_valid_data(lvc_ahb_transaction t);
    @(vif.cb_mon iff vif.cb_mon.hready);
    t.increase_data();//data新增加一位
    t.current_data_beat_num = t.data.size() - 1;//当前传输的位数
    t.data[t.current_data_beat_num] = t.xact_type == WRITE ? vif.cb_mon.hwdata : vif.cb_mon.hrdata;//判断读写传入数据
    t.all_beat_response[t.current_data_beat_num] = response_type_enum'(vif.cb_mon.hresp);//传入当前rsp
    t.trans_type = trans_type_enum'(vif.cb_mon.htrans);//当前传输状态
  endtask
endclass
`endif // LVC_AHB_MONITOR_SV

完善scoreboard

  • 为了后续方便seq地址范围,规定一下地址的范围。DUT的地址线16bit,所以规定地址范围:将定义的addr_start一级addr_end加入rkv_ahbram_config。属于配置的信息。
  bit [31:0] addr_start;
  bit [31:0] addr_end;
  int data_width;
  • 然后再在rkv_ahbram_base_test中规定范围的值:
  • 放在创建了cfg之后再赋值,否则会找不到cfg。
  // do parameter configuration
    cfg.addr_start = 32'h0;
    cfg.addr_end = 32'h0000_FFFF;

最后完善rkv_ahbram_scoreboard:

`ifndef RKV_AHBRAM_SCOREBOARD_SV
`define RKV_AHBRAM_SCOREBOARD_SV

// score from subscriber
class rkv_ahbram_scoreboard extends rkv_ahbram_subscriber;

	// events of scoreboard
  bit [31:0] mem[int unsigned]; // memory;


	// typedef enum {CHECK_LOADCOUNTER} check_type_e;
	`uvm_component_utils(rkv_ahbram_scoreboard)
	function new(string name = "rkv_ahbram_scoreboard",uvm_component parent);
		super.new(name, parent);
	endfunction

	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
	endfunction

	task run_phase(uvm_phase phase);
		super.run_phase(phase);
		do_data_check();
	endtask

  virtual function void write(lvc_ahb_transaction tr);
    if(is_addr_valid(tr.addr)) begin // addr is vaild
      case(tr.xact_type)
        WRITE: store_data_with_hburst(tr); // store data
        READ:  check_data_with_hburst(tr); // compare data
      endcase
    end
  endfunction

	task do_listen_events();
	
	endtask

 	virtual task do_data_check();
	endtask

  function bit is_addr_valid(bit [31:0] addr);
    if(addr >= cfg.addr_start && addr <= cfg.addr_end)
      return 1;
      else
        `uvm_error("ADDRERR","addr is over")
  endfunction
// xsct_type is WRITE
  function void store_data_with_hburst(lvc_ahb_transaction tr);
    case(tr.burst_type) 
      SINGLE:begin
                store_data_with_hsize(tr,0); // judge hsize
              end
      INCR: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      WRAP4: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      INCR4: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      WRAP8: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      INCR8: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      WRAP16: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      INCR16: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      default: begin
                `uvm_error("TYPEERR","burst_type not defined")
            end            
    endcase
  endfunction

// xsct_type is READ
  function bit check_data_with_hburst(lvc_ahb_transaction tr);
    case(tr.burst_type) 
      SINGLE:begin
                check_data_with_hburst = (check_data_with_hsize(tr,0));
              end
      INCR: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      WRAP4: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      INCR4: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      WRAP8: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      INCR8: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      WRAP16: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      INCR16: begin
                `uvm_error("TYPEERR","burst_type not supported yet")
            end
      default: begin
                `uvm_error("TYPEERR","burst_type not defined")
            end            
    endcase
    
    if(check_data_with_hburst)
      `uvm_info("DATA_CHECK",$sformatf("ahbram [%0x] hburst[%s] is as expected",tr.addr,tr.burst_type),UVM_HIGH)
      else
      `uvm_error("DATA_CHECK",$sformatf("ahbram [%0x] hburst[%s] is NOT as expected",tr.addr,tr.burst_type))
  endfunction

  // WRITE store data
  function void store_data_with_hsize(lvc_ahb_transaction tr,int beat);
    case(tr.burst_size)
      // beat is the number of data single:beat is 0
      BURST_SIZE_8BIT    :  mem[{tr.addr[31:2],2'b00}] = extract_current_beat_mem_data(tr,beat); //extract offset
      BURST_SIZE_16BIT   :  mem[{tr.addr[31:2],2'b00}] = extract_current_beat_mem_data(tr,beat);
      BURST_SIZE_32BIT   :  mem[{tr.addr[31:2],2'b00}] = extract_current_beat_mem_data(tr,beat);
      BURST_SIZE_64BIT   :  begin `uvm_error("TYPEERR","burst size is not support") end
      default            :  begin `uvm_error("TYPEERR","burst size is not support") end
    endcase
  //  $display("write size is %0d",tr.burst_size);
  endfunction

  // READ compare data
  function bit check_data_with_hsize(lvc_ahb_transaction tr,int beat);
    bit [31:0] tdata = extract_valid_data(tr.data[beat],tr.addr,tr.burst_size);
    
    bit [31:0] mdata = extract_valid_data(mem[{tr.addr[31:2],2'b00}],tr.addr,tr.burst_size);
    // $display("read data is %0h",tr.data[beat]);

    check_data_with_hsize = ( tdata == mdata ) ? 1:0;
    cfg.scb_check_count++;
    // $display("read size is %0d",tr.burst_size);
    if(check_data_with_hsize)
      `uvm_info("DATACHK", $sformatf("ahbram[%0x] data expected 'h%0x = actual '%0x",tr.addr, mdata,tdata),UVM_HIGH)
    else begin
      cfg.scb_check_error++;
      `uvm_error("DATACHK", $sformatf("ahbram[%0x] data expected 'h%0x != actual '%0x",tr.addr, mdata,tdata))
    end
  endfunction

  // store
  function bit [31:0] extract_current_beat_mem_data(lvc_ahb_transaction tr, int beat);
    bit [31:0] mdata = mem [{tr.addr[31:2],2'b00}];
    bit [31:0] tdata = tr.data[beat]; // dut input
    case(tr.burst_size)
      BURST_SIZE_8BIT: mdata[(tr.addr[1:0]*8 + 7) -: 8] = tdata >> (8*tr.addr[1:0]); // tdata>> after ,data is in 7:0 tr.addr[1:0]*8 calculate data location 00 -> 7:0 01 -> 15:8...
      BURST_SIZE_16BIT: mdata[(tr.addr[1]*16 + 15) -: 16] = tdata >> (16*tr.addr[1]);
      // addr[1] = 0 15:0 1-> 31:16
      BURST_SIZE_32BIT: begin mdata = tdata; 
                              $display("store data is %0h",mdata[tr.addr]);
                        end
      BURST_SIZE_64BIT: begin `uvm_error("TYPERR", "burst size not support") end
      default: begin `uvm_error("TYPERR", "burst size not support") end
    endcase

    return mdata;
  endfunction

  // check
  function bit[31:0] extract_valid_data([`LVC_AHB_MAX_DATA_WIDTH - 1 : 0] data,
                                  [`LVC_AHB_MAX_ADDR_WIDTH-1:0] addr,
                                  burst_size_enum bsize);
    case(bsize)
      BURST_SIZE_8BIT  : return (data >> (8*addr[1:0])) & 8'hFF;
      BURST_SIZE_16BIT : return (data >> (16*addr[1])) & 16'hFFFF;
      BURST_SIZE_32BIT : return  data & 32'hFFFF_FFFF;
      BURST_SIZE_64BIT: begin `uvm_error("TYPERR", "burst size not support") end
      default: begin `uvm_error("TYPERR", "burst size not support") end      
    endcase
  endfunction
endclass
`endif

  • 分为读写两种情况。其实scordboard就是构建了一个reference model,对于本次的dut来说,就是一个读写sram。内部构建了一个二维数组mem。模仿dut的读写操作。dut写数据的同时,此mem相同位置也会存入一个值。读的时候,此mem根据地址也会读出一个数据,再与dut读出的值进行比对。判断dut的功能是否正确。
  • 得到一个transaction首先看地址范围是否满足要求,满足的话根据xact来判断读还是写。
  • 写:
  • 首先去store_data_with_hburst判断type是SINGLE,再进行store_data_with_hsize判断写的SIZE是多少。
  • 如果为8BIT。会先将原来位置的数据读出来,再根据addr的低两位计算此data应该写入4部分的哪一部分。(不能理解的话建议手动写一下算算,就明白了)
  • 为什么写入的数据要是mem[{tr.addr[31:2],2’b00}]呢?这个scoreboard的mem直接就是一个32bit的mem,所以需要每次把整个一行读出来,再看数据去7:0还是15:8等等。而实际dut的话是4个8bit的拼接起来的。所以就是有自己的地址。相当于00对于模型来讲一次选中了4个8bit的一行。而对于dut而言,选中了最低一块8bit的sram。
  • 之前跑完代码会打印error和count是因为在seq中加入了比对函数。scoreboard则是模仿dut行为的一个模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值