Update code from upstream repository
https://github.com/lowRISC/opentitan to revision
7c4f8b3fde4bb625ac3330ff52d3f66507190fe5

Please note that we're adding push_pull_agent for the first time in this
commit.

Signed-off-by: Prajwala Puttappa <prajwalaputtappa@lowrisc.org>
This commit is contained in:
Prajwala Puttappa 2022-03-17 14:17:30 +00:00 committed by prajwalaputtappa
parent be5fffa656
commit c900ef1476
18 changed files with 1257 additions and 0 deletions

View file

@ -0,0 +1,13 @@
# PUSH_PULL UVM Agent
PUSH_PULL UVM Agent is extended from DV library agent classes.
## Description
This agent implements both Push (ready/valid) and Pull (req/ack) interface
protocols, and can be configured to behave in both host and device modes.
The agent configuration object (`push_pull_agent_cfg`) contains an enum `agent_type`
that is used to select either push or pull modes.
To configure the agent to use the ready/valid protocol, set `agent_type = PushAgent`, and
to configure the agent to use the req/ack protocol, set `agent_type = PullAgent`.

View file

@ -0,0 +1,32 @@
CAPI=2:
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:dv:push_pull_agent:0.1"
description: "PUSH_PULL DV UVM agent"
filesets:
files_dv:
depend:
- lowrisc:dv:dv_utils
- lowrisc:dv:dv_lib
files:
- push_pull_if.sv
- push_pull_agent_pkg.sv
- push_pull_item.sv: {is_include_file: true}
- push_pull_agent_cfg.sv: {is_include_file: true}
- push_pull_agent_cov.sv: {is_include_file: true}
- push_pull_driver_lib.sv: {is_include_file: true}
- push_pull_monitor.sv: {is_include_file: true}
- push_pull_sequencer.sv: {is_include_file: true}
- push_pull_agent.sv: {is_include_file: true}
- seq_lib/push_pull_base_seq.sv: {is_include_file: true}
- seq_lib/push_pull_host_seq.sv: {is_include_file: true}
- seq_lib/push_pull_indefinite_host_seq.sv: {is_include_file: true}
- seq_lib/push_pull_device_seq.sv: {is_include_file: true}
- seq_lib/push_pull_seq_list.sv: {is_include_file: true}
file_type: systemVerilogSource
targets:
default:
filesets:
- files_dv

View file

