blob: 7e27ccc87ac8513e5a96c9ac701bf3391e9043f3 [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 device driver
// ---------------------------------------------
class tl_device_driver extends tl_base_driver;
`uvm_component_utils(tl_device_driver)
`uvm_component_new
virtual task get_and_drive();
// Wait for initial reset to pass.
wait(cfg.vif.rst_n === 1'b0);
wait(cfg.vif.rst_n === 1'b1);
fork
a_channel_thread();
d_channel_thread();
join_none
endtask
// reset signals every time reset occurs.
virtual task reset_signals();
forever begin
@(negedge cfg.vif.rst_n);
invalidate_d_channel();
cfg.vif.d2h_int.a_ready <= 1'b0;
// Check for seq_item_port FIFO is empty when coming out of reset
`DV_CHECK_EQ(seq_item_port.has_do_available(), 0);
@(posedge cfg.vif.rst_n);
end
endtask
virtual task a_channel_thread();
int unsigned ready_delay;
forever begin
ready_delay = $urandom_range(cfg.a_ready_delay_min, cfg.a_ready_delay_max);
repeat(ready_delay) @(cfg.vif.device_cb);
cfg.vif.device_cb.d2h_int.a_ready <= 1'b1;
@(cfg.vif.device_cb);
cfg.vif.device_cb.d2h_int.a_ready <= 1'b0;
end
endtask
virtual task d_channel_thread();
bit req_found;
tl_seq_item rsp;
forever begin
int unsigned d_valid_delay, d_valid_len, d_valid_cnt;
bit rsp_done, rsp_abort;
seq_item_port.get_next_item(rsp);
while (!rsp_done && !rsp_abort && cfg.vif.rst_n) begin
if (cfg.use_seq_item_d_valid_delay) begin
d_valid_delay = rsp.d_valid_delay;
end else begin
d_valid_delay = $urandom_range(cfg.d_valid_delay_min, cfg.d_valid_delay_max);
end
if (cfg.allow_d_valid_drop_wo_d_ready || rsp.rsp_abort_after_d_valid_len) begin
if (cfg.use_seq_item_d_valid_len) begin
d_valid_len = rsp.d_valid_len;
end else begin
d_valid_len = $urandom_range(cfg.d_valid_len_min, cfg.d_valid_len_max);
end
end
// break delay loop if reset asserted to release blocking
repeat (d_valid_delay) begin
if (!cfg.vif.rst_n) break;
else @(cfg.vif.device_cb);
end
if (cfg.vif.rst_n) begin
cfg.vif.d2h_int.d_valid <= 1'b1;
cfg.vif.d2h_int.d_opcode <= tl_d_op_e'(rsp.d_opcode);
cfg.vif.d2h_int.d_data <= rsp.d_data;
cfg.vif.d2h_int.d_source <= rsp.d_source;
cfg.vif.d2h_int.d_param <= rsp.d_param;
cfg.vif.d2h_int.d_error <= rsp.d_error;
cfg.vif.d2h_int.d_sink <= rsp.d_sink;
cfg.vif.d2h_int.d_user <= rsp.d_user;
cfg.vif.d2h_int.d_size <= rsp.d_size;
end
// wait for ready or reaching d_valid_len
d_valid_cnt = 0;
while (cfg.vif.rst_n) begin
@(cfg.vif.device_cb);
d_valid_cnt++;
if (cfg.vif.device_cb.h2d.d_ready) begin
rsp_done = 1;
break;
end else if ((cfg.allow_d_valid_drop_wo_d_ready || rsp.rsp_abort_after_d_valid_len)
&& d_valid_cnt >= d_valid_len) begin
if (rsp.rsp_abort_after_d_valid_len) rsp_abort = 1;
invalidate_d_channel();
if (!cfg.vif.rst_n) @(cfg.vif.device_cb);
break;
end
end // while (!cfg.vif.rst_n)
end // while (!rsp_done && cfg.vif.rst_n)
invalidate_d_channel();
rsp.rsp_completed = !rsp_abort;
seq_item_port.item_done();
seq_item_port.put_response(rsp);
end
endtask : d_channel_thread
function void invalidate_d_channel();
cfg.vif.d2h_int.d_opcode <= tlul_pkg::tl_d_op_e'('x);
cfg.vif.d2h_int.d_param <= '{default:'x};
cfg.vif.d2h_int.d_size <= '{default:'x};
cfg.vif.d2h_int.d_source <= '{default:'x};
cfg.vif.d2h_int.d_sink <= '{default:'x};
cfg.vif.d2h_int.d_data <= '{default:'x};
cfg.vif.d2h_int.d_user <= '{default:'x};
cfg.vif.d2h_int.d_error <= 1'bx;
cfg.vif.d2h_int.d_valid <= 1'b0;
endfunction : invalidate_d_channel
endclass