blob: 6fb68e6ae5ccd83d9097cfe88071dfc4bec39b68 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// ---------------------------------------------
// TileLink interface monitor
// ---------------------------------------------
// TODO(taliu): Implement protocl check in the monitor
class tl_monitor extends uvm_monitor;
virtual tl_if vif;
tl_agent_cfg cfg;
tl_seq_item pending_a_req[$];
string agent_name;
bit objection_raised;
uvm_phase run_phase_h;
uvm_analysis_port #(tl_seq_item) d_chan_port;
uvm_analysis_port #(tl_seq_item) a_chan_port;
`uvm_component_utils(tl_monitor)
function new (string name, uvm_component parent);
super.new(name, parent);
agent_name = parent.get_name();
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
d_chan_port = new("d_chan_port", this);
a_chan_port = new("a_chan_port", this);
if(!uvm_config_db#(virtual tl_if)::get(this,"","vif",vif)) begin
`uvm_fatal("NO_VIF", {"virtual interface must be set for:",
get_full_name(),".vif"});
end
if (!uvm_config_db#(tl_agent_cfg)::get(this, "", "cfg", cfg)) begin
`uvm_fatal("NO_CFG", {"cfg must be set for:", get_full_name(),".cfg"});
end
endfunction : build_phase
virtual task run_phase(uvm_phase phase);
run_phase_h = phase;
wait_for_reset_done();
fork
a_channel_thread();
d_channel_thread();
join_none
endtask : run_phase
virtual task wait_for_reset_done();
@(posedge vif.rst_n);
endtask : wait_for_reset_done
virtual task a_channel_thread();
tl_seq_item req;
forever begin
if (vif.mon_cb.h2d.a_valid && vif.mon_cb.d2h.a_ready) begin
req = tl_seq_item::type_id::create("req");
req.a_addr = vif.mon_cb.h2d.a_address;
req.a_opcode = tl_a_op_e'(vif.mon_cb.h2d.a_opcode);
req.a_size = vif.mon_cb.h2d.a_size;
req.a_param = vif.mon_cb.h2d.a_param;
req.a_data = vif.mon_cb.h2d.a_data;
req.a_mask = vif.mon_cb.h2d.a_mask;
req.a_source = vif.mon_cb.h2d.a_source;
`uvm_info("tl_logging", $sformatf("[%0s][a_chan] : %0s",
agent_name, req.convert2string()), UVM_HIGH)
a_chan_port.write(req);
pending_a_req.push_back(req);
if ((cfg.max_outstanding_req > 0) &&
(pending_a_req.size() > cfg.max_outstanding_req)) begin
`uvm_error(get_full_name(), $sformatf("Number of pending a_req exceeds limit %0d",
pending_a_req.size()))
end
if (!objection_raised) begin
run_phase_h.raise_objection(this);
objection_raised = 1'b1;
end
end
@(vif.mon_cb);
end
endtask : a_channel_thread
// Collect ack from D channel
virtual task d_channel_thread();
tl_seq_item rsp;
forever begin
@(vif.mon_cb);
if (vif.mon_cb.d2h.d_valid && vif.mon_cb.h2d.d_ready) begin
// Use the source ID to find the matching request
bit req_found;
foreach (pending_a_req[i]) begin
if (pending_a_req[i].a_source == vif.mon_cb.d2h.d_source) begin
rsp = pending_a_req[i];
rsp.d_opcode = tl_d_op_e'(vif.mon_cb.d2h.d_opcode);
rsp.d_data = vif.mon_cb.d2h.d_data;
rsp.d_source = vif.mon_cb.d2h.d_source;
rsp.d_param = vif.mon_cb.d2h.d_param;
rsp.d_error = vif.mon_cb.d2h.d_error;
rsp.d_sink = vif.mon_cb.d2h.d_sink;
rsp.d_size = vif.mon_cb.d2h.d_size;
rsp.d_user = vif.mon_cb.d2h.d_user;
`uvm_info("tl_logging", $sformatf("[%0s][d_chan] : %0s",
agent_name, rsp.convert2string()), UVM_HIGH)
d_chan_port.write(rsp);
pending_a_req.delete(i);
if (pending_a_req.size() == 0) begin
run_phase_h.drop_objection(this);
objection_raised = 1'b0;
end
req_found = 1'b1;
break;
end
end
if (!req_found) begin
`uvm_error(get_full_name(), $sformatf(
"Cannot find request matching d_source 0x%0x", vif.mon_cb.d2h.d_source))
end
end
end
endtask : d_channel_thread
virtual function void report_phase(uvm_phase phase);
if (pending_a_req.size() > 0) begin
`uvm_error(get_full_name(), $sformatf(
"%0d items left at the end of sim", pending_a_req.size()))
foreach (pending_a_req[i]) begin
`uvm_info(get_full_name(), $sformatf("pending_a_req[%0d] = %0s",
i, pending_a_req[i].convert2string()), UVM_LOW)
end
end
endfunction : report_phase
endclass : tl_monitor