From d4f08e1cf22c6c225651db9fd31fae643ff1418b Mon Sep 17 00:00:00 2001 From: Michael Schaffner Date: Thu, 14 Mar 2024 13:22:38 -0700 Subject: [PATCH] [rv_dm] Implement NDM reset tracking This adds tracking logic for NDM reset requests and issues an acknowledgement to the DM_CSRs so that dmstatus.allhavereset / dmstatus.anyhavereset can be asserted at the correct time. Fixes #15123 Signed-off-by: Michael Schaffner --- hw/ip/rv_dm/data/rv_dm.hjson | 5 +- hw/ip/rv_dm/dv/tb.sv | 6 +- hw/ip/rv_dm/rtl/rv_dm.sv | 78 +++++++++++++++++-- .../data/autogen/top_earlgrey.gen.hjson | 7 ++ hw/top_earlgrey/data/top_earlgrey.hjson | 4 +- hw/top_earlgrey/rtl/autogen/top_earlgrey.sv | 4 +- 6 files changed, 94 insertions(+), 10 deletions(-) diff --git a/hw/ip/rv_dm/data/rv_dm.hjson b/hw/ip/rv_dm/data/rv_dm.hjson index 6fee2595ae21c1..f30a3dbfa51da4 100644 --- a/hw/ip/rv_dm/data/rv_dm.hjson +++ b/hw/ip/rv_dm/data/rv_dm.hjson @@ -21,7 +21,10 @@ design_stage: "D2S", verification_stage: "V1", clocking: [ - {clock: "clk_i", reset: "rst_ni"} + {clock: "clk_i", reset: "rst_ni", primary: true}, + // Ideally, this is the processor clock and reset. + // Note that only the reset input is used here for NDM reset request tracking. + {clock: "clk_lc_i", reset: "rst_lc_ni"} ] bus_interfaces: [ { protocol: "tlul", direction: "host", name: "sba" } diff --git a/hw/ip/rv_dm/dv/tb.sv b/hw/ip/rv_dm/dv/tb.sv index de7c42b8af0c7e..d4b854f560c023 100644 --- a/hw/ip/rv_dm/dv/tb.sv +++ b/hw/ip/rv_dm/dv/tb.sv @@ -32,7 +32,11 @@ module tb; ) dut ( .clk_i (clk ), .rst_ni (rst_n), - + // TODO: this should be attached to another reset that can be driven low separately from rst_n. + // It is used for tracking NDM reset requests internally (the clock input is unused, but + // required so that the topgen tooling works correctly). + .clk_lc_i (clk ), + .rst_lc_ni (rst_n), // the differing behavior of lc_hw_debug_en_i and pinmux_hw_debug_en_i // will be tested at the top-level. for the purposes of this TB we connect // both signals to the same life cycle signal. diff --git a/hw/ip/rv_dm/rtl/rv_dm.sv b/hw/ip/rv_dm/rtl/rv_dm.sv index afa0f5b4efb50f..30af79e1247a1b 100644 --- a/hw/ip/rv_dm/rtl/rv_dm.sv +++ b/hw/ip/rv_dm/rtl/rv_dm.sv @@ -19,8 +19,12 @@ module rv_dm parameter logic [31:0] IdcodeValue = 32'h 0000_0001 ) ( input logic clk_i, // clock + input logic clk_lc_i, // only declared here so that the topgen + // tooling connects the correct clk/rst domains. input logic rst_ni, // asynchronous reset active low, connect PoR // here, not the system reset + input logic rst_lc_ni, // asynchronous reset active low, connect the lc + // reset here. this is only used for NDM reset tracking. input logic [31:0] next_dm_addr_i, // static word address of the next debug module. // SEC_CM: LC_HW_DEBUG_EN.INTERSIG.MUBI // HW Debug lifecycle enable signal (live version from the life cycle controller) @@ -269,16 +273,80 @@ module rv_dm logic dmi_rsp_valid, dmi_rsp_ready; logic dmi_rst_n; + logic dmi_en; + // SEC_CM: DM_EN.CTRL.LC_GATED + assign dmi_en = lc_tx_test_true_strict(pinmux_hw_debug_en[PmEnDmiReq]); + + //////////////////////// + // NDM Reset Tracking // + //////////////////////// + logic reset_req_en; - logic ndmreset_req; + logic ndmreset_req, ndmreset_ack; logic ndmreset_req_qual; // SEC_CM: DM_EN.CTRL.LC_GATED assign reset_req_en = lc_tx_test_true_strict(lc_hw_debug_en_gated[LcEnResetReq]); assign ndmreset_req_o = ndmreset_req_qual & reset_req_en; - logic dmi_en; - // SEC_CM: DM_EN.CTRL.LC_GATED - assign dmi_en = lc_tx_test_true_strict(pinmux_hw_debug_en[PmEnDmiReq]); + // Sample the processor reset to detect lc reset assertion. + logic lc_rst_asserted_async; + prim_flop_2sync #( + .Width(1), + .ResetValue(1) // Resets to 1 to indicate assertion. + ) u_prim_flop_2sync_lc_rst_assert ( + .clk_i, // Use RV_DM clock + .rst_ni(rst_lc_ni), // Use LC reset here that resets the entire system except the RV_DM. + .d_i(1'b0), // Set to 0 to indicate deassertion. + .q_o(lc_rst_asserted_async) + ); + + // Note that the output of the above flops can be metastable at reset assertion, since the reset + // signal is coming from a different clock domain and has not been synchronized with clk_i. + logic lc_rst_asserted; + prim_flop_2sync #( + .Width(1) + ) u_prim_flop_2sync_lc_rst_sync ( + .clk_i, + .rst_ni, + .d_i(lc_rst_asserted_async), + .q_o(lc_rst_asserted) + ); + + // The acknowledgement pulse sets the dmstatus.allhavereset / dmstatus.anyhavereset registers in + // RV_DM. It should only be asserted once an NDM reset request has been fully completed. + logic ndmreset_pending_q; + logic lc_rst_pending_q; + always_ff @(posedge clk_i or negedge rst_ni) begin : p_ndm_reset + if (!rst_ni) begin + ndmreset_pending_q <= 1'b0; + lc_rst_pending_q <= 1'b0; + end else begin + // Only set this if there was no previous pending NDM request. + if (ndmreset_req && !ndmreset_pending_q) begin + ndmreset_pending_q <= 1'b1; + end else if (ndmreset_ack && ndmreset_pending_q) begin + ndmreset_pending_q <= 1'b0; + end + // We only track lc resets that are asserted during an active ndm reset request.. + if (ndmreset_pending_q && lc_rst_asserted) begin + lc_rst_pending_q <= 1'b1; + end else if (ndmreset_ack && lc_rst_pending_q) begin + lc_rst_pending_q <= 1'b0; + end + end + end + + // In order to ACK the following conditions must be met + // 1) an NDM reset request was asserted and is pending + // 2) a lc reset was asserted after the NDM reset request + // 3) the NDM reset request was deasserted + // 4) the NDM lc request was deasserted + // 5) the debug module has been ungated for operation (depending on LC state, OTP config and CSR) + assign ndmreset_ack = ndmreset_pending_q && + lc_rst_pending_q && + !ndmreset_req && + !lc_rst_asserted && + reset_req_en; ///////////////////////////////////////// // System Bus Access Port (TL-UL Host) // @@ -502,7 +570,7 @@ module rv_dm .next_dm_addr_i, .testmode_i (testmode ), .ndmreset_o (ndmreset_req ), - .ndmreset_ack_i (ndmreset_req ), + .ndmreset_ack_i (ndmreset_ack ), .dmactive_o, .debug_req_o (debug_req ), .unavailable_i, diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson index d085ddb7c9b130..eeb8885ed79d12 100644 --- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson +++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson @@ -5166,6 +5166,7 @@ clock_srcs: { clk_i: main + clk_lc_i: main } clock_group: infra reset_connections: @@ -5175,6 +5176,11 @@ name: sys domain: "0" } + rst_lc_ni: + { + name: lc + domain: "0" + } } param_decl: { @@ -5188,6 +5194,7 @@ clock_connections: { clk_i: clkmgr_aon_clocks.clk_main_infra + clk_lc_i: clkmgr_aon_clocks.clk_main_infra } domain: [ diff --git a/hw/top_earlgrey/data/top_earlgrey.hjson b/hw/top_earlgrey/data/top_earlgrey.hjson index 74856d84b97602..b1b4deccd110ff 100644 --- a/hw/top_earlgrey/data/top_earlgrey.hjson +++ b/hw/top_earlgrey/data/top_earlgrey.hjson @@ -620,9 +620,9 @@ }, { name: "rv_dm", type: "rv_dm", - clock_srcs: {clk_i: "main"}, + clock_srcs: {clk_i: "main", clk_lc_i: "main"}, clock_group: "infra", - reset_connections: {rst_ni: "sys"}, + reset_connections: {rst_ni: "sys", rst_lc_ni: "lc"}, param_decl: { IdcodeValue: "jtag_id_pkg::RV_DM_JTAG_IDCODE", }, diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv index 473c2355aa58ba..4aa9dfe39f6533 100644 --- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv +++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv @@ -2175,7 +2175,9 @@ module top_earlgrey #( // Clock and reset connections .clk_i (clkmgr_aon_clocks.clk_main_infra), - .rst_ni (rstmgr_aon_resets.rst_sys_n[rstmgr_pkg::Domain0Sel]) + .clk_lc_i (clkmgr_aon_clocks.clk_main_infra), + .rst_ni (rstmgr_aon_resets.rst_sys_n[rstmgr_pkg::Domain0Sel]), + .rst_lc_ni (rstmgr_aon_resets.rst_lc_n[rstmgr_pkg::Domain0Sel]) ); rv_plic #( .AlertAsyncOn(alert_handler_reg_pkg::AsyncOn[41:41])