blob: 92dca5306fecdb39f0f5beb479694cdb8eaa4765 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
typedef class push_pull_sub_driver;
typedef class push_host_driver;
typedef class pull_host_driver;
typedef class push_device_driver;
typedef class pull_device_driver;
// 'Main' driver class that is created by the agent in active mode.
//
// The actual driving of signals is done by the 'sub' driver class which is virtualized. The correct
// flavor of the sub driver is picked up base on whether it is a push / pull and whether its host or
// device.
class push_pull_driver #(
parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth
) extends dv_base_driver #(
.ITEM_T(push_pull_item #(HostDataWidth, DeviceDataWidth)),
.CFG_T (push_pull_agent_cfg #(HostDataWidth, DeviceDataWidth))
);
push_pull_sub_driver #(HostDataWidth, DeviceDataWidth) sub_driver;
`uvm_component_param_utils(push_pull_driver#(HostDataWidth, DeviceDataWidth))
`uvm_component_new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
case ({cfg.if_mode, cfg.agent_type})
{dv_utils_pkg::Host, PushAgent}: begin
sub_driver = push_host_driver#(HostDataWidth, DeviceDataWidth)::type_id::create(
"sub_driver");
end
{dv_utils_pkg::Host, PullAgent}: begin
sub_driver = pull_host_driver#(HostDataWidth, DeviceDataWidth)::type_id::create(
"sub_driver");
end
{dv_utils_pkg::Device, PushAgent}: begin
sub_driver = push_device_driver#(HostDataWidth, DeviceDataWidth)::type_id::create(
"sub_driver");
end
{dv_utils_pkg::Device, PullAgent}: begin
sub_driver = pull_device_driver#(HostDataWidth, DeviceDataWidth)::type_id::create(
"sub_driver");
end
default:;
endcase
sub_driver.cfg = cfg;
endfunction
virtual task reset_signals();
sub_driver.reset_signals();
forever begin
@(negedge cfg.vif.rst_n);
`uvm_info(`gfn, "Driver is under reset", UVM_HIGH)
sub_driver.reset_signals();
@(posedge cfg.vif.rst_n);
`uvm_info(`gfn, "Driver is out of reset", UVM_HIGH)
end
endtask
// Drive trans received from sequencer.
virtual task get_and_drive();
forever begin
seq_item_port.try_next_item(req);
if (req == null) begin
sub_driver.wait_cb();
continue;
end
`uvm_info(`gfn, $sformatf("Driver received item:\n%0s", req.convert2string()), UVM_HIGH)
sub_driver.drive_item(req);
seq_item_port.item_done(req);
end
endtask
endclass
// Virtual 'sub' driver class.
//
// Extended to provide different push / pull / host / device mode flavors.
virtual class push_pull_sub_driver #(
parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth
) extends uvm_object;
// The handle to the agent cfg object set by the main driver.
push_pull_agent_cfg #(HostDataWidth, DeviceDataWidth) cfg;
`uvm_object_new
// Reset the interface signals driven to the DUT.
pure virtual function void reset_signals();
// Wait for clock edges using the appropriate clocking block.
pure virtual task wait_cb(int num = 1);
// Apply the item to the interface.
pure virtual task drive_item(push_pull_item#(HostDataWidth, DeviceDataWidth) req);
endclass
// Push driver in host mode.
class push_host_driver #(
parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth
) extends push_pull_sub_driver #(HostDataWidth, DeviceDataWidth);
`uvm_object_param_utils(push_host_driver#(HostDataWidth, DeviceDataWidth))
`uvm_object_new
`define CB cfg.vif.host_push_cb
virtual function void reset_signals();
cfg.vif.valid_int <= '0;
cfg.vif.h_data_int <= 'x;
endfunction
virtual task wait_cb(int num = 1);
repeat (num) @(`CB);
endtask
// Drives host side of ready/valid protocol
virtual task drive_item(push_pull_item#(HostDataWidth, DeviceDataWidth) req);
`DV_SPINWAIT_EXIT(
repeat (req.host_delay) @(`CB);
`CB.valid_int <= 1'b1;
`CB.h_data_int <= req.h_data;
do @(`CB); while (!cfg.ignore_push_host_backpressure && !`CB.ready);
`CB.valid_int <= 1'b0;
if (!cfg.hold_h_data_until_next_req) `CB.h_data_int <= 'x;,
wait (cfg.in_reset);)
// In case there is race condition between the logic above and reset_signals task.
// We always set the valid_int again to 0 to make sure the data comes out of reset is not
// valid.
if (cfg.in_reset) `CB.valid_int <= '0;
endtask
`undef CB
endclass
// Pull driver in host mode.
class pull_host_driver #(
parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth
) extends push_pull_sub_driver #(HostDataWidth, DeviceDataWidth);
`uvm_object_param_utils(pull_host_driver#(HostDataWidth, DeviceDataWidth))
`uvm_object_new
`define CB cfg.vif.host_pull_cb
virtual function void reset_signals();
cfg.vif.req_int <= '0;
cfg.vif.h_data_int <= 'x;
endfunction
virtual task wait_cb(int num = 1);
repeat (num) @(`CB);
endtask
// Drives host side of req/ack protocol
virtual task drive_item(push_pull_item#(HostDataWidth, DeviceDataWidth) req);
`DV_SPINWAIT_EXIT(
repeat (req.host_delay) @(`CB);
`CB.req_int <= 1'b1;
`CB.h_data_int <= req.h_data;
do @(`CB); while (!`CB.ack);
if (cfg.pull_handshake_type == FourPhase) begin
repeat (req.req_lo_delay) @(`CB);
`CB.req_int <= 1'b0;
do @(`CB); while (`CB.ack);
end else begin
`CB.req_int <= 1'b0;
end
if (!cfg.hold_h_data_until_next_req) `CB.h_data_int <= 'x;,
wait (cfg.in_reset);)
endtask
`undef CB
endclass
// Push driver in device mode.
class push_device_driver #(
parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth
) extends push_pull_sub_driver #(HostDataWidth, DeviceDataWidth);
`uvm_object_param_utils(push_device_driver#(HostDataWidth, DeviceDataWidth))
`uvm_object_new
`define CB cfg.vif.device_push_cb
virtual function void reset_signals();
cfg.vif.ready_int <= '0;
cfg.vif.d_data_int <= 'x;
endfunction
virtual task wait_cb(int num = 1);
repeat (num) @(`CB);
endtask
// Drives device side of ready/valid protocol
virtual task drive_item(push_pull_item#(HostDataWidth, DeviceDataWidth) req);
`DV_SPINWAIT_EXIT(
// TODO: this may be needed in future: while (!`CB.valid) @(`CB);
repeat (req.device_delay) @(`CB);
`CB.ready_int <= 1'b1;
`CB.d_data_int <= req.d_data;
@(`CB);
`CB.ready_int <= 1'b0;
if (!cfg.hold_d_data_until_next_req) `CB.d_data_int <= 'x;,
wait (cfg.in_reset);)
endtask
`undef CB
endclass
// Pull driver in device mode.
class pull_device_driver #(
parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth
) extends push_pull_sub_driver #(HostDataWidth, DeviceDataWidth);
`uvm_object_param_utils(pull_device_driver#(HostDataWidth, DeviceDataWidth))
`uvm_object_new
`define CB cfg.vif.device_pull_cb
virtual function void reset_signals();
cfg.vif.ack_int <= '0;
cfg.vif.d_data_int <= 'x;
endfunction
virtual task wait_cb(int num = 1);
repeat (num) @(`CB);
endtask
// Drives device side of req/ack protocol
virtual task drive_item(push_pull_item#(HostDataWidth, DeviceDataWidth) req);
`DV_SPINWAIT_EXIT(
while (!`CB.req) @(`CB);
repeat (req.device_delay) @(`CB);
`CB.ack_int <= 1'b1;
`CB.d_data_int <= req.d_data;
if (cfg.pull_handshake_type == FourPhase) begin
do @(`CB); while (`CB.req);
repeat (req.ack_lo_delay) @(`CB);
end else begin
@(`CB);
end
`CB.ack_int <= 1'b0;
if (!cfg.hold_d_data_until_next_req) `CB.d_data_int <= 'x;,
wait (cfg.in_reset);)
endtask
`undef CB
endclass