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)