| // Copyright lowRISC contributors. | 
 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | module pinmux_wkup import pinmux_pkg::*; import pinmux_reg_pkg::*; #( | 
 |   parameter int Cycles = 4 | 
 | ) ( | 
 |   input                    clk_i, | 
 |   input                    rst_ni, | 
 |   // Always on clock / reset | 
 |   input                    clk_aon_i, | 
 |   input                    rst_aon_ni, | 
 |   // These signals get synchronized to the | 
 |   // slow AON clock within this module. | 
 |   // Note that wkup_en_i is assumed to be level encoded. | 
 |   input                    wkup_en_i, | 
 |   input                    filter_en_i, | 
 |   input wkup_mode_e        wkup_mode_i, | 
 |   input [WkupCntWidth-1:0] wkup_cnt_th_i, | 
 |   input                    pin_value_i, | 
 |   // Signals to/from cause register. | 
 |   // They are synched to/from the AON clock internally | 
 |   input                    wkup_cause_valid_i, | 
 |   input                    wkup_cause_data_i, | 
 |   output                   wkup_cause_data_o, | 
 |   // This signal is running on the AON clock | 
 |   // and is held high as long as the cause register | 
 |   // has not been cleared. | 
 |   output logic             aon_wkup_req_o | 
 | ); | 
 |  | 
 |   /////////////////////////// | 
 |   // Input Synchronization // | 
 |   /////////////////////////// | 
 |  | 
 |   // Synchronize configuration to slow clock | 
 |   wkup_mode_e aon_wkup_mode_q; | 
 |   logic aon_filter_en_q; | 
 |   logic aon_wkup_en_d, aon_wkup_en_q; | 
 |   logic [WkupCntWidth-1:0] aon_wkup_cnt_th_q; | 
 |  | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_prim_flop_2sync_config ( | 
 |     .clk_i  ( clk_aon_i      ), | 
 |     .rst_ni ( rst_aon_ni     ), | 
 |     .d      ( wkup_en_i     ), | 
 |     .q      ( aon_wkup_en_d ) | 
 |   ); | 
 |  | 
 |   always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin : p_sync | 
 |     if (!rst_aon_ni) begin | 
 |       aon_wkup_en_q     <= 1'b0; | 
 |       aon_wkup_mode_q   <= Disabled; | 
 |       aon_filter_en_q   <= 1'b0; | 
 |       aon_wkup_cnt_th_q <= '0; | 
 |     end else begin | 
 |       aon_wkup_en_q <= aon_wkup_en_d; | 
 |       // latch these when going into sleep. note that these | 
 |       // config signals should be stable at this point, since | 
 |       // SW has configured them many cycles ago. hence no | 
 |       // explicit multibit consistency check is performed. | 
 |       if (aon_wkup_en_d & !aon_wkup_en_q) begin | 
 |         aon_wkup_mode_q   <= wkup_mode_i; | 
 |         aon_filter_en_q   <= filter_en_i; | 
 |         aon_wkup_cnt_th_q <= wkup_cnt_th_i; | 
 |       end | 
 |     end | 
 |   end | 
 |  | 
 |   //////////////////////////// | 
 |   // Optional Signal Filter // | 
 |   //////////////////////////// | 
 |  | 
 |   // This uses a lower value for filtering than GPIO since | 
 |   // the always-on clock is slower. This can be disabled, | 
 |   // in which case the signal is just combinationally bypassed. | 
 |   logic aon_filter_out, aon_filter_out_d, aon_filter_out_q; | 
 |   prim_filter #( | 
 |     .Cycles(Cycles) | 
 |   ) i_prim_filter ( | 
 |     .clk_i    ( clk_aon_i       ), | 
 |     .rst_ni   ( rst_aon_ni      ), | 
 |     .enable_i ( aon_filter_en_q ), | 
 |     .filter_i ( pin_value_i     ), | 
 |     .filter_o ( aon_filter_out  ) | 
 |   ); | 
 |  | 
 |   // Run this through a 2 stage synchronizer to | 
 |   // prevent metastability. | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_prim_flop_2sync_filter ( | 
 |     .clk_i  ( clk_aon_i  ), | 
 |     .rst_ni ( rst_aon_ni ), | 
 |     .d      ( aon_filter_out ), | 
 |     .q      ( aon_filter_out_d ) | 
 |   ); | 
 |  | 
 |   ////////////////////// | 
 |   // Pattern Matching // | 
 |   ////////////////////// | 
 |  | 
 |   logic aon_rising, aon_falling; | 
 |   assign aon_falling = ~aon_filter_out_d &  aon_filter_out_q; | 
 |   assign aon_rising  =  aon_filter_out_d & ~aon_filter_out_q; | 
 |  | 
 |   logic aon_cnt_en, aon_cnt_eq_th; | 
 |   logic [WkupCntWidth-1:0] aon_cnt_d, aon_cnt_q; | 
 |   assign aon_cnt_d = (aon_cnt_eq_th) ? '0                : | 
 |                      (aon_cnt_en)    ?  aon_cnt_q + 1'b1 : '0; | 
 |  | 
 |   assign aon_cnt_eq_th = aon_cnt_q == aon_wkup_cnt_th_q; | 
 |  | 
 |   logic aon_wkup_pulse; | 
 |   always_comb begin : p_mode | 
 |     aon_wkup_pulse = 1'b0; | 
 |     aon_cnt_en     = 1'b0; | 
 |     if (aon_wkup_en_q) begin | 
 |       unique case (aon_wkup_mode_q) | 
 |         Negedge:   aon_wkup_pulse = aon_falling; | 
 |         Posedge:   aon_wkup_pulse = aon_rising; | 
 |         Edge:      aon_wkup_pulse = aon_rising | aon_falling; | 
 |         LowTimed: begin | 
 |           aon_cnt_en = ~aon_filter_out_d; | 
 |           aon_wkup_pulse = aon_cnt_eq_th; | 
 |         end | 
 |         HighTimed: begin | 
 |           aon_cnt_en = aon_filter_out_d; | 
 |           aon_wkup_pulse = aon_cnt_eq_th; | 
 |         end | 
 |         default: ; // also covers "Disabled" | 
 |       endcase | 
 |     end | 
 |   end | 
 |  | 
 |   always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin : p_aon_pattern | 
 |     if (!rst_aon_ni) begin | 
 |       aon_filter_out_q <= 1'b0; | 
 |       aon_cnt_q        <= '0; | 
 |     end else begin | 
 |       aon_filter_out_q <= aon_filter_out_d; | 
 |       aon_cnt_q        <= aon_cnt_d; | 
 |     end | 
 |   end | 
 |  | 
 |   //////////////////// | 
 |   // Cause register // | 
 |   //////////////////// | 
 |  | 
 |   // to AON domain | 
 |   logic aon_wkup_cause_valid, aon_wkup_cause_data; | 
 |   logic aon_wkup_cause_d, aon_wkup_cause_q; | 
 |  | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_prim_flop_2sync_cause_in ( | 
 |     .clk_i  ( clk_aon_i  ), | 
 |     .rst_ni ( rst_aon_ni ), | 
 |     .d      ( wkup_cause_data_i   ), | 
 |     .q      ( aon_wkup_cause_data ) | 
 |   ); | 
 |  | 
 |   prim_pulse_sync i_prim_pulse_sync_cause ( | 
 |     .clk_src_i   ( clk_i                ), | 
 |     .rst_src_ni  ( rst_ni               ), | 
 |     .src_pulse_i ( wkup_cause_valid_i   ), | 
 |     .clk_dst_i   ( clk_aon_i            ), | 
 |     .rst_dst_ni  ( rst_aon_ni           ), | 
 |     .dst_pulse_o ( aon_wkup_cause_valid ) | 
 |   ); | 
 |  | 
 |   // note that aon_wkup_pulse will not be asserted when not in sleep mode | 
 |   assign aon_wkup_cause_d = (aon_wkup_cause_valid) ? aon_wkup_cause_q & aon_wkup_cause_data : | 
 |                                                      aon_wkup_cause_q | aon_wkup_pulse; | 
 |  | 
 |   // output to power manager | 
 |   assign aon_wkup_req_o = aon_wkup_cause_q; | 
 |  | 
 |   // output to CSR | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_prim_flop_2sync_cause_out ( | 
 |     .clk_i, | 
 |     .rst_ni, | 
 |     .d      ( aon_wkup_cause_q  ), | 
 |     .q      ( wkup_cause_data_o ) | 
 |   ); | 
 |  | 
 |   always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin : p_aon_cause | 
 |     if (!rst_aon_ni) begin | 
 |       aon_wkup_cause_q <= 1'b0; | 
 |     end else begin | 
 |       aon_wkup_cause_q <= aon_wkup_cause_d; | 
 |     end | 
 |   end | 
 |  | 
 | endmodule : pinmux_wkup |