|  | // 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 ( | 
|  | 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, | 
|  |  | 
|  | // clk_i domain | 
|  | input  lc_ctrl_pkg::lc_tx_t lc_cpu_en_i, | 
|  | output logic                intr_wkup_timer_expired_o, | 
|  | output logic                intr_wdog_timer_bark_o, | 
|  |  | 
|  | // clk_aon_i domain | 
|  | output logic                aon_timer_wkup_req_o, | 
|  | output logic                aon_timer_rst_req_o, | 
|  |  | 
|  | // async domain | 
|  | input  logic                sleep_mode_i | 
|  | ); | 
|  |  | 
|  | import aon_timer_reg_pkg::*; | 
|  |  | 
|  | localparam int AON_WKUP = 0; | 
|  | localparam int AON_WDOG = 1; | 
|  |  | 
|  | // Register structs | 
|  | aon_timer_reg2hw_t         reg2hw; | 
|  | aon_timer_hw2reg_t         hw2reg, aon_hw2reg, hw2reg_sync; | 
|  | logic                      unused_intr_state_bits; | 
|  | // Register read signals | 
|  | logic                      wkup_enable; | 
|  | logic [11:0]               wkup_prescaler; | 
|  | logic [31:0]               wkup_thold; | 
|  | logic [31:0]               wkup_count; | 
|  | logic                      wdog_enable; | 
|  | logic                      wdog_pause; | 
|  | logic [31:0]               wdog_bark_thold; | 
|  | logic [31:0]               wdog_bite_thold; | 
|  | logic [31:0]               wdog_count; | 
|  | // Register write signals | 
|  | logic                      wkup_ctrl_reg_wr; | 
|  | logic [12:0]               wkup_ctrl_wr_data; | 
|  | logic                      wkup_thold_reg_wr; | 
|  | logic [31:0]               wkup_thold_wr_data; | 
|  | logic                      wkup_count_reg_wr; | 
|  | logic [31:0]               wkup_count_wr_data; | 
|  | logic                      wdog_ctrl_reg_wr; | 
|  | logic [1:0]                wdog_ctrl_wr_data; | 
|  | logic                      wdog_bark_thold_reg_wr; | 
|  | logic [31:0]               wdog_bark_thold_wr_data; | 
|  | logic                      wdog_bite_thold_reg_wr; | 
|  | logic [31:0]               wdog_bite_thold_wr_data; | 
|  | logic                      wdog_count_reg_wr; | 
|  | logic [31:0]               wdog_count_wr_data; | 
|  | // Other sync signals | 
|  | lc_ctrl_pkg::lc_tx_t [2:0] lc_cpu_en; | 
|  | // Wakeup signals | 
|  | logic                      aon_wkup_req_d, aon_wkup_req_q; | 
|  | logic                      wkup_ack, aon_wkup_ack; | 
|  | // Interrupt signals | 
|  | logic                      aon_wkup_intr_set, wkup_intr_set; | 
|  | logic                      aon_wdog_intr_set, wdog_intr_set; | 
|  | logic [1:0]                intr_aon_test_q; | 
|  | logic                      intr_aon_test_qe; | 
|  | logic [1:0]                intr_aon_state_q; | 
|  | logic                      intr_aon_state_de; | 
|  | logic [1:0]                intr_aon_state_d; | 
|  | logic [1:0]                intr_out; | 
|  | // Reset signals | 
|  | logic                      aon_rst_req_set; | 
|  | logic                      aon_rst_req_d, aon_rst_req_q; | 
|  |  | 
|  | //////////////////////////// | 
|  | // Register Read Sampling // | 
|  | //////////////////////////// | 
|  |  | 
|  | assign aon_hw2reg.wkup_ctrl.enable.d         = wkup_enable; | 
|  | assign aon_hw2reg.wkup_ctrl.prescaler.d      = wkup_prescaler; | 
|  | assign aon_hw2reg.wkup_thold.d               = wkup_thold; | 
|  | assign aon_hw2reg.wkup_count.d               = wkup_count; | 
|  | assign aon_hw2reg.wdog_ctrl.enable.d         = wdog_enable; | 
|  | assign aon_hw2reg.wdog_ctrl.pause_in_sleep.d = wdog_pause; | 
|  | assign aon_hw2reg.wdog_bark_thold.d          = wdog_bark_thold; | 
|  | assign aon_hw2reg.wdog_bite_thold.d          = wdog_bite_thold; | 
|  | assign aon_hw2reg.wdog_count.d               = wdog_count; | 
|  | assign aon_hw2reg.wkup_cause.d               = aon_wkup_req_q; | 
|  | assign aon_hw2reg.intr_state                 = '0; // Doesn't come from AON domain | 
|  |  | 
|  | // Register read values sampled into clk_i domain. These are sampled with a special slow to fast | 
|  | // synchronizer which captures the value on the negative edge of the slow clock. | 
|  | prim_sync_slow_fast #(.Width ($bits(aon_hw2reg))) wkup_ctrl_enable_rd_sync ( | 
|  | .clk_slow_i  (clk_aon_i), | 
|  | .clk_fast_i  (clk_i), | 
|  | .rst_fast_ni (rst_ni), | 
|  | .wdata_i     (aon_hw2reg), | 
|  | .rdata_o     (hw2reg_sync) | 
|  | ); | 
|  |  | 
|  | assign hw2reg.wkup_ctrl.enable.d         = hw2reg_sync.wkup_ctrl.enable.d; | 
|  | assign hw2reg.wkup_ctrl.prescaler.d      = hw2reg_sync.wkup_ctrl.prescaler.d; | 
|  | assign hw2reg.wkup_thold.d               = hw2reg_sync.wkup_thold.d; | 
|  | assign hw2reg.wkup_count.d               = hw2reg_sync.wkup_count.d; | 
|  | assign hw2reg.wdog_ctrl.enable.d         = hw2reg_sync.wdog_ctrl.enable.d; | 
|  | assign hw2reg.wdog_ctrl.pause_in_sleep.d = hw2reg_sync.wdog_ctrl.pause_in_sleep.d; | 
|  | assign hw2reg.wdog_bark_thold.d          = hw2reg_sync.wdog_bark_thold.d; | 
|  | assign hw2reg.wdog_bite_thold.d          = hw2reg_sync.wdog_bite_thold.d; | 
|  | assign hw2reg.wdog_count.d               = hw2reg_sync.wdog_count.d; | 
|  | assign hw2reg.wkup_cause.d               = hw2reg_sync.wkup_cause.d; | 
|  | assign unused_intr_state_bits            = &{1'b0, hw2reg_sync.intr_state.wkup_timer_expired.d, | 
|  | hw2reg_sync.intr_state.wkup_timer_expired.de, | 
|  | hw2reg_sync.intr_state.wdog_timer_expired.d, | 
|  | hw2reg_sync.intr_state.wdog_timer_expired.de}; | 
|  |  | 
|  | ////////////////////////////// | 
|  | // Register Write Interface // | 
|  | ////////////////////////////// | 
|  |  | 
|  | // Register write data needs to be synchronized to make sure we capture sensible values. | 
|  | // TODO This would probaby make more sense as a single fifo for all registers, but can't really | 
|  | // achieve that at the moment with current register tooling. | 
|  | // TODO An alternative improvement would be to fix the async fifo to allow depth < 4 | 
|  | // Note: these are fast clock to slow clock transfers | 
|  |  | 
|  | // wkup_ctrl | 
|  | prim_fifo_async #(.Width (13), .Depth (4)) wkup_ctrl_wr_data_sync ( | 
|  | .clk_wr_i  (clk_i), | 
|  | .rst_wr_ni (rst_ni), | 
|  | .wvalid_i  (reg2hw.wkup_ctrl.prescaler.qe | reg2hw.wkup_ctrl.enable.qe), | 
|  | .wready_o  (), // TODO no way of feeding this back to TLUL currently | 
|  | .wdata_i   ({reg2hw.wkup_ctrl.prescaler.q, reg2hw.wkup_ctrl.enable.q}), | 
|  | .wdepth_o  (), | 
|  | .clk_rd_i  (clk_aon_i), | 
|  | .rst_rd_ni (rst_aon_ni), | 
|  | .rvalid_o  (wkup_ctrl_reg_wr), | 
|  | .rready_i  (1'b1), | 
|  | .rdata_o   (wkup_ctrl_wr_data), | 
|  | .rdepth_o  ()); | 
|  |  | 
|  | // wkup_thold | 
|  | prim_fifo_async #(.Width (32), .Depth (4)) wkup_thold_wr_data_sync ( | 
|  | .clk_wr_i  (clk_i), | 
|  | .rst_wr_ni (rst_ni), | 
|  | .wvalid_i  (reg2hw.wkup_thold.qe), | 
|  | .wready_o  (), // TODO no way of feeding this back to TLUL currently | 
|  | .wdata_i   (reg2hw.wkup_thold.q), | 
|  | .wdepth_o  (), | 
|  | .clk_rd_i  (clk_aon_i), | 
|  | .rst_rd_ni (rst_aon_ni), | 
|  | .rvalid_o  (wkup_thold_reg_wr), | 
|  | .rready_i  (1'b1), | 
|  | .rdata_o   (wkup_thold_wr_data), | 
|  | .rdepth_o  ()); | 
|  |  | 
|  | // wkup_count | 
|  | prim_fifo_async #(.Width (32), .Depth (4)) wkup_count_wr_data_sync ( | 
|  | .clk_wr_i  (clk_i), | 
|  | .rst_wr_ni (rst_ni), | 
|  | .wvalid_i  (reg2hw.wkup_count.qe), | 
|  | .wready_o  (), // TODO no way of feeding this back to TLUL currently | 
|  | .wdata_i   (reg2hw.wkup_count.q), | 
|  | .wdepth_o  (), | 
|  | .clk_rd_i  (clk_aon_i), | 
|  | .rst_rd_ni (rst_aon_ni), | 
|  | .rvalid_o  (wkup_count_reg_wr), | 
|  | .rready_i  (1'b1), | 
|  | .rdata_o   (wkup_count_wr_data), | 
|  | .rdepth_o  ()); | 
|  |  | 
|  | // wdog_ctrl | 
|  | prim_fifo_async #(.Width (2), .Depth (4)) wdog_ctrl_wr_data_sync ( | 
|  | .clk_wr_i  (clk_i), | 
|  | .rst_wr_ni (rst_ni), | 
|  | .wvalid_i  (reg2hw.wdog_ctrl.pause_in_sleep.qe | reg2hw.wdog_ctrl.enable.qe), | 
|  | .wready_o  (), // TODO no way of feeding this back to TLUL currently | 
|  | .wdata_i   ({reg2hw.wdog_ctrl.pause_in_sleep.q, reg2hw.wdog_ctrl.enable.q}), | 
|  | .wdepth_o  (), | 
|  | .clk_rd_i  (clk_aon_i), | 
|  | .rst_rd_ni (rst_aon_ni), | 
|  | .rvalid_o  (wdog_ctrl_reg_wr), | 
|  | .rready_i  (1'b1), | 
|  | .rdata_o   (wdog_ctrl_wr_data), | 
|  | .rdepth_o  ()); | 
|  |  | 
|  | // wdog_bark_thold | 
|  | prim_fifo_async #(.Width (32), .Depth (4)) wdog_bark_thold_wr_data_sync ( | 
|  | .clk_wr_i  (clk_i), | 
|  | .rst_wr_ni (rst_ni), | 
|  | .wvalid_i  (reg2hw.wdog_bark_thold.qe), | 
|  | .wready_o  (), // TODO no way of feeding this back to TLUL currently | 
|  | .wdata_i   (reg2hw.wdog_bark_thold.q), | 
|  | .wdepth_o  (), | 
|  | .clk_rd_i  (clk_aon_i), | 
|  | .rst_rd_ni (rst_aon_ni), | 
|  | .rvalid_o  (wdog_bark_thold_reg_wr), | 
|  | .rready_i  (1'b1), | 
|  | .rdata_o   (wdog_bark_thold_wr_data), | 
|  | .rdepth_o  ()); | 
|  |  | 
|  | // wdog_bite_thold | 
|  | prim_fifo_async #(.Width (32), .Depth (4)) wdog_bite_thold_wr_data_sync ( | 
|  | .clk_wr_i  (clk_i), | 
|  | .rst_wr_ni (rst_ni), | 
|  | .wvalid_i  (reg2hw.wdog_bite_thold.qe), | 
|  | .wready_o  (), // TODO no way of feeding this back to TLUL currently | 
|  | .wdata_i   (reg2hw.wdog_bite_thold.q), | 
|  | .wdepth_o  (), | 
|  | .clk_rd_i  (clk_aon_i), | 
|  | .rst_rd_ni (rst_aon_ni), | 
|  | .rvalid_o  (wdog_bite_thold_reg_wr), | 
|  | .rready_i  (1'b1), | 
|  | .rdata_o   (wdog_bite_thold_wr_data), | 
|  | .rdepth_o  ()); | 
|  |  | 
|  | // wdog_count | 
|  | prim_fifo_async #(.Width (32), .Depth (4)) wdog_count_wr_data_sync ( | 
|  | .clk_wr_i  (clk_i), | 
|  | .rst_wr_ni (rst_ni), | 
|  | .wvalid_i  (reg2hw.wdog_count.qe), | 
|  | .wready_o  (), // TODO no way of feeding this back to TLUL currently | 
|  | .wdata_i   (reg2hw.wdog_count.q), | 
|  | .wdepth_o  (), | 
|  | .clk_rd_i  (clk_aon_i), | 
|  | .rst_rd_ni (rst_aon_ni), | 
|  | .rvalid_o  (wdog_count_reg_wr), | 
|  | .rready_i  (1'b1), | 
|  | .rdata_o   (wdog_count_wr_data), | 
|  | .rdepth_o  ()); | 
|  |  | 
|  | // registers instantiation | 
|  | aon_timer_reg_top u_reg ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  |  | 
|  | .tl_i, | 
|  | .tl_o, | 
|  |  | 
|  | .reg2hw, | 
|  | .hw2reg, | 
|  |  | 
|  | .intg_err_o (), | 
|  | .devmode_i  (1'b1) | 
|  | ); | 
|  |  | 
|  | // Lifecycle sync | 
|  | prim_lc_sync #( | 
|  | .NumCopies(3) | 
|  | ) u_lc_sync_cpu_en ( | 
|  | .clk_i   (clk_aon_i), | 
|  | .rst_ni  (rst_aon_ni), | 
|  | .lc_en_i (lc_cpu_en_i), | 
|  | .lc_en_o (lc_cpu_en) | 
|  | ); | 
|  |  | 
|  | //////////////// | 
|  | // Timer Core // | 
|  | //////////////// | 
|  |  | 
|  | logic 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     (sleep_mode) | 
|  | ); | 
|  |  | 
|  | aon_timer_core u_core ( | 
|  | .clk_aon_i, | 
|  | .rst_aon_ni, | 
|  | .sleep_mode_i              (sleep_mode), | 
|  | .lc_cpu_en_i               (lc_cpu_en), | 
|  | .wkup_enable_o             (wkup_enable), | 
|  | .wkup_prescaler_o          (wkup_prescaler), | 
|  | .wkup_thold_o              (wkup_thold), | 
|  | .wkup_count_o              (wkup_count), | 
|  | .wdog_enable_o             (wdog_enable), | 
|  | .wdog_pause_o              (wdog_pause), | 
|  | .wdog_bark_thold_o         (wdog_bark_thold), | 
|  | .wdog_bite_thold_o         (wdog_bite_thold), | 
|  | .wdog_count_o              (wdog_count), | 
|  | .wkup_ctrl_reg_wr_i        (wkup_ctrl_reg_wr), | 
|  | .wkup_ctrl_wr_data_i       (wkup_ctrl_wr_data), | 
|  | .wkup_thold_reg_wr_i       (wkup_thold_reg_wr), | 
|  | .wkup_thold_wr_data_i      (wkup_thold_wr_data), | 
|  | .wkup_count_reg_wr_i       (wkup_count_reg_wr), | 
|  | .wkup_count_wr_data_i      (wkup_count_wr_data), | 
|  | .wdog_ctrl_reg_wr_i        (wdog_ctrl_reg_wr), | 
|  | .wdog_ctrl_wr_data_i       (wdog_ctrl_wr_data), | 
|  | .wdog_bark_thold_reg_wr_i  (wdog_bark_thold_reg_wr), | 
|  | .wdog_bark_thold_wr_data_i (wdog_bark_thold_wr_data), | 
|  | .wdog_bite_thold_reg_wr_i  (wdog_bite_thold_reg_wr), | 
|  | .wdog_bite_thold_wr_data_i (wdog_bite_thold_wr_data), | 
|  | .wdog_count_reg_wr_i       (wdog_count_reg_wr), | 
|  | .wdog_count_wr_data_i      (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 signal remains high until acked by software | 
|  | assign aon_wkup_req_d = aon_wkup_intr_set | aon_wdog_intr_set | (aon_wkup_req_q & ~aon_wkup_ack); | 
|  |  | 
|  | always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin | 
|  | if (!rst_aon_ni) begin | 
|  | aon_wkup_req_q <= 1'b0; | 
|  | end else begin | 
|  | aon_wkup_req_q <= aon_wkup_req_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Wakeup request is cleared by SW writing zero | 
|  | assign wkup_ack = reg2hw.wkup_cause.qe & ~reg2hw.wkup_cause.q; | 
|  |  | 
|  | prim_pulse_sync wkup_ack_sync ( | 
|  | .clk_src_i   (clk_i), | 
|  | .rst_src_ni  (rst_ni), | 
|  | .src_pulse_i (wkup_ack), | 
|  | .clk_dst_i   (clk_aon_i), | 
|  | .rst_dst_ni  (rst_aon_ni), | 
|  | .dst_pulse_o (aon_wkup_ack) | 
|  | ); | 
|  |  | 
|  | assign aon_timer_wkup_req_o = aon_wkup_req_q; | 
|  |  | 
|  | //////////////////////// | 
|  | // Interrupt Handling // | 
|  | //////////////////////// | 
|  |  | 
|  | // Synchronize the interrupt pulses from the counters into the clk_i domain. | 
|  | prim_pulse_sync wkup_intr_req_sync ( | 
|  | .clk_src_i   (clk_aon_i), | 
|  | .rst_src_ni  (rst_aon_ni), | 
|  | .src_pulse_i (aon_wkup_intr_set), | 
|  | .clk_dst_i   (clk_i), | 
|  | .rst_dst_ni  (rst_ni), | 
|  | .dst_pulse_o (wkup_intr_set) | 
|  | ); | 
|  |  | 
|  | prim_pulse_sync wdog_intr_req_sync ( | 
|  | .clk_src_i   (clk_aon_i), | 
|  | .rst_src_ni  (rst_aon_ni), | 
|  | .src_pulse_i (aon_wdog_intr_set), | 
|  | .clk_dst_i   (clk_i), | 
|  | .rst_dst_ni  (rst_ni), | 
|  | .dst_pulse_o (wdog_intr_set) | 
|  | ); | 
|  |  | 
|  | // Registers to interrupt | 
|  | assign intr_aon_test_qe           = reg2hw.intr_test.wkup_timer_expired.qe | | 
|  | reg2hw.intr_test.wdog_timer_expired.qe; | 
|  | assign intr_aon_test_q [AON_WKUP] = reg2hw.intr_test.wkup_timer_expired.q; | 
|  | assign intr_aon_state_q[AON_WKUP] = reg2hw.intr_state.wkup_timer_expired.q; | 
|  | assign intr_aon_test_q [AON_WDOG] = reg2hw.intr_test.wdog_timer_expired.q; | 
|  | assign intr_aon_state_q[AON_WDOG] = reg2hw.intr_state.wdog_timer_expired.q; | 
|  |  | 
|  | // Interrupts to registers | 
|  | assign hw2reg.intr_state.wkup_timer_expired.d  = intr_aon_state_d[AON_WKUP]; | 
|  | assign hw2reg.intr_state.wkup_timer_expired.de = intr_aon_state_de; | 
|  | assign hw2reg.intr_state.wdog_timer_expired.d  = intr_aon_state_d[AON_WDOG]; | 
|  | assign hw2reg.intr_state.wdog_timer_expired.de = intr_aon_state_de; | 
|  |  | 
|  | prim_intr_hw #( | 
|  | .Width (2) | 
|  | ) u_intr_hw ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .event_intr_i           ({wdog_intr_set, wkup_intr_set}), | 
|  |  | 
|  | .reg2hw_intr_enable_q_i (2'b11), | 
|  | .reg2hw_intr_test_q_i   (intr_aon_test_q), | 
|  | .reg2hw_intr_test_qe_i  (intr_aon_test_qe), | 
|  | .reg2hw_intr_state_q_i  (intr_aon_state_q), | 
|  | .hw2reg_intr_state_de_o (intr_aon_state_de), | 
|  | .hw2reg_intr_state_d_o  (intr_aon_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]; | 
|  |  | 
|  | /////////////////// | 
|  | // 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(IntrWkupKnown_A, intr_wkup_timer_expired_o) | 
|  | `ASSERT_KNOWN(IntrWdogKnown_A, intr_wdog_timer_bark_o) | 
|  | // clk_aon_i domain | 
|  | `ASSERT_KNOWN(WkupReqKnown_A, aon_timer_wkup_req_o, clk_aon_i, !rst_aon_ni) | 
|  | `ASSERT_KNOWN(RstReqKnown_A, aon_timer_rst_req_o, clk_aon_i, !rst_aon_ni) | 
|  |  | 
|  | endmodule |