完善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行为的一个模型。