[dv, push pull agent] Support for 4-phase req-ack
- Add support for 4-phase req-ack
- add `pull_handshake_e` type
- Update cfg, agent, monitor, driver and interface to support this
- Refactor and minor fixups in monitor and sequences
Signed-off-by: Srikrishna Iyer <sriyer@google.com>
diff --git a/hw/dv/sv/push_pull_agent/push_pull_agent.sv b/hw/dv/sv/push_pull_agent/push_pull_agent.sv
index 6e26b03..e9c9910 100644
--- a/hw/dv/sv/push_pull_agent/push_pull_agent.sv
+++ b/hw/dv/sv/push_pull_agent/push_pull_agent.sv
@@ -28,9 +28,10 @@
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.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);
@@ -42,7 +43,7 @@
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);
+ 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());
diff --git a/hw/dv/sv/push_pull_agent/push_pull_agent_cfg.sv b/hw/dv/sv/push_pull_agent/push_pull_agent_cfg.sv
index 5d8ac45..475436e 100644
--- a/hw/dv/sv/push_pull_agent/push_pull_agent_cfg.sv
+++ b/hw/dv/sv/push_pull_agent/push_pull_agent_cfg.sv
@@ -13,24 +13,31 @@
// 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 = 1'b0;
+ 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 = 0;
- bit hold_d_data_until_next_req = 0;
-
- // Device-side delay range for both Push/Pull protocols.
- rand int unsigned device_delay_min;
- rand int unsigned device_delay_max;
- rand int unsigned large_device_delay_max_weight = 10; // max 100
+ 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;
- rand int unsigned large_host_delay_max_weight = 10; // max 100
+
+ // 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;
@@ -51,22 +58,76 @@
local bit [HostDataWidth-1:0] h_user_data_q[$];
local bit [DeviceDataWidth-1:0] d_user_data_q[$];
- constraint device_delay_min_c {
- device_delay_min == 0;
- }
-
- constraint device_delay_max_c {
- device_delay_max dist {1000 :/ large_device_delay_max_weight,
- 100 :/ 100 - large_device_delay_max_weight};
- }
-
constraint host_delay_min_c {
- host_delay_min == 0;
+ soft host_delay_min == 0;
}
constraint host_delay_max_c {
- host_delay_max dist {1000 :/ large_host_delay_max_weight,
- 100 :/ 100 - large_host_delay_max_weight};
+ 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.
@@ -124,16 +185,21 @@
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_int(in_bidirectional_mode, UVM_DEFAULT)
- `uvm_field_int(device_delay_min, UVM_DEFAULT)
- `uvm_field_int(device_delay_max, UVM_DEFAULT)
- `uvm_field_int(host_delay_min, UVM_DEFAULT)
- `uvm_field_int(host_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_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
diff --git a/hw/dv/sv/push_pull_agent/push_pull_agent_pkg.sv b/hw/dv/sv/push_pull_agent/push_pull_agent_pkg.sv
index 031c44f..41b5fc8 100644
--- a/hw/dv/sv/push_pull_agent/push_pull_agent_pkg.sv
+++ b/hw/dv/sv/push_pull_agent/push_pull_agent_pkg.sv
@@ -18,6 +18,32 @@
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"
diff --git a/hw/dv/sv/push_pull_agent/push_pull_driver_lib.sv b/hw/dv/sv/push_pull_agent/push_pull_driver_lib.sv
index 2a8319b..6cb84c8 100644
--- a/hw/dv/sv/push_pull_agent/push_pull_driver_lib.sv
+++ b/hw/dv/sv/push_pull_agent/push_pull_driver_lib.sv
@@ -52,24 +52,18 @@
endfunction
virtual task reset_signals();
- // initial reset at start of test
sub_driver.reset_signals();
forever begin
@(negedge cfg.vif.rst_n);
- `uvm_info(`gfn, "Driver is resetting", UVM_HIGH)
- sub_driver.set_in_reset(1'b1);
+ `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)
- sub_driver.set_in_reset(1'b0);
end
endtask
// Drive trans received from sequencer.
virtual task get_and_drive();
- // Wait for the initial reset to pass.
- @(posedge cfg.vif.rst_n);
-
forever begin
seq_item_port.try_next_item(req);
if (req == null) begin
@@ -78,11 +72,7 @@
end
`uvm_info(`gfn, $sformatf("Driver received item:\n%0s", req.convert2string()), UVM_HIGH)
- if (sub_driver.get_in_reset()) begin
- `uvm_info(`gfn, "Item not applied to the DUT due to reset assertion", UVM_HIGH)
- end else begin
- sub_driver.drive_item(req);
- end
+ sub_driver.drive_item(req);
seq_item_port.item_done(req);
end
endtask
@@ -97,27 +87,11 @@
parameter int DeviceDataWidth = HostDataWidth
) extends uvm_object;
- // Indicates that the interface is in reset.
- protected bit in_reset;
-
// The handle to the agent cfg object set by the main driver.
push_pull_agent_cfg #(HostDataWidth, DeviceDataWidth) cfg;
`uvm_object_new
- // Tell if the interface is in or out of reset.
- //
- // The main driver already implements a task that monitors the interface reset. The sub driver's
- // in_reset bit is controlled by the main driver through this function.
- virtual function void set_in_reset(bit in_reset);
- this.in_reset = in_reset;
- endfunction
-
- // Returns the in_reset value.
- virtual function bit get_in_reset();
- return in_reset;
- endfunction
-
// Reset the interface signals driven to the DUT.
pure virtual function void reset_signals();
@@ -153,14 +127,12 @@
virtual task drive_item(push_pull_item#(HostDataWidth, DeviceDataWidth) req);
`DV_SPINWAIT_EXIT(
repeat (req.host_delay) @(`CB);
- `CB.valid_int <= 1'b1;
+ `CB.valid_int <= 1'b1;
`CB.h_data_int <= req.h_data;
- do begin
- @(`CB);
- end while (!`CB.ready);
+ do @(`CB); while (!`CB.ready);
`CB.valid_int <= 1'b0;
if (!cfg.hold_h_data_until_next_req) `CB.h_data_int <= 'x;,
- wait(in_reset);)
+ wait (cfg.in_reset);)
endtask
`undef CB
@@ -191,14 +163,18 @@
virtual task drive_item(push_pull_item#(HostDataWidth, DeviceDataWidth) req);
`DV_SPINWAIT_EXIT(
repeat (req.host_delay) @(`CB);
- `CB.req_int <= 1'b1;
+ `CB.req_int <= 1'b1;
`CB.h_data_int <= req.h_data;
- do begin
- @(`CB);
- end while (!`CB.ack);
- `CB.req_int <= 1'b0;
+ 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(in_reset);)
+ wait (cfg.in_reset);)
endtask
`undef CB
@@ -228,13 +204,14 @@
// 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.ready_int <= 1'b1;
`CB.d_data_int <= req.d_data;
@(`CB);
- `CB.ready_int <= 1'b0;
+ `CB.ready_int <= 1'b0;
if (!cfg.hold_d_data_until_next_req) `CB.d_data_int <= 'x;,
- wait(in_reset);)
+ wait (cfg.in_reset);)
endtask
`undef CB
@@ -266,12 +243,17 @@
`DV_SPINWAIT_EXIT(
while (!`CB.req) @(`CB);
repeat (req.device_delay) @(`CB);
- `CB.ack_int <= 1'b1;
+ `CB.ack_int <= 1'b1;
`CB.d_data_int <= req.d_data;
- @(`CB);
- `CB.ack_int <= 1'b0;
+ 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(in_reset);)
+ wait (cfg.in_reset);)
endtask
`undef CB
diff --git a/hw/dv/sv/push_pull_agent/push_pull_if.sv b/hw/dv/sv/push_pull_agent/push_pull_if.sv
index cdbd9a5..49c8c49 100644
--- a/hw/dv/sv/push_pull_agent/push_pull_if.sv
+++ b/hw/dv/sv/push_pull_agent/push_pull_if.sv
@@ -47,6 +47,9 @@
// 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;
@@ -140,7 +143,7 @@
// 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),
- in_bidirectional_mode, clk, !rst_n)
+ !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
@@ -150,11 +153,23 @@
// Based on the final decision on this issue, these assertions may have to be removed
// if it is allowed for requests to be dropped.
- // ack cannot be 1 if req is not 1.
- `ASSERT_IF(AckAssertedOnlyWhenReqAsserted_A, ack |-> req, !is_push_agent, clk, !rst_n)
+ // 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]),
+ `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
diff --git a/hw/dv/sv/push_pull_agent/push_pull_item.sv b/hw/dv/sv/push_pull_agent/push_pull_item.sv
index 84a0a9b..7a80659 100644
--- a/hw/dv/sv/push_pull_agent/push_pull_item.sv
+++ b/hw/dv/sv/push_pull_agent/push_pull_item.sv
@@ -9,17 +9,37 @@
rand bit [HostDataWidth-1:0] h_data;
rand bit [DeviceDataWidth-1:0] d_data;
- // Host-side delay for both Push/Pull protocols.
+ // 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.
+ // 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
@@ -27,8 +47,10 @@
virtual function string convert2string();
return {$sformatf("h_data = 0x%0x ", h_data),
$sformatf("d_data = 0x%0x ", d_data),
- $sformatf("host_delay = 0x%0x ", host_delay),
- $sformatf("device_delay = 0x%0x ", device_delay)};
+ $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
diff --git a/hw/dv/sv/push_pull_agent/push_pull_monitor.sv b/hw/dv/sv/push_pull_agent/push_pull_monitor.sv
index db10a28..70ab9ad 100644
--- a/hw/dv/sv/push_pull_agent/push_pull_monitor.sv
+++ b/hw/dv/sv/push_pull_agent/push_pull_monitor.sv
@@ -23,12 +23,9 @@
@(posedge cfg.vif.rst_n);
fork
monitor_reset();
- collect_valid_trans();
- // We only need to monitor incoming requests if the agent is configured
- // in device mode and is using Pull protocol.
- if (cfg.if_mode == dv_utils_pkg::Device && cfg.agent_type == PullAgent) begin
- collect_request();
- end
+ collect_trans(phase);
+ // Collect partial pull reqs for the reactive pull device agent.
+ collect_pull_req();
join_none
endtask
@@ -36,30 +33,83 @@
forever begin
@(negedge cfg.vif.rst_n);
cfg.in_reset = 1;
- // TODO: sample any reset-related covergroups
@(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_valid_trans();
- forever begin
- @(cfg.vif.mon_cb);
- if (cfg.agent_type == PushAgent) begin
+ 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();
- // TODO: sample covergroups
end
- end else begin
+ 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();
- // TODO: sample covergroups
+ // 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 -
@@ -75,39 +125,17 @@
analysis_port.write(item);
endfunction
- // 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.
+ // Detects periods of inactivity for the ok_to_end watchdog.
//
- // TODO: This assumes no requests can be dropped, and might need to be fixed
- // if this is not allowed.
- virtual protected task collect_request();
- push_pull_item#(HostDataWidth, DeviceDataWidth) item;
- forever begin
- @(cfg.vif.mon_cb);
- if (cfg.vif.req) begin
- `uvm_info(`gfn, "detected pull request", UVM_HIGH)
- // TODO: sample any covergroups
- item = push_pull_item#(HostDataWidth, DeviceDataWidth)::type_id::create("item");
- 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))
- end
- end
- endtask
-
- // update ok_to_end to prevent simulation from finishing when
- // there is any activity on the bus.
+ // 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.valid == 0);
+ ok_to_end = !cfg.vif.mon_cb.valid;
end else begin
- ok_to_end = (cfg.vif.req == 0) && (cfg.vif.ack == 0);
+ ok_to_end = !cfg.vif.mon_cb.req && !cfg.vif.mon_cb.ack;
end
end
endtask
diff --git a/hw/dv/sv/push_pull_agent/seq_lib/push_pull_base_seq.sv b/hw/dv/sv/push_pull_agent/seq_lib/push_pull_base_seq.sv
index 8de6a0a..66b858a 100644
--- a/hw/dv/sv/push_pull_agent/seq_lib/push_pull_base_seq.sv
+++ b/hw/dv/sv/push_pull_agent/seq_lib/push_pull_base_seq.sv
@@ -13,6 +13,25 @@
`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
diff --git a/hw/dv/sv/push_pull_agent/seq_lib/push_pull_device_seq.sv b/hw/dv/sv/push_pull_agent/seq_lib/push_pull_device_seq.sv
index 00a09f2..0b15f3f 100644
--- a/hw/dv/sv/push_pull_agent/seq_lib/push_pull_device_seq.sv
+++ b/hw/dv/sv/push_pull_agent/seq_lib/push_pull_device_seq.sv
@@ -14,62 +14,62 @@
`uvm_object_new
- mailbox #(push_pull_item#(HostDataWidth, DeviceDataWidth)) req_mailbox;
+ // 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();
- req_mailbox = new();
-
if (cfg.agent_type == PushAgent) begin
- // 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.
- forever begin
- // Pause if we are in reset.
- if (cfg.in_reset) begin
- wait(!cfg.in_reset);
- end
-
- req = push_pull_item#(HostDataWidth, DeviceDataWidth)::type_id::create("req");
- start_item(req);
- `DV_CHECK_RANDOMIZE_WITH_FATAL(req,
- if (cfg.zero_delays) {
- device_delay == 0;
- } else {
- device_delay inside {[cfg.device_delay_min : cfg.device_delay_max]};
- }
- )
- // If user-provided data is available, use this instead of randomized data.
- if (cfg.has_d_user_data()) req.d_data = cfg.get_d_user_data();
- finish_item(req);
- get_response(rsp);
- `uvm_info(`gfn, $sformatf("Received response: %0s", rsp.convert2string()), UVM_HIGH)
- end
+ push_device_thread();
end else begin
- fork
- forever begin : collect_req
- // We indefinitely poll for any traffic sent from the monitor and store it to a mailbox.
- p_sequencer.req_analysis_fifo.get(req);
- req_mailbox.put(req);
- end : collect_req
- forever begin : send_req
- // Collect transactions stored in the mailbox and send them to the driver.
- req_mailbox.get(rsp);
- start_item(rsp);
- `DV_CHECK_RANDOMIZE_WITH_FATAL(rsp,
- if (cfg.zero_delays) {
- device_delay == 0;
- } else {
- device_delay inside {[cfg.device_delay_min : cfg.device_delay_max]};
- }
- )
- // If user-provided data is availabe, use this instead of randomized data.
- if (cfg.has_d_user_data()) rsp.d_data = cfg.get_d_user_data();
- finish_item(rsp);
- get_response(rsp);
- `uvm_info(`gfn, $sformatf("Received response: %0s", rsp.convert2string()), UVM_HIGH)
- end : send_req
- join
+ 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
diff --git a/hw/dv/sv/push_pull_agent/seq_lib/push_pull_host_seq.sv b/hw/dv/sv/push_pull_agent/seq_lib/push_pull_host_seq.sv
index e562213..98c3b02 100644
--- a/hw/dv/sv/push_pull_agent/seq_lib/push_pull_host_seq.sv
+++ b/hw/dv/sv/push_pull_agent/seq_lib/push_pull_host_seq.sv
@@ -17,25 +17,20 @@
// 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();
- for (int i = 0; i < num_trans; i++) begin : send_req
- req = push_pull_item#(HostDataWidth, DeviceDataWidth)::type_id::create(
- $sformatf("req[%0d]", i));
+ repeat (num_trans) begin : send_req
+ `uvm_create(req)
start_item(req);
- `DV_CHECK_RANDOMIZE_WITH_FATAL(req,
- if (cfg.zero_delays) {
- host_delay == 0;
- } else {
- host_delay inside {[cfg.host_delay_min : cfg.host_delay_max]};
- }
- )
- // If user-provided data is ready, use this instead of random data.
- if (cfg.has_h_user_data()) req.h_data = cfg.get_h_user_data();
- `uvm_info(`gfn, $sformatf("Starting sequence item: %0s", req.convert2string()), UVM_HIGH)
- // We don't care about response data here, the monitor will log all complete transactions.
+ randomize_item(req);
finish_item(req);
get_response(rsp);
- `uvm_info(`gfn, $sformatf("Received response: %0s", rsp.convert2string()), UVM_HIGH)
end : send_req
endtask