blob: 9348adac3872c9da841333da95743c30ae86b283 [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 host driver
// ---------------------------------------------
class tl_host_driver extends tl_base_driver;
tl_seq_item pending_a_req[$];
bit reset_asserted;
`uvm_component_utils(tl_host_driver)
`uvm_component_new
virtual task get_and_drive();
fork
begin : process_seq_item
forever begin
seq_item_port.try_next_item(req);
if (req != null) begin
send_a_channel_request(req);
end else begin
@(cfg.vif.host_cb);
end
end
end
d_channel_thread();
reset_thread();
join_none
endtask
virtual task reset_thread();
forever begin
@(negedge cfg.vif.rst_n);
reset_asserted = 1'b1;
@(posedge cfg.vif.rst_n == 1);
reset_asserted = 1'b0;
// Check for seq_item_port FIFO & pending req queue is empty when coming out of reset
`DV_CHECK_EQ(pending_a_req.size(), 0)
`DV_CHECK_EQ(seq_item_port.has_do_available(), 0);
end
endtask : reset_thread
virtual task reset_signals();
invalidate_a_channel();
cfg.vif.host_cb.h2d.d_ready <= 1'b0;
@(posedge cfg.vif.host_cb.rst_n);
// wait a clk to make sure a_channel a_valid stay high for a clk cycle if a_valid_delay = 0
@(cfg.vif.host_cb);
endtask
// Send request on A channel
virtual task send_a_channel_request(tl_seq_item req);
int unsigned a_valid_delay;
if (cfg.use_seq_item_a_valid_delay) begin
a_valid_delay = req.a_valid_delay;
end else begin
a_valid_delay = $urandom_range(cfg.a_valid_delay_min, cfg.a_valid_delay_max);
end
// break delay loop if reset asserted to release blocking
repeat (a_valid_delay) begin
if (reset_asserted) break;
else @(cfg.vif.host_cb);
end
// wait until no outstanding transaction with same source id
while (is_source_in_pending_req(req.a_source) & !reset_asserted) @(cfg.vif.host_cb);
cfg.vif.host_cb.h2d.a_address <= req.a_addr;
cfg.vif.host_cb.h2d.a_opcode <= tl_a_op_e'(req.a_opcode);
cfg.vif.host_cb.h2d.a_size <= req.a_size;
cfg.vif.host_cb.h2d.a_param <= req.a_param;
cfg.vif.host_cb.h2d.a_data <= req.a_data;
cfg.vif.host_cb.h2d.a_mask <= req.a_mask;
cfg.vif.host_cb.h2d.a_user <= '0;
cfg.vif.host_cb.h2d.a_source <= req.a_source;
cfg.vif.host_cb.h2d.a_valid <= 1'b1;
// bypass delay in case of reset
if (!reset_asserted) @(cfg.vif.host_cb);
while(!cfg.vif.host_cb.d2h.a_ready && !reset_asserted) @(cfg.vif.host_cb);
invalidate_a_channel();
seq_item_port.item_done();
if (reset_asserted) seq_item_port.put_response(req); // if reset, skip data phase
else pending_a_req.push_back(req);
`uvm_info(get_full_name(), $sformatf("Req sent: %0s", req.convert2string()), UVM_HIGH)
endtask : send_a_channel_request
// Collect ack from D channel
virtual task d_channel_thread();
int unsigned d_ready_delay;
tl_seq_item rsp;
forever begin
bit req_found;
d_ready_delay = $urandom_range(cfg.d_ready_delay_min, cfg.d_ready_delay_max);
// break delay loop if reset asserted to release blocking
repeat (d_ready_delay) begin
if (reset_asserted & (pending_a_req.size() != 0)) break;
else @(cfg.vif.host_cb);
end
cfg.vif.host_cb.h2d.d_ready <= 1'b1;
if (!(reset_asserted & (pending_a_req.size() != 0))) @(cfg.vif.host_cb);
if (cfg.vif.host_cb.d2h.d_valid | ((pending_a_req.size() != 0) & reset_asserted)) begin
// Use the source ID to find the matching request
foreach (pending_a_req[i]) begin
if ((pending_a_req[i].a_source == cfg.vif.host_cb.d2h.d_source) | reset_asserted) begin
rsp = pending_a_req[i];
rsp.d_opcode = cfg.vif.host_cb.d2h.d_opcode;
rsp.d_data = cfg.vif.host_cb.d2h.d_data;
rsp.d_param = cfg.vif.host_cb.d2h.d_param;
rsp.d_error = cfg.vif.host_cb.d2h.d_error;
rsp.d_sink = cfg.vif.host_cb.d2h.d_sink;
rsp.d_size = cfg.vif.host_cb.d2h.d_size;
rsp.d_user = cfg.vif.host_cb.d2h.d_user;
// make sure every req has a rsp with same source even during reset
if (reset_asserted) rsp.d_source = rsp.a_source;
else rsp.d_source = cfg.vif.host_cb.d2h.d_source;
req_found = 1'b1;
seq_item_port.put_response(rsp);
pending_a_req.delete(i);
`uvm_info(get_full_name(), $sformatf("Got response %0s, pending req:%0d",
rsp.convert2string(), pending_a_req.size()), UVM_HIGH)
break;
end
end
if (!req_found) begin
`uvm_error(get_full_name(), $sformatf(
"Cannot find request matching d_source 0x%0x", cfg.vif.host_cb.d2h.d_source))
end
end
cfg.vif.host_cb.h2d.d_ready <= 1'b0;
end
endtask : d_channel_thread
function bit is_source_in_pending_req(bit [SourceWidth-1 : 0] source);
foreach (pending_a_req[i]) begin
if (pending_a_req[i].a_source == source) return 1;
end
return 0;
endfunction
function void invalidate_a_channel();
cfg.vif.host_cb.h2d.a_opcode <= tlul_pkg::tl_a_op_e'('x);
cfg.vif.host_cb.h2d.a_param <= '{default:'x};
cfg.vif.host_cb.h2d.a_size <= '{default:'x};
cfg.vif.host_cb.h2d.a_source <= '{default:'x};
cfg.vif.host_cb.h2d.a_address <= '{default:'x};
cfg.vif.host_cb.h2d.a_mask <= '{default:'x};
cfg.vif.host_cb.h2d.a_data <= '{default:'x};
cfg.vif.host_cb.h2d.a_user <= '{default:'x};
cfg.vif.host_cb.h2d.a_valid <= 1'b0;
endfunction : invalidate_a_channel
endclass : tl_host_driver