`ifndef CALC_DRIVER_SV `define CALC_DRIVER_SV //----------------------------------------------------------------------------- // Calc1 driver. // // Protocol (Vezba 5): // * command + operand1 are driven in the same cycle, // * operand2 is driven in the next cycle (command line back to 0), // * a response appears on out_respX a few (>=3) cycles later. // // Only one request may be outstanding per port, so after issuing a request the // driver waits for that port's response before completing the item. This keeps // the very simple "bidirectional, non-pipelined" use model from Vezba 6. //----------------------------------------------------------------------------- class calc_driver extends uvm_driver#(calc_seq_item); `uvm_component_utils(calc_driver) virtual interface calc_if vif; function new(string name = "calc_driver", uvm_component parent = null); super.new(name,parent); endfunction function void connect_phase(uvm_phase phase); super.connect_phase(phase); if (!uvm_config_db#(virtual calc_if)::get(this, "", "calc_if", vif)) `uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"}) endfunction : connect_phase task main_phase(uvm_phase phase); // start from a known idle state and wait until reset is released reset_inputs(); wait_reset_done(); forever begin seq_item_port.get_next_item(req); `uvm_info(get_type_name(), $sformatf("Driving: %s", req.convert2string()), UVM_HIGH) drive_item(req); seq_item_port.item_done(); end endtask : main_phase // Drive a single transaction following the two-cycle request protocol and // wait for the corresponding response on the same port. task drive_item(calc_seq_item it); int unsigned wait_cnt; // optional idle gap before the request repeat (it.delay) @(posedge vif.clk); // cycle 1: command + operand1 drive_req(it.port, it.cmd, it.op1); @(posedge vif.clk); // cycle 2: operand2, command de-asserted drive_req(it.port, CMD_NOP, it.op2); @(posedge vif.clk); // idle the port again while the pipeline produces the result drive_req(it.port, CMD_NOP, '0); // wait for this port's response (resp != 0), bounded so a non-responding // DUT cannot deadlock the test wait_cnt = 0; do begin @(posedge vif.clk); wait_cnt++; end while (get_resp(it.port) === RESP_NONE && wait_cnt < RSP_TIMEOUT); if (get_resp(it.port) === RESP_NONE) `uvm_warning(get_type_name(), $sformatf("No response on port %0d within %0d cycles (cmd=%s) - the scoreboard will flag this", it.port+1, RSP_TIMEOUT, it.cmd2string())) endtask : drive_item // Drive command/data onto the selected port, leaving the others untouched. task drive_req(bit [1:0] port, bit [CMD_WIDTH-1:0] cmd, bit [DATA_WIDTH-1:0] data); case (port) 2'd0 : begin vif.req1_cmd_in <= cmd; vif.req1_data_in <= data; end 2'd1 : begin vif.req2_cmd_in <= cmd; vif.req2_data_in <= data; end 2'd2 : begin vif.req3_cmd_in <= cmd; vif.req3_data_in <= data; end 2'd3 : begin vif.req4_cmd_in <= cmd; vif.req4_data_in <= data; end endcase endtask : drive_req // Combinational read of a port's response line. function bit [RESP_WIDTH-1:0] get_resp(bit [1:0] port); case (port) 2'd0 : return vif.out_resp1; 2'd1 : return vif.out_resp2; 2'd2 : return vif.out_resp3; 2'd3 : return vif.out_resp4; endcase endfunction // Drive all request lines to their idle (zero) state. task reset_inputs(); vif.req1_cmd_in <= '0; vif.req1_data_in <= '0; vif.req2_cmd_in <= '0; vif.req2_data_in <= '0; vif.req3_cmd_in <= '0; vif.req3_data_in <= '0; vif.req4_cmd_in <= '0; vif.req4_data_in <= '0; endtask : reset_inputs task wait_reset_done(); // reset is active-high (all ones); wait until it is fully released while (vif.rst !== '0) @(posedge vif.clk); `uvm_info(get_type_name(), "Reset released - starting to drive", UVM_LOW) endtask : wait_reset_done endclass : calc_driver `endif