116 lines
4.2 KiB
Systemverilog
116 lines
4.2 KiB
Systemverilog
|
|
`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
|