|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // Description adc_ctrl detection FSM module | 
|  |  | 
|  | module adc_ctrl_fsm | 
|  | import adc_ctrl_reg_pkg::*; | 
|  | import adc_ctrl_pkg::*; | 
|  | ( | 
|  | input clk_aon_i, | 
|  | input rst_aon_ni, | 
|  | input cfg_fsm_rst_i, | 
|  | input cfg_adc_enable_i, | 
|  | input cfg_oneshot_mode_i, | 
|  | input cfg_lp_mode_i, | 
|  | input [3:0] cfg_pwrup_time_i, | 
|  | input [23:0] cfg_wakeup_time_i, | 
|  | input [7:0]  cfg_lp_sample_cnt_i, | 
|  | input [15:0] cfg_np_sample_cnt_i, | 
|  | input [NumAdcFilter-1:0] adc_ctrl_match_i, | 
|  | input [9:0] adc_d_i, | 
|  | input       adc_d_val_i,//valid bit for ADC value | 
|  | output logic      adc_pd_o, | 
|  | output logic[1:0] adc_chn_sel_o, | 
|  | output logic      chn0_val_we_o, | 
|  | output logic      chn1_val_we_o, | 
|  | output logic [9:0] chn0_val_o, | 
|  | output logic [9:0] chn1_val_o, | 
|  | output logic       adc_ctrl_done_o, | 
|  | output logic       oneshot_done_o | 
|  | ); | 
|  |  | 
|  | logic trigger_q; | 
|  | logic trigger_l2h, trigger_h2l; | 
|  |  | 
|  |  | 
|  | logic [3:0] pwrup_timer_cnt_d, pwrup_timer_cnt_q; | 
|  | logic pwrup_timer_cnt_clr, pwrup_timer_cnt_en; | 
|  | logic [9:0] chn0_val_d, chn1_val_d; | 
|  | logic fsm_chn0_sel, fsm_chn1_sel; | 
|  | logic chn0_val_we_d, chn1_val_we_d; | 
|  | logic [7:0] lp_sample_cnt_d, lp_sample_cnt_q; | 
|  | logic lp_sample_cnt_clr, lp_sample_cnt_en; | 
|  | logic [23:0] wakeup_timer_cnt_d, wakeup_timer_cnt_q; | 
|  | logic wakeup_timer_cnt_clr, wakeup_timer_cnt_en; | 
|  | logic [NumAdcFilter-1:0] adc_ctrl_match_q; | 
|  | logic stay_match; | 
|  | logic [15:0] np_sample_cnt_d, np_sample_cnt_q; | 
|  | logic np_sample_cnt_clr, np_sample_cnt_en; | 
|  | logic [7:0] lp_sample_cnt_thresh; | 
|  | logic [15:0] np_sample_cnt_thresh; | 
|  |  | 
|  |  | 
|  | fsm_state_e fsm_state_q, fsm_state_d; | 
|  |  | 
|  | always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin | 
|  | if (!rst_aon_ni) begin | 
|  | trigger_q <= 1'b0; | 
|  | end | 
|  | else if (cfg_fsm_rst_i) begin | 
|  | trigger_q <= 1'b0; | 
|  | end else begin | 
|  | trigger_q  <= cfg_adc_enable_i; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign trigger_l2h = (trigger_q == 1'b0) && (cfg_adc_enable_i == 1'b1); | 
|  | assign trigger_h2l = (trigger_q == 1'b1) && (cfg_adc_enable_i == 1'b0); | 
|  |  | 
|  | assign pwrup_timer_cnt_d = (pwrup_timer_cnt_en) ? pwrup_timer_cnt_q + 1'b1 : pwrup_timer_cnt_q; | 
|  |  | 
|  | always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin | 
|  | if (!rst_aon_ni) begin | 
|  | pwrup_timer_cnt_q    <= '0; | 
|  | end | 
|  | else if (pwrup_timer_cnt_clr || cfg_fsm_rst_i || trigger_h2l) begin | 
|  | pwrup_timer_cnt_q <= '0; | 
|  | end else begin | 
|  | pwrup_timer_cnt_q <= pwrup_timer_cnt_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign lp_sample_cnt_d = (lp_sample_cnt_en) ? lp_sample_cnt_q + 1'b1 : lp_sample_cnt_q; | 
|  |  | 
|  | always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin | 
|  | if (!rst_aon_ni) begin | 
|  | lp_sample_cnt_q    <= '0; | 
|  | end | 
|  | else if (lp_sample_cnt_clr || cfg_fsm_rst_i || trigger_h2l) begin | 
|  | lp_sample_cnt_q <= '0; | 
|  | end else begin | 
|  | lp_sample_cnt_q <= lp_sample_cnt_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign np_sample_cnt_d = (np_sample_cnt_en) ? np_sample_cnt_q + 1'b1 : np_sample_cnt_q; | 
|  |  | 
|  | always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin | 
|  | if (!rst_aon_ni) begin | 
|  | np_sample_cnt_q    <= '0; | 
|  | end | 
|  | else if (np_sample_cnt_clr || cfg_fsm_rst_i || trigger_h2l) begin | 
|  | np_sample_cnt_q <= '0; | 
|  | end else begin | 
|  | np_sample_cnt_q <= np_sample_cnt_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign wakeup_timer_cnt_d = (wakeup_timer_cnt_en) ? | 
|  | wakeup_timer_cnt_q + 1'b1 : wakeup_timer_cnt_q; | 
|  |  | 
|  | always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin | 
|  | if (!rst_aon_ni) begin | 
|  | wakeup_timer_cnt_q    <= '0; | 
|  | end | 
|  | else if (wakeup_timer_cnt_clr || cfg_fsm_rst_i || trigger_h2l) begin | 
|  | wakeup_timer_cnt_q <= '0; | 
|  | end else begin | 
|  | wakeup_timer_cnt_q <= wakeup_timer_cnt_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign fsm_chn0_sel = (fsm_state_q == ONEST_0) || (fsm_state_q == LP_0) || (fsm_state_q == NP_0); | 
|  | assign chn0_val_we_d = fsm_chn0_sel && adc_d_val_i;//adc_d_val_i is a valid pulse | 
|  | assign chn0_val_d = (chn0_val_we_d) ? adc_d_i : chn0_val_o; | 
|  |  | 
|  | assign fsm_chn1_sel = (fsm_state_q == ONEST_1) || (fsm_state_q == LP_1) || (fsm_state_q == NP_1); | 
|  | assign chn1_val_we_d = fsm_chn1_sel && adc_d_val_i; | 
|  | assign chn1_val_d = (chn1_val_we_d) ? adc_d_i : chn1_val_o; | 
|  |  | 
|  | always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin | 
|  | if (!rst_aon_ni) begin | 
|  | chn0_val_we_o  <= '0; | 
|  | chn1_val_we_o  <= '0; | 
|  | chn0_val_o     <= '0; | 
|  | chn1_val_o     <= '0; | 
|  | end | 
|  | else if (cfg_fsm_rst_i) begin | 
|  | chn0_val_we_o  <= '0; | 
|  | chn1_val_we_o  <= '0; | 
|  | chn0_val_o     <= '0; | 
|  | chn1_val_o     <= '0; | 
|  | end else begin | 
|  | chn0_val_we_o  <= chn0_val_we_d; | 
|  | chn1_val_we_o  <= chn1_val_we_d; | 
|  | chn0_val_o     <= chn0_val_d; | 
|  | chn1_val_o     <= chn1_val_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | // TODO: #13725 | 
|  | // The logic below can be enhanced for better handling in the future | 
|  | logic ld_match; | 
|  | always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin | 
|  | if (!rst_aon_ni) begin | 
|  | adc_ctrl_match_q  <= '0; | 
|  | end | 
|  | else if (cfg_fsm_rst_i) begin | 
|  | adc_ctrl_match_q  <= '0; | 
|  | end | 
|  | else if (ld_match) begin | 
|  | adc_ctrl_match_q  <= adc_ctrl_match_i; | 
|  | end | 
|  | end | 
|  |  | 
|  | logic np_match; | 
|  | assign np_match = |adc_ctrl_match_i &                       // if current match is non-zero | 
|  | ((adc_ctrl_match_i == adc_ctrl_match_q) | // match if same as previous match | 
|  | ~|adc_ctrl_match_q);                      // or match if previous match was zero | 
|  |  | 
|  | assign stay_match = np_match; | 
|  |  | 
|  | always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin | 
|  | if (!rst_aon_ni) begin | 
|  | fsm_state_q    <= PWRDN; | 
|  | end | 
|  | else if (trigger_h2l || cfg_fsm_rst_i) begin | 
|  | fsm_state_q    <= PWRDN; | 
|  | end else begin | 
|  | fsm_state_q    <= fsm_state_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign lp_sample_cnt_thresh = cfg_lp_sample_cnt_i - 1'b1; | 
|  | assign np_sample_cnt_thresh = cfg_np_sample_cnt_i - 1'b1; | 
|  |  | 
|  |  | 
|  | always_comb begin: adc_fsm | 
|  | fsm_state_d = fsm_state_q; | 
|  | //outputs | 
|  | adc_chn_sel_o = 2'b0; | 
|  | adc_pd_o = 1'b0;//default value | 
|  | pwrup_timer_cnt_clr = 1'b0; | 
|  | pwrup_timer_cnt_en = 1'b0; | 
|  | lp_sample_cnt_clr = 1'b0; | 
|  | lp_sample_cnt_en = 1'b0; | 
|  | wakeup_timer_cnt_clr = 1'b0; | 
|  | wakeup_timer_cnt_en = 1'b0; | 
|  | np_sample_cnt_clr = 1'b0; | 
|  | np_sample_cnt_en = 1'b0; | 
|  | adc_ctrl_done_o = 1'b0; | 
|  | oneshot_done_o = 1'b0; | 
|  | ld_match = 1'b0; | 
|  |  | 
|  | unique case (fsm_state_q) | 
|  | PWRDN: begin | 
|  | adc_pd_o = 1'b1; | 
|  | if (trigger_l2h) begin | 
|  | fsm_state_d = PWRUP; | 
|  | end | 
|  | end | 
|  |  | 
|  | PWRUP: begin | 
|  | if (pwrup_timer_cnt_q != cfg_pwrup_time_i) begin | 
|  | pwrup_timer_cnt_en = 1'b1; | 
|  | end | 
|  | else if (pwrup_timer_cnt_q == cfg_pwrup_time_i) begin | 
|  | pwrup_timer_cnt_clr = 1'b1; | 
|  | if (cfg_oneshot_mode_i) begin | 
|  | fsm_state_d = ONEST_0; | 
|  | end | 
|  | else if (cfg_lp_mode_i) begin | 
|  | fsm_state_d = LP_0; | 
|  | end | 
|  | else if (!cfg_lp_mode_i) begin | 
|  | fsm_state_d = NP_0; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | ONEST_0: begin | 
|  | adc_chn_sel_o = 2'b01; | 
|  | if (adc_d_val_i) begin//sample chn0 value | 
|  | fsm_state_d = ONEST_021; | 
|  | end | 
|  | end | 
|  |  | 
|  | ONEST_021: begin//transition between chn0 and chn1; adc_chn_sel_o=2'b0 | 
|  | if (!adc_d_val_i) begin | 
|  | fsm_state_d = ONEST_1; | 
|  | end | 
|  | end | 
|  |  | 
|  | ONEST_1: begin | 
|  | adc_chn_sel_o = 2'b10; | 
|  | if (adc_d_val_i) begin//sample chn1 value | 
|  | fsm_state_d = ONEST_DONE; | 
|  | end | 
|  | end | 
|  |  | 
|  | // delay done assertion by one cycle to match | 
|  | // adc capture register timing | 
|  | ONEST_DONE: begin | 
|  | oneshot_done_o = 1'b1; | 
|  | fsm_state_d = PWRDN; | 
|  | end | 
|  |  | 
|  | LP_0: begin | 
|  | adc_chn_sel_o = 2'b01; | 
|  | if (adc_d_val_i) begin//sample chn0 value | 
|  | fsm_state_d = LP_021; | 
|  | end | 
|  | end | 
|  |  | 
|  | LP_021: begin//transition between chn0 and chn1; adc_chn_sel_o=2'b0 | 
|  | if (!adc_d_val_i) begin | 
|  | fsm_state_d = LP_1; | 
|  | end | 
|  | end | 
|  |  | 
|  | LP_1: begin | 
|  | adc_chn_sel_o = 2'b10; | 
|  | if (adc_d_val_i) begin//sample chn1 value | 
|  | fsm_state_d = LP_EVAL; | 
|  | end | 
|  | end | 
|  |  | 
|  | LP_EVAL: begin | 
|  | // do not transition forward until handshake with ADC is complete | 
|  | if (!adc_d_val_i) begin | 
|  | ld_match = 1'b1; | 
|  | if (!stay_match) begin | 
|  | fsm_state_d = LP_SLP; | 
|  | lp_sample_cnt_clr = 1'b1; | 
|  | end else if (lp_sample_cnt_q < lp_sample_cnt_thresh) begin | 
|  | fsm_state_d = LP_SLP; | 
|  | lp_sample_cnt_en = 1'b1; | 
|  | end else if (lp_sample_cnt_q == lp_sample_cnt_thresh) begin | 
|  | fsm_state_d = NP_0; | 
|  | lp_sample_cnt_clr = 1'b1; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | LP_SLP: begin | 
|  | adc_pd_o = 1'b1; | 
|  | if (wakeup_timer_cnt_q  != cfg_wakeup_time_i) begin | 
|  | wakeup_timer_cnt_en = 1'b1; | 
|  | end | 
|  | else if (wakeup_timer_cnt_q == cfg_wakeup_time_i) begin | 
|  | fsm_state_d = LP_PWRUP; | 
|  | wakeup_timer_cnt_clr = 1'b1; | 
|  | end | 
|  | end | 
|  |  | 
|  | LP_PWRUP: begin | 
|  | if (pwrup_timer_cnt_q != cfg_pwrup_time_i) begin | 
|  | pwrup_timer_cnt_en = 1'b1; | 
|  | end | 
|  | else if (pwrup_timer_cnt_q == cfg_pwrup_time_i) begin | 
|  | pwrup_timer_cnt_clr = 1'b1; | 
|  | fsm_state_d = LP_0; | 
|  | end | 
|  | end | 
|  |  | 
|  | NP_0: begin | 
|  | adc_chn_sel_o = 2'b01; | 
|  | if (adc_d_val_i) begin//sample chn0 value | 
|  | fsm_state_d = NP_021; | 
|  | end | 
|  | end | 
|  |  | 
|  | NP_021: begin//transition between chn0 and chn1; adc_chn_sel_o=2'b0 | 
|  | if (!adc_d_val_i) begin | 
|  | fsm_state_d = NP_1; | 
|  | end | 
|  | end | 
|  |  | 
|  | NP_1: begin | 
|  | adc_chn_sel_o = 2'b10; | 
|  | if (adc_d_val_i) begin//sample chn1 value | 
|  | fsm_state_d = NP_EVAL; | 
|  | end | 
|  | end | 
|  |  | 
|  | NP_EVAL: begin | 
|  | // do not transition forward until handshake with ADC is complete | 
|  | if (!adc_d_val_i) begin | 
|  | ld_match = 1'b1; | 
|  | // if there is no match, clear counter and begin sampling again. | 
|  | // if there is a match, there are 3 conditions: | 
|  | // 1. the sample count is less than the threshold -> still attempting to make a new match, | 
|  | //    keep sampling. | 
|  | // 2. the sample count is equal to the threshold -> a new match has just been made, go to | 
|  | //    DONE. | 
|  | // 3, the sample count is greater than the threshold -> this is a continued stable match, | 
|  | //    keep sampling. | 
|  | if (!stay_match) begin | 
|  | fsm_state_d = NP_0; | 
|  | np_sample_cnt_clr = 1'b1; | 
|  | end else if (np_sample_cnt_q < np_sample_cnt_thresh) begin | 
|  | fsm_state_d = NP_0; | 
|  | np_sample_cnt_en = 1'b1; | 
|  | end else if (np_sample_cnt_q == np_sample_cnt_thresh) begin | 
|  | fsm_state_d = NP_DONE; | 
|  | np_sample_cnt_en = 1'b1; | 
|  | end else if (np_sample_cnt_q > np_sample_cnt_thresh) begin | 
|  | fsm_state_d = NP_0; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // delay done assertion by one cycle to match with channel register timing | 
|  | NP_DONE: begin | 
|  | adc_ctrl_done_o = 1'b1; | 
|  | fsm_state_d = NP_0; | 
|  | end | 
|  |  | 
|  | default: fsm_state_d = PWRDN; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | `ASSUME(LpSampleCntCfg_M, cfg_lp_sample_cnt_i > '0, clk_aon_i, !rst_aon_ni) | 
|  | `ASSUME(NpSampleCntCfg_M, cfg_np_sample_cnt_i > '0, clk_aon_i, !rst_aon_ni) | 
|  | `ASSERT(NpCntClrPwrDn_A, fsm_state_q == PWRDN |-> (np_sample_cnt_q == '0), | 
|  | clk_aon_i, !rst_aon_ni) | 
|  |  | 
|  | // This statement should hold true even during low power scanning | 
|  | `ASSERT(NpCntClrMisMatch_A, ld_match & !stay_match |=> | 
|  | (np_sample_cnt_q == '0), clk_aon_i, !rst_aon_ni) | 
|  |  | 
|  | endmodule |