207 lines
6.9 KiB
Systemverilog
207 lines
6.9 KiB
Systemverilog
|
|
`ifndef CALC_MONITOR_SV
|
||
|
|
`define CALC_MONITOR_SV
|
||
|
|
|
||
|
|
//-----------------------------------------------------------------------------
|
||
|
|
// Calc1 monitor.
|
||
|
|
//
|
||
|
|
// Passive component. One collector thread per port reconstructs a transaction
|
||
|
|
// from the pin activity:
|
||
|
|
// * a request starts when reqX_cmd_in != 0 -> capture cmd and operand1,
|
||
|
|
// * operand2 is the data line on the following cycle,
|
||
|
|
// * the matching response is the first cycle in which out_respX != 0.
|
||
|
|
//
|
||
|
|
// The completed transaction (stimulus + observed response/result) is broadcast
|
||
|
|
// over the analysis port to the scoreboard, and functional coverage is sampled.
|
||
|
|
//-----------------------------------------------------------------------------
|
||
|
|
class calc_monitor extends uvm_monitor;
|
||
|
|
|
||
|
|
// control fields
|
||
|
|
bit checks_enable = 1;
|
||
|
|
bit coverage_enable = 1;
|
||
|
|
|
||
|
|
uvm_analysis_port #(calc_seq_item) item_collected_port;
|
||
|
|
|
||
|
|
`uvm_component_utils_begin(calc_monitor)
|
||
|
|
`uvm_field_int(checks_enable, UVM_DEFAULT)
|
||
|
|
`uvm_field_int(coverage_enable, UVM_DEFAULT)
|
||
|
|
`uvm_component_utils_end
|
||
|
|
|
||
|
|
// The virtual interface used to view HDL signals.
|
||
|
|
virtual interface calc_if vif;
|
||
|
|
|
||
|
|
// number of transactions collected (per port and total)
|
||
|
|
int unsigned num_collected;
|
||
|
|
|
||
|
|
//--------------------------------------------------------------------------
|
||
|
|
// Functional coverage model (Vezba 11).
|
||
|
|
//--------------------------------------------------------------------------
|
||
|
|
bit [CMD_WIDTH-1:0] cov_cmd;
|
||
|
|
bit [1:0] cov_port;
|
||
|
|
bit [RESP_WIDTH-1:0] cov_resp;
|
||
|
|
bit [DATA_WIDTH-1:0] cov_op1;
|
||
|
|
bit [DATA_WIDTH-1:0] cov_op2;
|
||
|
|
|
||
|
|
covergroup calc_cg;
|
||
|
|
option.per_instance = 1;
|
||
|
|
option.name = "calc_functional_coverage";
|
||
|
|
|
||
|
|
cp_cmd : coverpoint cov_cmd {
|
||
|
|
bins add = {CMD_ADD};
|
||
|
|
bins sub = {CMD_SUB};
|
||
|
|
bins shl = {CMD_SHL};
|
||
|
|
bins shr = {CMD_SHR};
|
||
|
|
bins invalid = {[4'h3:4'h4], 4'h7, [4'h8:4'hF]};
|
||
|
|
}
|
||
|
|
cp_port : coverpoint cov_port {
|
||
|
|
bins port1 = {2'd0};
|
||
|
|
bins port2 = {2'd1};
|
||
|
|
bins port3 = {2'd2};
|
||
|
|
bins port4 = {2'd3};
|
||
|
|
}
|
||
|
|
// the monitor only emits an item once a real response is seen, so only
|
||
|
|
// SUCCESS and ERROR are ever sampled here
|
||
|
|
cp_resp : coverpoint cov_resp {
|
||
|
|
bins success = {RESP_SUCCESS};
|
||
|
|
bins error = {RESP_ERROR};
|
||
|
|
}
|
||
|
|
// interesting operand corners
|
||
|
|
cp_op1 : coverpoint cov_op1 {
|
||
|
|
bins zero = {32'h0000_0000};
|
||
|
|
bins one = {32'h0000_0001};
|
||
|
|
bins max = {32'hFFFF_FFFF};
|
||
|
|
bins msb = {32'h8000_0000};
|
||
|
|
bins others = default;
|
||
|
|
}
|
||
|
|
cp_op2 : coverpoint cov_op2 {
|
||
|
|
bins zero = {32'h0000_0000};
|
||
|
|
bins one = {32'h0000_0001};
|
||
|
|
bins max = {32'hFFFF_FFFF};
|
||
|
|
bins shamt = {[32'h2:32'h1F]}; // small shift amounts
|
||
|
|
bins others = default;
|
||
|
|
}
|
||
|
|
// every legal command must be exercised on every port
|
||
|
|
cx_cmd_port : cross cp_cmd, cp_port;
|
||
|
|
// every command must be seen producing both success and error responses
|
||
|
|
cx_cmd_resp : cross cp_cmd, cp_resp;
|
||
|
|
endgroup
|
||
|
|
|
||
|
|
function new(string name = "calc_monitor", uvm_component parent = null);
|
||
|
|
super.new(name,parent);
|
||
|
|
item_collected_port = new("item_collected_port", this);
|
||
|
|
calc_cg = new();
|
||
|
|
endfunction
|
||
|
|
|
||
|
|
function void connect_phase(uvm_phase phase);
|
||
|
|
super.connect_phase(phase);
|
||
|
|
if (!uvm_config_db#(virtual calc_if)::get(this, "", "calc_if", vif))
|
||
|
|
`uvm_fatal("NOVIF",{"virtual interface must be set:",get_full_name(),".vif"})
|
||
|
|
endfunction : connect_phase
|
||
|
|
|
||
|
|
task main_phase(uvm_phase phase);
|
||
|
|
wait_reset_done();
|
||
|
|
// launch one independent collector per port
|
||
|
|
for (int p = 0; p < NUM_PORTS; p++) begin
|
||
|
|
automatic int port = p;
|
||
|
|
fork
|
||
|
|
collect_port(port[1:0]);
|
||
|
|
join_none
|
||
|
|
end
|
||
|
|
endtask : main_phase
|
||
|
|
|
||
|
|
// Collect every request/response pair seen on a single port.
|
||
|
|
task collect_port(bit [1:0] port);
|
||
|
|
calc_seq_item it;
|
||
|
|
int unsigned wait_cnt;
|
||
|
|
forever begin
|
||
|
|
// wait for the start of a request on this port
|
||
|
|
do @(posedge vif.clk); while (get_cmd(port) === CMD_NOP || vif.rst !== '0);
|
||
|
|
|
||
|
|
it = calc_seq_item::type_id::create($sformatf("it_p%0d", port));
|
||
|
|
it.port = port;
|
||
|
|
it.cmd = get_cmd(port);
|
||
|
|
it.op1 = get_data(port);
|
||
|
|
|
||
|
|
// operand2 is presented on the next cycle
|
||
|
|
@(posedge vif.clk);
|
||
|
|
it.op2 = get_data(port);
|
||
|
|
|
||
|
|
// wait for the response on this port (bounded - a non-responding DUT
|
||
|
|
// is reported, not waited on forever); resp stays NONE on timeout so
|
||
|
|
// the scoreboard flags the missing response
|
||
|
|
wait_cnt = 0;
|
||
|
|
do begin
|
||
|
|
@(posedge vif.clk);
|
||
|
|
wait_cnt++;
|
||
|
|
end while (get_resp(port) === RESP_NONE && wait_cnt < RSP_TIMEOUT);
|
||
|
|
it.resp = get_resp(port);
|
||
|
|
it.result = get_out_data(port);
|
||
|
|
|
||
|
|
num_collected++;
|
||
|
|
`uvm_info(get_type_name(),
|
||
|
|
$sformatf("Collected: %s", it.convert2string()), UVM_MEDIUM)
|
||
|
|
|
||
|
|
if (coverage_enable) sample_coverage(it);
|
||
|
|
item_collected_port.write(it);
|
||
|
|
end
|
||
|
|
endtask : collect_port
|
||
|
|
|
||
|
|
function void sample_coverage(calc_seq_item it);
|
||
|
|
cov_cmd = it.cmd;
|
||
|
|
cov_port = it.port;
|
||
|
|
cov_resp = it.resp;
|
||
|
|
cov_op1 = it.op1;
|
||
|
|
cov_op2 = it.op2;
|
||
|
|
calc_cg.sample();
|
||
|
|
endfunction
|
||
|
|
|
||
|
|
//--- per-port signal accessors -------------------------------------------
|
||
|
|
function bit [CMD_WIDTH-1:0] get_cmd(bit [1:0] port);
|
||
|
|
case (port)
|
||
|
|
2'd0 : return vif.req1_cmd_in;
|
||
|
|
2'd1 : return vif.req2_cmd_in;
|
||
|
|
2'd2 : return vif.req3_cmd_in;
|
||
|
|
2'd3 : return vif.req4_cmd_in;
|
||
|
|
endcase
|
||
|
|
endfunction
|
||
|
|
|
||
|
|
function bit [DATA_WIDTH-1:0] get_data(bit [1:0] port);
|
||
|
|
case (port)
|
||
|
|
2'd0 : return vif.req1_data_in;
|
||
|
|
2'd1 : return vif.req2_data_in;
|
||
|
|
2'd2 : return vif.req3_data_in;
|
||
|
|
2'd3 : return vif.req4_data_in;
|
||
|
|
endcase
|
||
|
|
endfunction
|
||
|
|
|
||
|
|
function bit [RESP_WIDTH-1:0] get_resp(bit [1:0] port);
|
||
|
|
case (port)
|
||
|
|
2'd0 : return vif.out_resp1;
|
||
|
|
2'd1 : return vif.out_resp2;
|
||
|
|
2'd2 : return vif.out_resp3;
|
||
|
|
2'd3 : return vif.out_resp4;
|
||
|
|
endcase
|
||
|
|
endfunction
|
||
|
|
|
||
|
|
function bit [DATA_WIDTH-1:0] get_out_data(bit [1:0] port);
|
||
|
|
case (port)
|
||
|
|
2'd0 : return vif.out_data1;
|
||
|
|
2'd1 : return vif.out_data2;
|
||
|
|
2'd2 : return vif.out_data3;
|
||
|
|
2'd3 : return vif.out_data4;
|
||
|
|
endcase
|
||
|
|
endfunction
|
||
|
|
|
||
|
|
task wait_reset_done();
|
||
|
|
while (vif.rst !== '0) @(posedge vif.clk);
|
||
|
|
endtask : wait_reset_done
|
||
|
|
|
||
|
|
function void report_phase(uvm_phase phase);
|
||
|
|
`uvm_info(get_type_name(),
|
||
|
|
$sformatf("Monitor collected %0d transactions, functional coverage = %0.2f%%",
|
||
|
|
num_collected, calc_cg.get_coverage()), UVM_LOW)
|
||
|
|
endfunction : report_phase
|
||
|
|
|
||
|
|
endclass : calc_monitor
|
||
|
|
|
||
|
|
`endif
|