| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // Debounces the input signal and detects level changes. |
| |
| module sysrst_ctrl_detect #( |
| parameter int unsigned TimerWidth = 16, |
| // If this parameter is set to 1, an edge is required for detection. |
| // Otherwise the correct level (e.g. low for h2l) is sufficient. |
| parameter bit EdgeDetect = 0, |
| // If this parameter is set to 1, the l2h_detected_o and h2l_detected_o conditions can only be |
| // reset by disabling the corresponding detector (cfg_l2h_en_i or cfg_h2l_en_i). |
| parameter bit Sticky = 0 |
| ) ( |
| input clk_i, |
| input rst_ni, |
| input trigger_i, |
| input [TimerWidth-1:0] cfg_timer_i, |
| input cfg_l2h_en_i, |
| input cfg_h2l_en_i, |
| output logic l2h_detected_o, |
| output logic h2l_detected_o, |
| output logic l2h_detected_pulse_o, |
| output logic h2l_detected_pulse_o |
| |
| ); |
| |
| logic trigger_debounced_d; |
| prim_filter_ctr #( |
| .AsyncOn(0), // input signal has already been synced to the AON clock |
| .CntWidth(TimerWidth) |
| ) u_prim_filter_ctr ( |
| .clk_i, |
| .rst_ni, |
| .enable_i(1'b1), |
| .filter_i(trigger_i), |
| .thresh_i(cfg_timer_i), |
| .filter_o(trigger_debounced_d) |
| ); |
| |
| logic h2l_event, l2h_event; |
| if (EdgeDetect) begin : gen_edge_detect |
| logic trigger_debounced_q; |
| assign l2h_event = trigger_debounced_d & ~trigger_debounced_q; |
| assign h2l_event = ~trigger_debounced_d & trigger_debounced_q; |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg |
| if (!rst_ni) begin |
| trigger_debounced_q <= 1'b0; |
| end else begin |
| trigger_debounced_q <= trigger_debounced_d; |
| end |
| end |
| end else begin : gen_level_only |
| // In this case we just look at the signal level. |
| assign l2h_event = trigger_debounced_d; |
| assign h2l_event = ~trigger_debounced_d; |
| end |
| |
| logic h2l_detected_d, h2l_detected_q; |
| logic l2h_detected_d, l2h_detected_q; |
| if (Sticky) begin : gen_sticky |
| // Keep detected event until detector is disabled. |
| assign l2h_detected_d = (cfg_l2h_en_i) ? (l2h_event | l2h_detected_q) : 1'b0; |
| assign h2l_detected_d = (cfg_h2l_en_i) ? (h2l_event | h2l_detected_q) : 1'b0; |
| end else begin : gen_not_sticky |
| // Deassert automatically if the level does not remain stable after event. |
| assign l2h_detected_d = (~trigger_debounced_d) ? 1'b0 : |
| (cfg_l2h_en_i) ? (l2h_event | l2h_detected_q) : |
| 1'b0; |
| assign h2l_detected_d = (trigger_debounced_d) ? 1'b0 : |
| (cfg_h2l_en_i) ? (h2l_event | h2l_detected_q) : |
| 1'b0; |
| end |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs |
| if (!rst_ni) begin |
| l2h_detected_q <= 1'b0; |
| h2l_detected_q <= 1'b0; |
| end else begin |
| l2h_detected_q <= l2h_detected_d; |
| h2l_detected_q <= h2l_detected_d; |
| end |
| end |
| |
| assign l2h_detected_o = l2h_detected_d; |
| assign h2l_detected_o = h2l_detected_d; |
| assign l2h_detected_pulse_o = l2h_detected_d & ~l2h_detected_q; |
| assign h2l_detected_pulse_o = h2l_detected_d & ~h2l_detected_q; |
| |
| endmodule |