@ -0,0 +1,54 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class push_pull_agent #(
parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth
) extends dv_base_agent #(
.CFG_T (push_pull_agent_cfg#(HostDataWidth, DeviceDataWidth)),
.DRIVER_T (push_pull_driver#(HostDataWidth, DeviceDataWidth)),
.SEQUENCER_T(push_pull_sequencer#(HostDataWidth, DeviceDataWidth)),
.MONITOR_T (push_pull_monitor#(HostDataWidth, DeviceDataWidth)),
.COV_T (push_pull_agent_cov#(HostDataWidth, DeviceDataWidth))
);
`uvm_component_param_utils(push_pull_agent#(HostDataWidth, DeviceDataWidth))
`uvm_component_new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// use for reactive device
cfg.has_req_fifo = 1;
// get push_pull_if handle
if (!uvm_config_db#(virtual push_pull_if#(HostDataWidth, DeviceDataWidth))::get(
this, "", "vif", cfg.vif)) begin
`uvm_fatal(`gfn, "failed to get push_pull_if handle from uvm_config_db")
end
cfg.vif.if_mode = cfg.if_mode;
cfg.vif.is_push_agent = (cfg.agent_type == PushAgent);
cfg.vif.in_bidirectional_mode = cfg.in_bidirectional_mode;
cfg.vif.is_pull_handshake_4_phase = (cfg.pull_handshake_type == FourPhase);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if (cfg.if_mode == dv_utils_pkg::Device) begin
monitor.req_analysis_port.connect(sequencer.req_analysis_fifo.analysis_export);
end
endfunction
virtual task run_phase(uvm_phase phase);
push_pull_device_seq#(HostDataWidth, DeviceDataWidth) m_seq =
push_pull_device_seq#(HostDataWidth, DeviceDataWidth)::type_id::create("m_seq", this);
if (cfg.if_mode == dv_utils_pkg::Device && cfg.start_default_device_seq) begin
uvm_config_db#(uvm_object_wrapper)::set(null, {sequencer.get_full_name(), ".run_phase"},
"default_sequence", m_seq.get_type());
sequencer.start_phase_sequence(phase);
end
endtask
endclass

View file

@ -0,0 +1,207 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class push_pull_agent_cfg #(parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth)
extends dv_base_agent_cfg;
// interface handle used by driver, monitor & the sequencer, via cfg handle
virtual push_pull_if#(HostDataWidth, DeviceDataWidth) vif;
// Determines whether this agent is configured as push or pull.
// Should be set from the IP level environment.
push_pull_agent_e agent_type;
// Indicates the type of req-ack handshake.
pull_handshake_e pull_handshake_type;
// Configures the agent to act in bidirectional mode,
// transferring data on both sides of the handshake.
bit in_bidirectional_mode;
// A knob to keep the data until next req, rather than driving unknown after handshake
// completes. See #4465 for the detailed discussion
bit hold_h_data_until_next_req;
bit hold_d_data_until_next_req;
// Host-side delay range for both Push/Pull protocols.
rand int unsigned host_delay_min;
rand int unsigned host_delay_max;
// Device-side delay range for both Push/Pull protocols.
rand int unsigned device_delay_min;
rand int unsigned device_delay_max;
// 4-phase pull protocol delay ranges to de-assert req & ack.
rand int unsigned req_lo_delay_min;
rand int unsigned req_lo_delay_max;
rand int unsigned ack_lo_delay_min;
rand int unsigned ack_lo_delay_max;
// Enables/disable all protocol delays.
rand bit zero_delays;
// Enable starting the device sequence by default if configured in Device mode.
bit start_default_device_seq = 1;
// These data queues allows users to specify data to be driven by the sequence at a higher level.
//
// To specify some Host data to be sent, `set_h_user_data()` should be called from a higher layer
// to push the specified user data into the `h_user_data_q` queue.
// The Host sequence will then check if the queue has any data in it, if so it will pop off the
// first entry and drive that data value.
//
// Similarly, `set_d_user_data()` should be called from a higher layer to push some specified
// user data into the `d_user_data_q` queue, which will then be popped off and driven by the
// Device sequence.
local bit [HostDataWidth-1:0] h_user_data_q[$];
local bit [DeviceDataWidth-1:0] d_user_data_q[$];
constraint host_delay_min_c {
soft host_delay_min == 0;
}
constraint host_delay_max_c {
solve zero_delays before host_delay_max;
if (zero_delays) {
host_delay_max == 0;
} else {
host_delay_max dist {
[1:10] :/ 1,
[11:50] :/ 4,
[51:100] :/ 3,
[101:500] :/ 2,
[501:1000] :/ 1
};
}
}
constraint device_delay_min_c {
soft device_delay_min == 0;
}
constraint device_delay_max_c {
solve zero_delays before device_delay_max;
if (zero_delays) {
device_delay_max == 0;
} else {
device_delay_max dist {
[1:10] :/ 1,
[11:50] :/ 4,
[51:100] :/ 3,
[101:500] :/ 2,
[501:1000] :/ 1
};
}
}
constraint req_lo_delay_min_c {
soft req_lo_delay_min == 0;
}
constraint req_lo_delay_max_c {
solve zero_delays before req_lo_delay_max;
if (zero_delays) {
req_lo_delay_max == 0;
} else {
req_lo_delay_max dist {
[1:10] :/ 1,
[11:50] :/ 4,
[51:100] :/ 3
};
}
}
constraint ack_lo_delay_min_c {
soft ack_lo_delay_min == 0;
}
constraint ack_lo_delay_max_c {
solve zero_delays before ack_lo_delay_max;
if (zero_delays) {
ack_lo_delay_max == 0;
} else {
ack_lo_delay_max dist {
[1:10] :/ 1,
[11:50] :/ 4,
[51:100] :/ 3
};
}
}
// Bias randomization in favor of enabling zero delays less often.
constraint zero_delays_c {
zero_delays dist { 0 := 7,
1 := 3 };
}
// Setter method for the user data queues - must be called externally to place specific user-data
// to be sent by the driver.
function void add_h_user_data(bit [HostDataWidth-1:0] data);
h_user_data_q.push_back(data);
endfunction
// Setter method for the user data queues - must be called externally to place specific user-data
// to be sent by the driver.
function void add_d_user_data(bit [DeviceDataWidth-1:0] data);
d_user_data_q.push_back(data);
endfunction
// Clear method for the user data queues - must be called externally to clear user-data queue
// which was being set by add_h_user_data method.
function void clear_h_user_data();
h_user_data_q.delete();
endfunction
// Clear method for the user data queues - must be called externally to clear user-data queue
// which was being set by add_d_user_data method.
function void clear_d_user_data();
d_user_data_q.delete();
endfunction
// Getter method for the user data queues - returns the first data entry.
function bit [HostDataWidth-1:0] get_h_user_data();
`DV_CHECK_NE_FATAL(has_h_user_data(), 0, "h_user_data_q is empty!")
return h_user_data_q.pop_front();
endfunction
// Getter method for the user data queues - returns the first data entry.
function bit [DeviceDataWidth-1:0] get_d_user_data();
`DV_CHECK_NE_FATAL(has_d_user_data(), 0, "d_user_data_q is empty!")
return d_user_data_q.pop_front();
endfunction
// Getter method for the user data queues - must be called externally to check whether there is
// any user data in the queues.
function bit has_h_user_data();
return (h_user_data_q.size() > 0);
endfunction
// Getter method for the user data queues - must be called externally to check whether there is
// any user data in the queues.
function bit has_d_user_data();
return (d_user_data_q.size() > 0);
endfunction
`uvm_object_param_utils_begin(push_pull_agent_cfg#(HostDataWidth, DeviceDataWidth))
`uvm_field_enum(push_pull_agent_e, agent_type, UVM_DEFAULT)
`uvm_field_enum(pull_handshake_e, pull_handshake_type, UVM_DEFAULT)
`uvm_field_int(in_bidirectional_mode, UVM_DEFAULT)
`uvm_field_int(host_delay_min, UVM_DEFAULT)
`uvm_field_int(host_delay_max, UVM_DEFAULT)
`uvm_field_int(device_delay_min, UVM_DEFAULT)
`uvm_field_int(device_delay_max, UVM_DEFAULT)
`uvm_field_int(req_lo_delay_min, UVM_DEFAULT)
`uvm_field_int(req_lo_delay_max, UVM_DEFAULT)
`uvm_field_int(ack_lo_delay_min, UVM_DEFAULT)
`uvm_field_int(ack_lo_delay_max, UVM_DEFAULT)
`uvm_field_int(zero_delays, UVM_DEFAULT)
`uvm_field_int(start_default_device_seq, UVM_DEFAULT)
`uvm_field_queue_int(h_user_data_q, UVM_DEFAULT)
`uvm_field_queue_int(d_user_data_q, UVM_DEFAULT)
`uvm_object_utils_end
`uvm_object_new
endclass

View file

@ -0,0 +1,23 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class push_pull_agent_cov #(parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth)
extends dv_base_agent_cov #(
push_pull_agent_cfg#(HostDataWidth, DeviceDataWidth)
);
`uvm_component_param_utils(push_pull_agent_cov#(HostDataWidth, DeviceDataWidth))
// the base class provides the following handles for use:
// push_pull_agent_cfg: cfg
// covergroups
function new(string name, uvm_component parent);
super.new(name, parent);
// instantiate all covergroups here
endfunction : new
endclass

View file

@ -0,0 +1,56 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
package push_pull_agent_pkg;
// dep packages
import uvm_pkg::*;
import dv_utils_pkg::*;
import dv_lib_pkg::*;
// macro includes
`include "uvm_macros.svh"
`include "dv_macros.svh"
// Instantiated in agent's config object.
typedef enum {
PushAgent,
PullAgent
} push_pull_agent_e;
// Pull req-ack handshake type.
typedef enum {
/*
* The two-phase handshake follows this protocol:
* ____________________
* req _______/ \____________
* ____
* ack _______________________/ \____________
*
* Ack asserts for 1 cycle. Req is accepted when both req and ack is true.
*/
TwoPhase,
/*
* The four-phase handshake follows this protocol:
* __________________
* req _______/ \______________
* _______________________
* ack _____________/ \___
*
* Req de-asserts after ack asserts. Ack de-asserts after the req
* de-asserts. This type of handshake is more common in async domains.
*/
FourPhase
} pull_handshake_e;
`include "push_pull_item.sv"
`include "push_pull_agent_cfg.sv"
`include "push_pull_agent_cov.sv"
`include "push_pull_driver_lib.sv"
`include "push_pull_monitor.sv"
`include "push_pull_sequencer.sv"
`include "push_pull_seq_list.sv"
`include "push_pull_agent.sv"
endpackage : push_pull_agent_pkg

View file

@ -0,0 +1,261 @@
// 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 (!`CB.ready);
`CB.valid_int <= 1'b0;
if (!cfg.hold_h_data_until_next_req) `CB.h_data_int <= 'x;,
wait (cfg.in_reset);)
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

