From 7eb9f33f6d43c3b05d3c791e97617a8b4fe534cc Mon Sep 17 00:00:00 2001 From: Rupert Swarbrick Date: Thu, 20 Jun 2024 14:31:58 +0100 Subject: [PATCH] [jtag,dv] Keep TCK running a short time after a transaction 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 --- hw/dv/sv/jtag_agent/jtag_agent_cfg.sv | 10 +++++ hw/dv/sv/jtag_agent/jtag_driver.sv | 44 +++++++++++++++++-- .../jtag_dmi_agent/jtag_dmi_reg_frontdoor.sv | 4 ++ 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/hw/dv/sv/jtag_agent/jtag_agent_cfg.sv b/hw/dv/sv/jtag_agent/jtag_agent_cfg.sv index 8ddfc46fcf1d8..79a1b27e24aa2 100644 --- a/hw/dv/sv/jtag_agent/jtag_agent_cfg.sv +++ b/hw/dv/sv/jtag_agent/jtag_agent_cfg.sv @@ -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 diff --git a/hw/dv/sv/jtag_agent/jtag_driver.sv b/hw/dv/sv/jtag_agent/jtag_driver.sv index 7b50a4b0763aa..64992aba10c8c 100644 --- a/hw/dv/sv/jtag_agent/jtag_driver.sv +++ b/hw/dv/sv/jtag_agent/jtag_driver.sv @@ -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 @@ -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 @@ -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);, @@ -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 @@ -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 @@ -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, diff --git a/hw/dv/sv/jtag_dmi_agent/jtag_dmi_reg_frontdoor.sv b/hw/dv/sv/jtag_dmi_agent/jtag_dmi_reg_frontdoor.sv index 1d82d9dd19ff5..83802fd56692a 100644 --- a/hw/dv/sv/jtag_dmi_agent/jtag_dmi_reg_frontdoor.sv +++ b/hw/dv/sv/jtag_dmi_agent/jtag_dmi_reg_frontdoor.sv @@ -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)