| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| // | |
| `include "prim_assert.sv" | |
| module aon_timer import aon_timer_reg_pkg::*; | |
| #( | |
| parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}} | |
| ) ( | |
| input logic clk_i, | |
| input logic clk_aon_i, | |
| input logic rst_ni, | |
| input logic rst_aon_ni, | |
| // TLUL interface on clk_i domain | |
| input tlul_pkg::tl_h2d_t tl_i, | |
| output tlul_pkg::tl_d2h_t tl_o, | |
| // Alerts | |
| input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, | |
| output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, | |
| // clk_i domain | |
| input lc_ctrl_pkg::lc_tx_t lc_escalate_en_i, | |
| output logic intr_wkup_timer_expired_o, | |
| output logic intr_wdog_timer_bark_o, | |
| output logic nmi_wdog_timer_bark_o, | |
| // clk_aon_i domain | |
| output logic wkup_req_o, | |
| output logic aon_timer_rst_req_o, | |
| // async domain | |
| input logic sleep_mode_i | |
| ); | |
| localparam int AON_WKUP = 0; | |
| localparam int AON_WDOG = 1; | |
| // Register structs | |
| aon_timer_reg2hw_t reg2hw; | |
| aon_timer_hw2reg_t hw2reg; | |
| // Register write signals | |
| logic aon_wkup_count_reg_wr; | |
| logic [31:0] aon_wkup_count_wr_data; | |
| logic aon_wdog_count_reg_wr; | |
| logic [31:0] aon_wdog_count_wr_data; | |
| // Other sync signals | |
| lc_ctrl_pkg::lc_tx_t [2:0] lc_escalate_en; | |
| // Interrupt signals | |
| logic aon_wkup_intr_set; | |
| logic aon_wdog_intr_set; | |
| logic [1:0] intr_test_q; | |
| logic intr_test_qe; | |
| logic [1:0] intr_state_q; | |
| logic intr_state_de; | |
| logic [1:0] intr_state_d; | |
| logic [1:0] intr_out; | |
| // Reset signals | |
| logic aon_rst_req_set; | |
| logic aon_rst_req_d, aon_rst_req_q; | |
| // Alert signals | |
| logic [NumAlerts-1:0] alert_test, alerts; | |
| ////////////////////////////// | |
| // Register Write Interface // | |
| ////////////////////////////// | |
| logic aon_sleep_mode; | |
| prim_flop_2sync #( | |
| .Width(1) | |
| ) u_sync_sleep_mode ( | |
| .clk_i (clk_aon_i), | |
| .rst_ni (rst_aon_ni), | |
| .d_i (sleep_mode_i), | |
| .q_o (aon_sleep_mode) | |
| ); | |
| assign hw2reg.wkup_count.de = aon_wkup_count_reg_wr; | |
| assign hw2reg.wkup_count.d = aon_wkup_count_wr_data; | |
| assign hw2reg.wdog_count.de = aon_wdog_count_reg_wr; | |
| assign hw2reg.wdog_count.d = aon_wdog_count_wr_data; | |
| // registers instantiation | |
| aon_timer_reg_top u_reg ( | |
| .clk_i, | |
| .rst_ni, | |
| .clk_aon_i, | |
| .rst_aon_ni, | |
| .tl_i, | |
| .tl_o, | |
| .reg2hw, | |
| .hw2reg, | |
| // SEC_CM: BUS.INTEGRITY | |
| .intg_err_o (alerts[0]), | |
| .devmode_i (1'b1) | |
| ); | |
| //////////// | |
| // Alerts // | |
| //////////// | |
| assign alert_test = { | |
| reg2hw.alert_test.q & | |
| reg2hw.alert_test.qe | |
| }; | |
| for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx | |
| prim_alert_sender #( | |
| .AsyncOn(AlertAsyncOn[i]), | |
| .IsFatal(1'b1) | |
| ) u_prim_alert_sender ( | |
| .clk_i, | |
| .rst_ni, | |
| .alert_test_i ( alert_test[i] ), | |
| .alert_req_i ( alerts[0] ), | |
| .alert_ack_o ( ), | |
| .alert_state_o ( ), | |
| .alert_rx_i ( alert_rx_i[i] ), | |
| .alert_tx_o ( alert_tx_o[i] ) | |
| ); | |
| end | |
| // Lifecycle sync | |
| prim_lc_sync #( | |
| .NumCopies(3) | |
| ) u_lc_sync_escalate_en ( | |
| .clk_i (clk_aon_i), | |
| .rst_ni (rst_aon_ni), | |
| .lc_en_i (lc_escalate_en_i), | |
| .lc_en_o (lc_escalate_en) | |
| ); | |
| //////////////// | |
| // Timer Core // | |
| //////////////// | |
| aon_timer_core u_core ( | |
| .clk_aon_i, | |
| .rst_aon_ni, | |
| .sleep_mode_i (aon_sleep_mode), | |
| .lc_escalate_en_i (lc_escalate_en), | |
| .reg2hw_i (reg2hw), | |
| .wkup_count_reg_wr_o (aon_wkup_count_reg_wr), | |
| .wkup_count_wr_data_o (aon_wkup_count_wr_data), | |
| .wdog_count_reg_wr_o (aon_wdog_count_reg_wr), | |
| .wdog_count_wr_data_o (aon_wdog_count_wr_data), | |
| .wkup_intr_o (aon_wkup_intr_set), | |
| .wdog_intr_o (aon_wdog_intr_set), | |
| .wdog_reset_req_o (aon_rst_req_set) | |
| ); | |
| //////////////////// | |
| // Wakeup Signals // | |
| //////////////////// | |
| // Wakeup request is set by HW and cleared by SW | |
| // The wakeup cause is always captured and only sent out when the system has entered sleep mode | |
| assign hw2reg.wkup_cause.de = aon_wkup_intr_set | aon_wdog_intr_set; | |
| assign hw2reg.wkup_cause.d = 1'b1; | |
| // cause register resides in AON domain. | |
| assign wkup_req_o = reg2hw.wkup_cause.q; | |
| // The wakeup signal is not latched in the pwrmgr so must be held until acked by software | |
| `ASSERT(WkupStable_A, wkup_req_o |=> wkup_req_o || | |
| $fell(reg2hw.wkup_cause.q) && !aon_sleep_mode, clk_aon_i, !rst_aon_ni) | |
| //////////////////////// | |
| // Interrupt Handling // | |
| //////////////////////// | |
| logic [1:0] aon_intr_set, intr_set; | |
| prim_flop #( | |
| .Width (2), | |
| .ResetValue (2'b 00) | |
| ) u_aon_intr_flop ( | |
| .clk_i (clk_aon_i), | |
| .rst_ni (rst_aon_ni), | |
| .d_i ({aon_wdog_intr_set, aon_wkup_intr_set}), | |
| .q_o (aon_intr_set) | |
| ); | |
| prim_edge_detector #( | |
| .Width (2), | |
| .ResetValue (2'b 00), | |
| .EnSync (1'b 1) | |
| ) u_intr_sync ( | |
| .clk_i, | |
| .rst_ni, | |
| .d_i (aon_intr_set), | |
| .q_sync_o (), | |
| .q_posedge_pulse_o (intr_set), | |
| .q_negedge_pulse_o () | |
| ); | |
| // Registers to interrupt | |
| assign intr_test_qe = reg2hw.intr_test.wkup_timer_expired.qe | | |
| reg2hw.intr_test.wdog_timer_bark.qe; | |
| assign intr_test_q [AON_WKUP] = reg2hw.intr_test.wkup_timer_expired.q; | |
| assign intr_state_q[AON_WKUP] = reg2hw.intr_state.wkup_timer_expired.q; | |
| assign intr_test_q [AON_WDOG] = reg2hw.intr_test.wdog_timer_bark.q; | |
| assign intr_state_q[AON_WDOG] = reg2hw.intr_state.wdog_timer_bark.q; | |
| // Interrupts to registers | |
| assign hw2reg.intr_state.wkup_timer_expired.d = intr_state_d[AON_WKUP]; | |
| assign hw2reg.intr_state.wkup_timer_expired.de = intr_state_de; | |
| assign hw2reg.intr_state.wdog_timer_bark.d = intr_state_d[AON_WDOG]; | |
| assign hw2reg.intr_state.wdog_timer_bark.de = intr_state_de; | |
| prim_intr_hw #( | |
| .Width (2) | |
| ) u_intr_hw ( | |
| .clk_i, | |
| .rst_ni, | |
| .event_intr_i (intr_set), | |
| .reg2hw_intr_enable_q_i (2'b11), | |
| .reg2hw_intr_test_q_i (intr_test_q), | |
| .reg2hw_intr_test_qe_i (intr_test_qe), | |
| .reg2hw_intr_state_q_i (intr_state_q), | |
| .hw2reg_intr_state_de_o (intr_state_de), | |
| .hw2reg_intr_state_d_o (intr_state_d), | |
| .intr_o (intr_out) | |
| ); | |
| assign intr_wkup_timer_expired_o = intr_out[AON_WKUP]; | |
| assign intr_wdog_timer_bark_o = intr_out[AON_WDOG]; | |
| // The interrupt line can be routed as nmi as well. | |
| assign nmi_wdog_timer_bark_o = intr_wdog_timer_bark_o; | |
| /////////////////// | |
| // Reset Request // | |
| /////////////////// | |
| // Once set, the reset request remains asserted until the next aon reset | |
| assign aon_rst_req_d = aon_rst_req_set | aon_rst_req_q; | |
| always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin | |
| if (!rst_aon_ni) begin | |
| aon_rst_req_q <= 1'b0; | |
| end else begin | |
| aon_rst_req_q <= aon_rst_req_d; | |
| end | |
| end | |
| assign aon_timer_rst_req_o = aon_rst_req_q; | |
| ///////////////////////////// | |
| // Assert Known on Outputs // | |
| ///////////////////////////// | |
| // clk_i domain | |
| `ASSERT_KNOWN(TlODValidKnown_A, tl_o.d_valid) | |
| `ASSERT_KNOWN(TlOAReadyKnown_A, tl_o.a_ready) | |
| `ASSERT_KNOWN(AlertsKnown_A, alert_tx_o) | |
| `ASSERT_KNOWN(IntrWkupKnown_A, intr_wkup_timer_expired_o) | |
| `ASSERT_KNOWN(IntrWdogKnown_A, intr_wdog_timer_bark_o) | |
| // clk_aon_i domain | |
| `ASSERT_KNOWN(WkupReqKnown_A, wkup_req_o, clk_aon_i, !rst_aon_ni) | |
| `ASSERT_KNOWN(RstReqKnown_A, aon_timer_rst_req_o, clk_aon_i, !rst_aon_ni) | |
| // Alert assertions for reg_we onehot check | |
| `ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[0]) | |
| endmodule |