View file

@ -0,0 +1,175 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`include "prim_assert.sv"
interface push_pull_if #(parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth) (
input wire clk, input wire rst_n
);
// Pins for the push handshake (ready/valid)
wire ready;
wire valid;
// Pins for the pull handshake (req/ack)
wire req;
wire ack;
// Parameterized width data payloads in both directions of the handshake.
// Data sent from host to device
wire [HostDataWidth-1:0] h_data;
// Data sent from device to host
wire [DeviceDataWidth-1:0] d_data;
// Internal versions of the interface output signals.
// These signals are assigned as outputs depending on
// how the agent is configured.
logic ready_int;
logic valid_int;
logic req_int;
logic ack_int;
logic [HostDataWidth-1:0] h_data_int;
logic [DeviceDataWidth-1:0] d_data_int;
// Interface mode - Host or Device
dv_utils_pkg::if_mode_e if_mode;
// This bit controls what protocol assertions will be enabled,
// e.g. if the agent is configured in Push mode, we do not want to
// enable assertions relating to the Pull protocol.
//
// This bit is set to the appropriate value in push_pull_agent::build_phase().
bit is_push_agent;
// This bit controls whether the agent is in bidirectional mode,
// transferring data on both sides of the handshake.
bit in_bidirectional_mode;
// Indicates whether pull interface follows 2-phase (0) or 4-phase (1) handshake.
bit is_pull_handshake_4_phase;
// clocking blocks
clocking host_push_cb @(posedge clk);
input ready;
input d_data;
output valid_int;
output h_data_int;
endclocking
clocking device_push_cb @(posedge clk);
output ready_int;
output d_data_int;
input valid;
input h_data;
endclocking
clocking host_pull_cb @(posedge clk);
output req_int;
output h_data_int;
input ack;
input d_data;
endclocking
clocking device_pull_cb @(posedge clk);
input req;
input h_data;
output ack_int;
output d_data_int;
endclocking
clocking mon_cb @(posedge clk);
input ready;
input valid;
input req;
input ack;
input d_data;
input h_data;
endclocking
// Push output assignments
assign ready = (is_push_agent && if_mode == dv_utils_pkg::Device) ? ready_int : 'z;
assign valid = (is_push_agent && if_mode == dv_utils_pkg::Host) ? valid_int : 'z;
// Pull output assignments
assign req = (!is_push_agent && if_mode == dv_utils_pkg::Host) ? req_int : 'z;
assign ack = (!is_push_agent && if_mode == dv_utils_pkg::Device) ? ack_int : 'z;
// Data signal assignments
assign h_data = (if_mode == dv_utils_pkg::Host) ? h_data_int : 'z;
assign d_data = (if_mode == dv_utils_pkg::Device) ? d_data_int : 'z;
/////////////////////////////////////////
// Assertions for ready/valid protocol //
/////////////////////////////////////////
// The ready and valid signals should always have known values.
`ASSERT_KNOWN_IF(ReadyIsKnown_A, ready, is_push_agent, clk, !rst_n)
`ASSERT_KNOWN_IF(ValidIsKnown_A, valid, is_push_agent, clk, !rst_n)
// Whenever valid is asserted, h_data must have a known value.
`ASSERT_KNOWN_IF(H_DataKnownWhenValid_A, h_data, valid && is_push_agent, clk, !rst_n)
// Whenver ready is asserted and the agent is in bidirectional mode,
// d_data must have a known value.
`ASSERT_KNOWN_IF(D_DataKnownWhenReady_A, d_data,
ready && is_push_agent && in_bidirectional_mode, clk, !rst_n)
// When valid is asserted but ready is low the h_data must stay stable.
`ASSERT_IF(H_DataStableWhenValidAndNotReady_A, (valid && !ready) |=> $stable(h_data),
is_push_agent, clk, !rst_n)
// When valid is asserted, it must stay high until seeing ready be asserted.
`ASSERT_IF(ValidHighUntilReady_A, $rose(valid) |-> (valid throughout ready [->1]),
is_push_agent, clk, !rst_n)
/////////////////////////////////////
// Assertions for req/ack protocol //
/////////////////////////////////////
// The req and ack signals should always have known values.
`ASSERT_KNOWN_IF(ReqIsKnown_A, req, !is_push_agent, clk, !rst_n)
`ASSERT_KNOWN_IF(AckIsKnown_A, ack, !is_push_agent, clk, !rst_n)
// When ack is asserted, d_data must have a known value.
`ASSERT_KNOWN_IF(D_DataKnownWhenAck_A, d_data, ack && !is_push_agent, clk, !rst_n)
// When req is asserted and the agent is in bidirectional mode,
// h_data must have a known value.
`ASSERT_KNOWN_IF(H_DataKnownWhenReq_A, h_data,
req && !is_push_agent && in_bidirectional_mode, clk, !rst_n)
// When req is asserted but ack is low, and the agent is in bidirectional mode,
// h_data must remain stable.
`ASSERT_IF(H_DataStableWhenBidirectionalAndReq_A, (req && !ack) |=> $stable(h_data),
!is_push_agent && in_bidirectional_mode, clk, !rst_n)
// TODO: The following two assertions make a rather important assumption about the req/ack
// protocol that will be used for the key/csrng interfaces, which is that no requests
// are allowed to be dropped by the network. This issue is made more complex by the
// fact that several of the IPs connected to this network may be in different clock
// domains, requiring CDC.
// Based on the final decision on this issue, these assertions may have to be removed
// if it is allowed for requests to be dropped.
// I 2-phase req-ack handshake, ack cannot be 1 if req is not 1.
`ASSERT_IF(AckAssertedOnlyWhenReqAsserted_A, ack |-> req,
!is_push_agent && !is_pull_handshake_4_phase, clk, !rst_n)
// Req is asserted only after previous ack is de-asserted.
`ASSERT_IF(NoAckOnNewReq_A, $rose(req) |-> !($past(ack, 1)) && !ack,
!is_push_agent, clk, !rst_n)
// When req is asserted, it must stay high until a corresponding ack is seen.
`ASSERT_IF(ReqHighUntilAck_A, $rose(req) |-> (req throughout ack[->1]),
!is_push_agent, clk, !rst_n)
// When ack is asserted, it must stay high until a corresponding req is de-asserted,
// in case of four-phase handshake.
`ASSERT_IF(AckHighUntilReq_A, $rose(ack) |-> (ack throughout (!req[->1])),
!is_push_agent && is_pull_handshake_4_phase, clk, !rst_n)
// TODO: Add support for async clock domains.
endinterface

