blob: 60cdc86df5ee17d2aada5378c2c725dd01487f5e [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class csrng_monitor extends dv_base_monitor #(
.ITEM_T (csrng_item),
.CFG_T (csrng_agent_cfg),
.COV_T (csrng_agent_cov)
);
`uvm_component_utils(csrng_monitor)
// the base class provides the following handles for use:
// csrng_agent_cfg: cfg
// csrng_agent_cov: cov
// uvm_analysis_port #(csrng_item): analysis_port
// Analysis port for the csrng_rsp_sts.
uvm_analysis_port #(bit) rsp_sts_ap;
uvm_tlm_analysis_fifo#(push_pull_item#(.HostDataWidth(csrng_pkg::CSRNG_CMD_WIDTH)))
csrng_cmd_fifo;
`uvm_component_new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
csrng_cmd_fifo = new("csrng_cmd_fifo", this);
rsp_sts_ap = new("rsp_sts_ap", this);
endfunction
task run_phase(uvm_phase phase);
@(posedge cfg.vif.rst_n);
fork
handle_reset();
collect_valid_trans();
// We only need to monitor incoming requests if the agent is configured
// in device mode.
if (cfg.if_mode == dv_utils_pkg::Device) begin
collect_request();
end
join_none
endtask
virtual protected task handle_reset();
forever begin
@(negedge cfg.vif.rst_n);
cfg.under_reset = 1;
csrng_cmd_fifo.flush();
// TODO: sample any reset-related covergroups
@(posedge cfg.vif.rst_n);
cfg.under_reset = 0;
end
endtask
virtual task collect_valid_trans();
forever begin
wait (cfg.under_reset == 0);
`DV_SPINWAIT_EXIT(
push_pull_item#(.HostDataWidth(csrng_pkg::CSRNG_CMD_WIDTH)) item;
csrng_item cs_item = csrng_item::type_id::create("cs_item");
for (int i = 0; i <= cs_item.clen; i++) begin
csrng_cmd_fifo.get(item);
`uvm_info(`gfn, $sformatf("Received cs_item: %s", item.convert2string()), UVM_HIGH)
if (i == 0) begin
cs_item.acmd = acmd_e'(item.h_data[3:0]);
cs_item.clen = item.h_data[7:4];
if (item.h_data[11:8] == MuBi4True) begin
cs_item.flags = MuBi4True;
end else begin
cs_item.flags = MuBi4False;
end
cs_item.glen = item.h_data[30:12];
cs_item.cmd_data_q.delete();
end else begin
cs_item.cmd_data_q.push_back(item.h_data);
end
end
if (cs_item.acmd == csrng_pkg::GEN) begin
for (int i = 0; i < cs_item.glen; i++) begin
@(posedge cfg.vif.mon_cb.cmd_rsp.genbits_valid);
cs_item.genbits_q.push_back(cfg.vif.mon_cb.cmd_rsp.genbits_bus);
end
end
// Illegal commands fail without getting acknowledged.
if (!(cs_item.acmd inside {csrng_pkg::INV, csrng_pkg::GENB,
csrng_pkg::GENU})) begin
cfg.vif.wait_cmd_ack_or_rst_n();
end
`uvm_info(`gfn, $sformatf("Writing analysis_port: %s", cs_item.convert2string()),
UVM_HIGH)
analysis_port.write(cs_item);
if (cfg.en_cov) cov.sample_csrng_cmds(cs_item, cfg.vif.cmd_rsp.csrng_rsp_sts);
,
// Wait reset
wait (cfg.under_reset);)
end
endtask
// This task is only used for device agents responding
// It will pick up any incoming requests from the DUT and send a signal to the
// sequencer (in the form of a sequence item), which will then be forwarded to
// the sequence, which then generates the appropriate response item.
//
// TODO: This assumes no requests can be dropped, and might need to be fixed
// if this is not allowed.
virtual protected task collect_request();
csrng_item cs_item;
forever begin
wait(cfg.under_reset == 0);
@(cfg.vif.cmd_push_if.mon_cb);
if ((cfg.vif.cmd_push_if.mon_cb.valid) && (cfg.vif.cmd_push_if.mon_cb.ready)) begin
// TODO: sample any covergroups
// TODO: Implement suggestion in PR #5456
cs_item = csrng_item::type_id::create("cs_item");
cs_item.acmd = acmd_e'(cfg.vif.mon_cb.cmd_req.csrng_req_bus[3:0]);
cs_item.clen = cfg.vif.mon_cb.cmd_req.csrng_req_bus[7:4];
if (cfg.vif.mon_cb.cmd_req.csrng_req_bus[11:8] == MuBi4True) begin
cs_item.flags = MuBi4True;
end
else begin
cs_item.flags = MuBi4False;
end
if (cs_item.acmd == csrng_pkg::GEN) begin
cs_item.glen = cfg.vif.mon_cb.cmd_req.csrng_req_bus[30:12];
end
`DV_SPINWAIT_EXIT(
for (int i = 0; i < cs_item.clen; i++) begin
do begin
@(cfg.vif.cmd_push_if.mon_cb);
end
while (!(cfg.vif.cmd_push_if.mon_cb.valid) || !(cfg.vif.cmd_push_if.mon_cb.ready));
cs_item.cmd_data_q.push_back(cfg.vif.mon_cb.cmd_req.csrng_req_bus);
end,
wait(cfg.under_reset))
`uvm_info(`gfn, $sformatf("Writing req_analysis_port: %s", cs_item.convert2string()),
UVM_HIGH)
req_analysis_port.write(cs_item);
// After picking up a request, wait until a response is sent before
// detecting another request, as this is not a pipelined protocol.
`DV_SPINWAIT_EXIT(while (!cfg.vif.mon_cb.cmd_rsp.csrng_rsp_ack) @(cfg.vif.mon_cb);,
wait(cfg.under_reset))
// Increment the counters only if ack is sent.
if (!cfg.under_reset) begin
if (cs_item.acmd == csrng_pkg::RES) cfg.reseed_cnt += 1;
if (cs_item.acmd == csrng_pkg::GEN) begin
cfg.generate_cnt += 1;
if (cfg.reseed_cnt == 1) cfg.generate_between_reseeds_cnt += 1;
end
end
rsp_sts_ap.write(cfg.vif.cmd_rsp.csrng_rsp_sts);
end
end
endtask
endclass