| // 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 |