View file

@ -0,0 +1,56 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class push_pull_item #(parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth)
extends uvm_sequence_item;
rand bit [HostDataWidth-1:0] h_data;
rand bit [DeviceDataWidth-1:0] d_data;
// Host-side delay for both push/pull protocols.
rand int unsigned host_delay;
constraint host_delay_max_c {
soft host_delay <= 1000;
}
// Device-side delay for both push/pull protocols.
rand int unsigned device_delay;
constraint device_delay_max_c {
soft device_delay <= 1000;
}
// 4-phase pull protocol delays to de-assert req. This delay is applied after ack assertion.
rand int unsigned req_lo_delay;
constraint req_lo_delay_max_c {
soft req_lo_delay <= 1000;
}
// 4-phase pull protocol delays to de-assert ack. This delay is applied after req de-assertion.
rand int unsigned ack_lo_delay;
constraint ack_lo_delay_max_c {
soft ack_lo_delay <= 1000;
}
`uvm_object_param_utils_begin(push_pull_item#(HostDataWidth, DeviceDataWidth))
`uvm_field_int(h_data, UVM_DEFAULT)
`uvm_field_int(d_data, UVM_DEFAULT)
`uvm_field_int(host_delay, UVM_DEFAULT)
`uvm_field_int(device_delay, UVM_DEFAULT)
`uvm_field_int(req_lo_delay, UVM_DEFAULT)
`uvm_field_int(ack_lo_delay, UVM_DEFAULT)
`uvm_object_utils_end
`uvm_object_new
virtual function string convert2string();
return {$sformatf("h_data = 0x%0x ", h_data),
$sformatf("d_data = 0x%0x ", d_data),
$sformatf("host_delay = 0x%0d ", host_delay),
$sformatf("device_delay = 0x%0d ", device_delay),
$sformatf("req_lo_delay = 0x%0d ", req_lo_delay),
$sformatf("ack_lo_delay = 0x%0d ", ack_lo_delay)};
endfunction
endclass

View file

@ -0,0 +1,143 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class push_pull_monitor #(parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth)
extends dv_base_monitor #(
.ITEM_T (push_pull_item#(HostDataWidth, DeviceDataWidth)),
.CFG_T (push_pull_agent_cfg#(HostDataWidth, DeviceDataWidth)),
.COV_T (push_pull_agent_cov#(HostDataWidth, DeviceDataWidth))
);
`uvm_component_param_utils(push_pull_monitor#(HostDataWidth, DeviceDataWidth))
// the base class provides the following handles for use:
// push_pull_agent_cfg: cfg
// push_pull_agent_cov: cov
// uvm_analysis_port #(ITEM_T): analysis_port
// uvm_analysis_port #(ITEM_T): req_analysis_port;
`uvm_component_new
task run_phase(uvm_phase phase);
@(posedge cfg.vif.rst_n);
fork
monitor_reset();
collect_trans(phase);
// Collect partial pull reqs for the reactive pull device agent.
collect_pull_req();
join_none
endtask
virtual protected task monitor_reset();
forever begin
@(negedge cfg.vif.rst_n);
cfg.in_reset = 1;
@(posedge cfg.vif.rst_n);
cfg.in_reset = 0;
end
endtask
// Shorthand for restarting forever loop on reset detection.
`define WAIT_FOR_RESET \
if (cfg.in_reset) begin \
wait (!cfg.in_reset); \
continue; \
end
// Collect fully-completed transactions.
//
// TODO : sample covergroups
virtual protected task collect_trans(uvm_phase phase);
if (cfg.agent_type == PushAgent) begin
forever begin
@(cfg.vif.mon_cb);
`WAIT_FOR_RESET
if (cfg.vif.mon_cb.ready && cfg.vif.mon_cb.valid) begin
create_and_write_item();
end
end
end else begin
forever begin
@(cfg.vif.mon_cb);
`WAIT_FOR_RESET
if (cfg.vif.mon_cb.req && cfg.vif.mon_cb.ack) begin
create_and_write_item();
// Wait for req to de-assert in case of four-phase handshake.
if (cfg.pull_handshake_type == FourPhase) begin
`uvm_info(`gfn, "Waiting for 4-phase req-ack to de-asssert", UVM_HIGH)
`DV_SPINWAIT_EXIT(while (cfg.vif.mon_cb.ack) @(cfg.vif.mon_cb);,
wait (cfg.in_reset))
end
end
end
end
endtask
// Collects partial pull requests.
//
// This task is only used for device agents using the Pull protocol.
// It will pick up any incoming requests from the DUT and send a signal to the
// sequencer (in the form of a sequence item), which will then be forwarded to
// the sequence, which then generates the appropriate response item.
//
// TODO: This assumes requests cannot be dropped, and might need to be fixed
// if this is allowed.
virtual protected task collect_pull_req();
push_pull_item#(HostDataWidth, DeviceDataWidth) item;
if (!(cfg.agent_type == PullAgent && cfg.if_mode == dv_utils_pkg::Device)) return;
forever begin
@(cfg.vif.mon_cb);
`WAIT_FOR_RESET
if (cfg.vif.mon_cb.req) begin
`uvm_info(`gfn, $sformatf("[%0s] pull req detected", cfg.agent_type), UVM_HIGH)
// TODO: sample any covergroups
item = push_pull_item#(HostDataWidth, DeviceDataWidth)::type_id::create("item");
item.h_data = cfg.vif.mon_cb.h_data;
req_analysis_port.write(item);
// After picking up a request, wait until a response is sent before
// detecting another request, as this is not a pipelined protocol.
`DV_SPINWAIT_EXIT(while (!cfg.vif.mon_cb.ack) @(cfg.vif.mon_cb);,
wait (cfg.in_reset))
if (cfg.pull_handshake_type == FourPhase) begin
`DV_SPINWAIT_EXIT(while (cfg.vif.mon_cb.ack) @(cfg.vif.mon_cb);,
wait (cfg.in_reset))
end
end
end
endtask
`undef WAIT_FOR_RESET
// Creates and writes the item to the analysis_port.
//
// The onus is on the caller to invoke this function at the right time -
// i.e. when the transaction is valid.
virtual protected function void create_and_write_item();
push_pull_item#(HostDataWidth, DeviceDataWidth) item;
item = push_pull_item#(HostDataWidth, DeviceDataWidth)::type_id::create("item");
item.d_data = cfg.vif.mon_cb.d_data;
item.h_data = cfg.vif.mon_cb.h_data;
`uvm_info(`gfn,
$sformatf("[%0s] transaction detected: h_data[0x%0x], d_data[0x%0x]",
cfg.agent_type, item.h_data, item.d_data), UVM_HIGH)
analysis_port.write(item);
endfunction
// Detects periods of inactivity for the ok_to_end watchdog.
//
// Set ok_to_end bit to detect inactivity on the bus. Spawned by
// dv_base_monitor as a thread towards the end of run_phase.
virtual task monitor_ready_to_end();
forever begin
@(cfg.vif.mon_cb);
if (cfg.agent_type == PushAgent) begin
ok_to_end = !cfg.vif.mon_cb.valid;
end else begin
ok_to_end = !cfg.vif.mon_cb.req && !cfg.vif.mon_cb.ack;
end
end
endtask
endclass

View file

@ -0,0 +1,14 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class push_pull_sequencer #(parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth)
extends dv_base_sequencer #(
.ITEM_T (push_pull_item#(HostDataWidth, DeviceDataWidth)),
.CFG_T (push_pull_agent_cfg#(HostDataWidth, DeviceDataWidth))
);
`uvm_component_param_utils(push_pull_sequencer#(HostDataWidth, DeviceDataWidth))
`uvm_component_new
endclass

View file

@ -0,0 +1,39 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class push_pull_base_seq #(parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth)
extends dv_base_seq #(
.REQ (push_pull_item#(HostDataWidth, DeviceDataWidth)),
.CFG_T (push_pull_agent_cfg#(HostDataWidth, DeviceDataWidth)),
.SEQUENCER_T (push_pull_sequencer#(HostDataWidth, DeviceDataWidth))
);
`uvm_object_param_utils(push_pull_base_seq#(HostDataWidth, DeviceDataWidth))
`uvm_object_new
// Randomizes the req or response.
//
// Regardless of agent or item type, we can apply the same set of limits.
virtual function void randomize_item(push_pull_item #(HostDataWidth, DeviceDataWidth) item);
`DV_CHECK_RANDOMIZE_WITH_FATAL(item,
if (cfg.zero_delays) {
host_delay == 0;
device_delay == 0;
req_lo_delay == 0;
ack_lo_delay == 0;
} else {
host_delay inside {[cfg.host_delay_min : cfg.host_delay_max]};
device_delay inside {[cfg.device_delay_min : cfg.device_delay_max]};
req_lo_delay inside {[cfg.req_lo_delay_min : cfg.req_lo_delay_max]};
ack_lo_delay inside {[cfg.ack_lo_delay_min : cfg.ack_lo_delay_max]};
}
)
endfunction
virtual task body();
`uvm_fatal(`gtn, "Need to override this when you extend from this class!")
endtask
endclass

View file

@ -0,0 +1,75 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Response sequence for Push and Pull protocols.
// This sequence will infinitely pol for any DUT requests detected by
// the monitor, and trigger the response driver anytime a request is detected.
class push_pull_device_seq #(parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth)
extends push_pull_base_seq #(HostDataWidth, DeviceDataWidth);
`uvm_object_param_utils(push_pull_device_seq#(HostDataWidth, DeviceDataWidth))
`uvm_object_new
// Randomizes the device rsp.
//
// The randomization works out the same regardless of the agent type.
virtual function void randomize_item(push_pull_item #(HostDataWidth, DeviceDataWidth) item);
super.randomize_item(item);
// If user-provided data is available, use it.
if (cfg.has_d_user_data()) item.d_data = cfg.get_d_user_data();
endfunction
virtual task body();
if (cfg.agent_type == PushAgent) begin
push_device_thread();
end else begin
pull_device_thread();
end
endtask
// Sends random rsps (assertion of ready) back to host.
//
// In Push mode, continuously send an empty but randomized sequence item
// to the device driver (which just needs to assert ready).
// If the agent is in bidirectional mode, send the corresponding data.
// TODO: for bidirectional mode, there may be a need for the returned device data to be
// constructed based on the received host data. This may need to be made "reactive" as well.
virtual task push_device_thread();
forever begin
wait (!cfg.in_reset);
`uvm_create(req)
start_item(req);
randomize_item(req);
finish_item(req);
get_response(rsp);
end
endtask
// Sends random rsps (device data and ack) back to host.
virtual task pull_device_thread();
push_pull_item #(HostDataWidth, DeviceDataWidth) req_q[$];
fork
forever begin : get_req
p_sequencer.req_analysis_fifo.get(req);
req_q.push_back(req);
end : get_req
forever begin : send_rsp
if (cfg.in_reset) begin
req_q.delete();
wait (!cfg.in_reset);
end
wait (req_q.size());
rsp = req_q.pop_front();
start_item(rsp);
randomize_item(rsp);
finish_item(rsp);
get_response(rsp);
end : send_rsp
join
endtask
endclass

View file

@ -0,0 +1,37 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Request sequence for Push and Pull protocols.
// This sequence will send num_trans requests to the DUT.
class push_pull_host_seq #(parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth)
extends push_pull_base_seq #(HostDataWidth, DeviceDataWidth);
`uvm_object_param_utils(push_pull_host_seq#(HostDataWidth, DeviceDataWidth))
`uvm_object_new
// Default to send 1 transactions.
// Can be overridden at a higher layer.
int unsigned num_trans = 1;
// Randomizes the host req.
virtual function void randomize_item(push_pull_item #(HostDataWidth, DeviceDataWidth) item);
super.randomize_item(item);
// If user-provided data is available, use it.
if (cfg.has_h_user_data()) item.h_data = cfg.get_h_user_data();
endfunction
virtual task body();
repeat (num_trans) begin : send_req
`uvm_create(req)
start_item(req);
randomize_item(req);
finish_item(req);
get_response(rsp);
end : send_req
endtask
endclass

View file

@ -0,0 +1,58 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Request sequence for Push and Pull protocols.
//
// Similiar push_pull_host_seq, this sequence will send an unlimited number of requests to
// the DUT. It is the responsibility of the parent sequence to halt this sequence by setting
// the stop field.
//
class push_pull_indefinite_host_seq #(parameter int HostDataWidth = 32,
parameter int DeviceDataWidth = HostDataWidth)
extends push_pull_host_seq #(HostDataWidth, DeviceDataWidth);
`uvm_object_param_utils(push_pull_indefinite_host_seq#(HostDataWidth, DeviceDataWidth))
`uvm_object_new
typedef enum int {
Continue = 0,
Stop = 1,
HardStop = 2
} stop_status_e;
stop_status_e stop_status;
virtual task body();
fork : isolation_fork
begin
fork
begin
wait(stop_status == HardStop);
`uvm_info(`gfn, "Rec'd stop message", UVM_FULL)
end
while (stop_status == Continue) begin : send_req
`uvm_create(req)
start_item(req);
randomize_item(req);
finish_item(req);
get_response(rsp);
end : send_req
join_any;
disable fork;
end
join : isolation_fork
`uvm_info(`gfn, "STOPPED", UVM_FULL)
endtask
virtual task stop(bit hard = 1);
stop_status = hard ? HardStop : Stop;
endtask
virtual task pre_start();
super.pre_start();
stop_status = Continue;
endtask
endclass

View file

@ -0,0 +1,8 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`include "push_pull_base_seq.sv"
`include "push_pull_host_seq.sv"
`include "push_pull_indefinite_host_seq.sv"
`include "push_pull_device_seq.sv"