Files
fvh_vezbe/code/vezba10/verif/Agent/calc_driver.sv
2026-06-12 07:53:32 +02:00

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