blob: 9e6e3a4fc090e181b0b4aca569a75bdeea5f9729 [file] [log] [blame]
// 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_i ( wkup_en_i ),
.q_o ( 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 <= Posedge;
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_i ( aon_filter_out ),
.q_o ( 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;
Edge: aon_wkup_pulse = aon_rising | aon_falling;
HighTimed: begin
aon_cnt_en = aon_filter_out_d;
aon_wkup_pulse = aon_cnt_eq_th;
end
LowTimed: begin
aon_cnt_en = ~aon_filter_out_d;
aon_wkup_pulse = aon_cnt_eq_th;
end
// Default to rising
default: aon_wkup_pulse = aon_rising;
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_i ( wkup_cause_data_i ),
.q_o ( 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_i ( aon_wkup_cause_q ),
.q_o ( 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