Skip to content

Commit

Permalink
[jtag,dv] Keep TCK running a short time after a transaction
Browse files Browse the repository at this point in the history
This handles a rather confusing situation in the DMI frontdoor. The
problem is that a DMI request has to travel from dmi_jtag to
dm_top (involving a CDC from TCK to the main clock) and then back
again (involving a CDC from the main clock back to TCK).

If we stop sending JTAG operations in that time, the CDC doesn't
actually get anywhere. The wait is jtag_agent_cfg_h.vif.wait_tck(10),
which ends up waiting based on elapsed time rather than clock edges.

The result is that when we send another JTAG operation to find out the
result of the operation, the "busy" status only makes it through the
CDC *after* the JTAG operation has finished and reported that the dm
is idle.

Things then get rather confused when the "busy" status appears, which
the agent interprets as a mysterious operation that has just come into
being.

Signed-off-by: Rupert Swarbrick <[email protected]>
  • Loading branch information
rswarbrick committed Jun 24, 2024
1 parent 85f9684 commit 7eb9f33
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 4 deletions.
10 changes: 10 additions & 0 deletions hw/dv/sv/jtag_agent/jtag_agent_cfg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ class jtag_agent_cfg extends dv_base_agent_cfg;
// This knob can bypass default 1 cycle initial delay of 'driver.drive_jtag_req()' task.
// Use this knob only for the necessary tests.
bit min_rti = 0;

// This JTAG driver expects to control the JTAG interface clock (TCK), which it does by setting
// tck_en to 0 or 1. If rtc_length is positive, it gives a number of TCK cycles to "run to clear"
// (where we expect to be in the RunTest/IDLE state) before disabling the clock.
//
// This allows safe interactions with things like DMI operations, where we expect CDC to convert
// between the JTAG clock and a system clock. This only works when TCK is running!
int unsigned rtc_length = 0;


`uvm_object_utils_begin(jtag_agent_cfg)
`uvm_field_object(jtag_dtm_ral, UVM_DEFAULT)
`uvm_object_utils_end
Expand Down
44 changes: 40 additions & 4 deletions hw/dv/sv/jtag_agent/jtag_driver.sv
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class jtag_driver extends dv_base_driver #(jtag_item, jtag_agent_cfg);
// without going to Run-Test-Idle), this extra cycle must be skipped
protected bit exit_to_rti_dr_past = 1;

// A state flag used for controlling when we disable TCK if rtc_length is positive (see
// release_tck() for details)
protected bit tck_in_use = 1'b0;

// Reset internal model of interface state
//
// This is needed on a genuine interface reset, but is also needed when we find out our interface
Expand All @@ -52,6 +56,37 @@ class jtag_driver extends dv_base_driver #(jtag_item, jtag_agent_cfg);
exit_to_rti_dr_past = 1;
endfunction

// Turn on TCK in the jtag_if
//
// This "acquires TCK" by setting tck_in_use (and checks that it was false beforehand).
virtual function void enable_tck();
`DV_CHECK_FATAL(!tck_in_use)
tck_in_use = 1'b1;
cfg.vif.tck_en <= 1'b1;
endfunction

// Turn off TCK in the jtag_if.
//
// If rtc_length is positive, wait that many ticks before actually turning TCK off. Clear
// tck_in_use at the start (after checking it was true). If it becomes true while we're waiting,
// return without making any change since another process now requires the clock running.
virtual task release_tck();
`DV_CHECK_FATAL(tck_in_use)
tck_in_use = 1'b0;
fork begin
if (cfg.rtc_length > 0) begin
fork begin : isolation_fork
fork
cfg.vif.wait_tck(cfg.rtc_length);
@(tck_in_use);
join_any
disable fork;
end join
end
if (!tck_in_use) cfg.vif.tck_en <= 1'b0;
end join_none
endtask

virtual task reset_signals();
fork
begin
Expand Down Expand Up @@ -96,8 +131,9 @@ class jtag_driver extends dv_base_driver #(jtag_item, jtag_agent_cfg);
// Make sure that we're aligned to HOST_CB (on the negedge of TCK). If we have just finished
// the previous item, this delay is going to add a cycle of TCK, but we're in RunTest/IDLE,
// so it won't cause any harm.
cfg.vif.tck_en <= 1'b1;
enable_tck();
@(`HOST_CB);
release_tck();

`uvm_info(`gfn, req.sprint(uvm_default_line_printer), UVM_HIGH)
`DV_SPINWAIT_EXIT(drive_jtag_req(req, rsp);,
Expand Down Expand Up @@ -131,7 +167,7 @@ class jtag_driver extends dv_base_driver #(jtag_item, jtag_agent_cfg);
fork
begin
// Enable clock
cfg.vif.tck_en <= 1'b1;
enable_tck();
`HOST_CB.tms <= 1'b0;
@(`HOST_CB);
// Go to Test Logic Reset
Expand All @@ -152,7 +188,7 @@ class jtag_driver extends dv_base_driver #(jtag_item, jtag_agent_cfg);

// drive jtag req and retrieve rsp
virtual task drive_jtag_req(jtag_item req, jtag_item rsp);
cfg.vif.tck_en <= 1'b1;
enable_tck();
if (req.reset_tap_fsm) begin
drive_jtag_test_logic_reset();
end
Expand Down Expand Up @@ -185,7 +221,7 @@ class jtag_driver extends dv_base_driver #(jtag_item, jtag_agent_cfg);
req.dr_pause_cycle,
req.exit_to_rti_dr);
end
cfg.vif.tck_en <= 1'b0;
release_tck();
endtask

task drive_jtag_ir(int len,
Expand Down
4 changes: 4 additions & 0 deletions hw/dv/sv/jtag_dmi_agent/jtag_dmi_reg_frontdoor.sv
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class jtag_dmi_reg_frontdoor extends uvm_reg_frontdoor;
jtag_dtm_reg_block jtag_dtm_ral = jtag_agent_cfg_h.jtag_dtm_ral;
jtag_dmi_op_rsp_e op_rsp;

// Configure the JTAG agent to have a positive run-to-clear length, ensuring that DMI operations
// make it from dmi_jtag to dm_top and back again before TCK stops.
jtag_agent_cfg_h.rtc_length = 4;

// If the JTAG agent is sitting in reset, print a debug message and exit early
if (jtag_agent_cfg_h.in_reset) begin
`uvm_info(`gfn, "DMI CSR req skipped due to reset", UVM_HIGH)
Expand Down

0 comments on commit 7eb9f33

Please sign in to comment.