| // Copyright lowRISC contributors. | 
 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | // Power Manager Fast FSM | 
 | // | 
 |  | 
 | `include "prim_assert.sv" | 
 |  | 
 | module pwrmgr_fsm import pwrmgr_pkg::*; import pwrmgr_reg_pkg::*;( | 
 |   input clk_i, | 
 |   input rst_ni, | 
 |   input clk_slow_i, | 
 |   input rst_slow_ni, | 
 |  | 
 |   // interface with slow_fsm | 
 |   input req_pwrup_i, | 
 |   input pwrup_cause_e pwrup_cause_i, | 
 |   output logic ack_pwrup_o, | 
 |   output logic req_pwrdn_o, | 
 |   input ack_pwrdn_i, | 
 |   input low_power_entry_i, | 
 |   input main_pd_ni, | 
 |   input [NumRstReqs:0] reset_reqs_i, | 
 |  | 
 |   // consumed in pwrmgr | 
 |   output logic wkup_o,        // generate wake interrupt | 
 |   output logic fall_through_o, | 
 |   output logic abort_o, | 
 |   output logic clr_hint_o, | 
 |   output logic clr_cfg_lock_o, | 
 |  | 
 |   // rstmgr | 
 |   output pwr_rst_req_t pwr_rst_o, | 
 |   input pwr_rst_rsp_t pwr_rst_i, | 
 |  | 
 |   // clkmgr | 
 |   output logic ips_clk_en_o, | 
 |   input clk_en_status_i, | 
 |  | 
 |   // otp | 
 |   output logic otp_init_o, | 
 |   input otp_done_i, | 
 |   input otp_idle_i, | 
 |  | 
 |   // lc | 
 |   output logic lc_init_o, | 
 |   input lc_done_i, | 
 |   input lc_idle_i, | 
 |  | 
 |   // flash | 
 |   input flash_idle_i, | 
 |  | 
 |   // rom_ctrl | 
 |   input rom_ctrl_done_i, | 
 |   input rom_ctrl_good_i, | 
 |  | 
 |   // pinmux | 
 |   output logic strap_o, | 
 |   output logic low_power_o, | 
 |  | 
 |   // processing elements | 
 |   output lc_ctrl_pkg::lc_tx_t fetch_en_o | 
 | ); | 
 |  | 
 |   // The code below always assumes the always on domain is index 0 | 
 |   `ASSERT_INIT(AlwaysOnIndex_A, ALWAYS_ON_DOMAIN == 0) | 
 |  | 
 |   // when there are multiple on domains, the latter 1 should become another parameter | 
 |   localparam int OffDomainSelStart = ALWAYS_ON_DOMAIN + 1; | 
 |  | 
 |   // all powered down domains have resets asserted | 
 |   logic pd_n_rsts_asserted; | 
 |  | 
 |   // all domains have resets asserted | 
 |   logic all_rsts_asserted; | 
 |  | 
 |   // resets are valid | 
 |   logic reset_valid; | 
 |  | 
 |   // reset hint to rstmgr | 
 |   reset_cause_e reset_cause_q, reset_cause_d; | 
 |  | 
 |   // reset request | 
 |   logic reset_req; | 
 |  | 
 |   // strap sample should only happen on cold boot or when the | 
 |   // the system goes through a reset cycle | 
 |   logic strap_sampled; | 
 |  | 
 |   // disable processing element fetching | 
 |   lc_ctrl_pkg::lc_tx_t fetch_en_q, fetch_en_d; | 
 |  | 
 |   fast_pwr_state_e state_d, state_q; | 
 |   logic reset_ongoing_q, reset_ongoing_d; | 
 |   logic req_pwrdn_q, req_pwrdn_d; | 
 |   logic ack_pwrup_q, ack_pwrup_d; | 
 |   logic ip_clk_en_q, ip_clk_en_d; | 
 |   logic [PowerDomains-1:0] rst_lc_req_q, rst_sys_req_q; | 
 |   logic [PowerDomains-1:0] rst_lc_req_d, rst_sys_req_d; | 
 |   logic otp_init; | 
 |   logic lc_init; | 
 |   logic low_power_q, low_power_d; | 
 |  | 
 |   assign pd_n_rsts_asserted = pwr_rst_i.rst_lc_src_n[PowerDomains-1:1] == '0 & | 
 |                               pwr_rst_i.rst_sys_src_n[PowerDomains-1:1] == '0; | 
 |  | 
 |   assign all_rsts_asserted = pwr_rst_i.rst_lc_src_n == '0 & | 
 |                              pwr_rst_i.rst_sys_src_n == '0; | 
 |  | 
 |   assign reset_req = |reset_reqs_i; | 
 |  | 
 |   // when in low power path, resets are controlled by domain power down | 
 |   // when in reset path, all resets must be asserted | 
 |   // when the reset cause is something else, it is invalid | 
 |   assign reset_valid = reset_cause_q == LowPwrEntry ? main_pd_ni | pd_n_rsts_asserted : | 
 |                        reset_cause_q == HwReq       ? all_rsts_asserted : 1'b0; | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       state_q <= FastPwrStateLowPower; | 
 |       ack_pwrup_q <= 1'b0; | 
 |       req_pwrdn_q <= 1'b0; | 
 |       reset_ongoing_q <= 1'b0; | 
 |       ip_clk_en_q <= 1'b0; | 
 |       rst_lc_req_q <= {PowerDomains{1'b1}}; | 
 |       rst_sys_req_q <= {PowerDomains{1'b1}}; | 
 |       reset_cause_q <= ResetUndefined; | 
 |       low_power_q <= 1'b1; | 
 |     end else begin | 
 |       state_q <= state_d; | 
 |       ack_pwrup_q <= ack_pwrup_d; | 
 |       req_pwrdn_q <= req_pwrdn_d; | 
 |       reset_ongoing_q <= reset_ongoing_d; | 
 |       ip_clk_en_q <= ip_clk_en_d; | 
 |       rst_lc_req_q <= rst_lc_req_d; | 
 |       rst_sys_req_q <= rst_sys_req_d; | 
 |       reset_cause_q <= reset_cause_d; | 
 |       low_power_q <= low_power_d; | 
 |     end | 
 |   end | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       strap_sampled <= 1'b0; | 
 |     end else if (&rst_lc_req_q || &rst_sys_req_q) begin | 
 |       strap_sampled <= 1'b0; | 
 |     end else if (strap_o) begin | 
 |       strap_sampled <= 1'b1; | 
 |     end | 
 |   end | 
 |  | 
 |   prim_lc_sender u_fetch_en ( | 
 |     .clk_i, | 
 |     .rst_ni, | 
 |     .lc_en_i(fetch_en_d), | 
 |     .lc_en_o(fetch_en_q) | 
 |   ); | 
 |   assign fetch_en_o = fetch_en_q; | 
 |  | 
 |   // Life cycle broadcast may take time to propagate through the system. | 
 |   // The sync below simulates that behavior using the slowest clock in the | 
 |   // system. | 
 |   logic slow_lc_done; | 
 |   logic lc_done; | 
 |  | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) u_slow_sync_lc_done ( | 
 |     .clk_i(clk_slow_i), | 
 |     .rst_ni(rst_slow_ni), | 
 |     .d_i(lc_done_i), | 
 |     .q_o(slow_lc_done) | 
 |   ); | 
 |  | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) u_sync_lc_done ( | 
 |     .clk_i, | 
 |     .rst_ni, | 
 |     .d_i(slow_lc_done), | 
 |     .q_o(lc_done) | 
 |   ); | 
 |  | 
 |   always_comb begin | 
 |     otp_init = 1'b0; | 
 |     lc_init = 1'b0; | 
 |     wkup_o = 1'b0; | 
 |     fall_through_o = 1'b0; | 
 |     abort_o = 1'b0; | 
 |     clr_hint_o = 1'b0; | 
 |     clr_cfg_lock_o = 1'b0; | 
 |     strap_o = 1'b0; | 
 |  | 
 |     state_d = state_q; | 
 |     ack_pwrup_d = ack_pwrup_q; | 
 |     req_pwrdn_d = req_pwrdn_q; | 
 |     reset_ongoing_d = reset_ongoing_q; | 
 |     ip_clk_en_d = ip_clk_en_q; | 
 |     rst_lc_req_d = rst_lc_req_q; | 
 |     rst_sys_req_d = rst_sys_req_q; | 
 |     reset_cause_d = reset_cause_q; | 
 |     low_power_d = low_power_q; | 
 |     fetch_en_d = fetch_en_q; | 
 |  | 
 |     unique case(state_q) | 
 |  | 
 |       FastPwrStateLowPower: begin | 
 |         if (req_pwrup_i || reset_ongoing_q) begin | 
 |           state_d = FastPwrStateEnableClocks; | 
 |         end | 
 |       end | 
 |  | 
 |       FastPwrStateEnableClocks: begin | 
 |         ip_clk_en_d = 1'b1; | 
 |  | 
 |         if (clk_en_status_i) begin | 
 |           state_d = FastPwrStateReleaseLcRst; | 
 |         end | 
 |       end | 
 |  | 
 |       FastPwrStateReleaseLcRst: begin | 
 |         rst_lc_req_d = '0;  // release rst_lc_n for all power domains | 
 |  | 
 |         if (&pwr_rst_i.rst_lc_src_n) begin // once all resets are released | 
 |           state_d = FastPwrStateOtpInit; | 
 |         end | 
 |       end | 
 |  | 
 |       FastPwrStateOtpInit: begin | 
 |         otp_init = 1'b1; | 
 |  | 
 |         if (otp_done_i) begin | 
 |           state_d = FastPwrStateLcInit; | 
 |         end | 
 |       end | 
 |  | 
 |       FastPwrStateLcInit: begin | 
 |         lc_init = 1'b1; | 
 |  | 
 |         if (lc_done) begin | 
 |           state_d = FastPwrStateStrap; | 
 |  | 
 |         end | 
 |       end | 
 |  | 
 |       FastPwrStateStrap: begin | 
 |         strap_o = ~strap_sampled; | 
 |         state_d =  FastPwrStateAckPwrUp; | 
 |       end | 
 |  | 
 |       FastPwrStateAckPwrUp: begin | 
 |         // only ack the slow_fsm if we actually transitioned through it | 
 |         ack_pwrup_d = !reset_ongoing_q; | 
 |  | 
 |         // wait for request power up to drop relative to ack | 
 |         if (!req_pwrup_i || reset_ongoing_q) begin | 
 |           ack_pwrup_d = 1'b0; | 
 |           clr_cfg_lock_o = 1'b1; | 
 |           wkup_o = pwrup_cause_i == Wake; | 
 |           state_d = FastPwrStateRomCheck; | 
 |         end | 
 |       end | 
 |  | 
 |       FastPwrStateRomCheck: begin | 
 |         // zero outgoing low power indication | 
 |         low_power_d = '0; | 
 |         rst_sys_req_d = '0; | 
 |         reset_cause_d = ResetNone; | 
 |  | 
 |         if (rom_ctrl_done_i && rom_ctrl_good_i) begin | 
 |           state_d = FastPwrStateActive; | 
 |         end | 
 |       end | 
 |  | 
 |  | 
 |       FastPwrStateActive: begin | 
 |         // only in active state, allow processor to execute | 
 |         fetch_en_d = lc_ctrl_pkg::On; | 
 |  | 
 |         if (reset_req || low_power_entry_i) begin | 
 |           reset_cause_d = ResetUndefined; | 
 |           state_d = FastPwrStateDisClks; | 
 |         end | 
 |       end | 
 |  | 
 |       FastPwrStateDisClks: begin | 
 |         ip_clk_en_d = 1'b0; | 
 |  | 
 |         if (!clk_en_status_i) begin | 
 |           state_d = reset_req ? FastPwrStateNvmShutDown : FastPwrStateFallThrough; | 
 |           low_power_d = ~reset_req; | 
 |         end | 
 |       end | 
 |  | 
 |       // Low Power Path | 
 |       FastPwrStateFallThrough: begin | 
 |         clr_hint_o = 1'b1; | 
 |  | 
 |         // The processor was interrupted after it asserted WFI and is executing again | 
 |         if (!low_power_entry_i) begin | 
 |           ip_clk_en_d = 1'b1; | 
 |           wkup_o = 1'b1; | 
 |           fall_through_o = 1'b1; | 
 |           state_d = FastPwrStateActive; | 
 |         end else begin | 
 |           state_d = FastPwrStateNvmIdleChk; | 
 |         end | 
 |       end | 
 |  | 
 |       FastPwrStateNvmIdleChk: begin | 
 |  | 
 |         if (otp_idle_i && lc_idle_i && flash_idle_i) begin | 
 |           state_d = FastPwrStateLowPowerPrep; | 
 |         end else begin | 
 |           ip_clk_en_d = 1'b1; | 
 |           wkup_o = 1'b1; | 
 |           abort_o = 1'b1; | 
 |           state_d = FastPwrStateActive; | 
 |         end | 
 |       end | 
 |  | 
 |       FastPwrStateLowPowerPrep: begin | 
 |         reset_cause_d = LowPwrEntry; | 
 |  | 
 |         // reset non-always-on domains if requested | 
 |         // this includes the clock manager, which implies pwr/rst managers must | 
 |         // be fed directly from the source | 
 |         for (int i = OffDomainSelStart; i < PowerDomains; i++) begin | 
 |           rst_lc_req_d[i] = ~main_pd_ni; | 
 |           rst_sys_req_d[i] = ~main_pd_ni; | 
 |         end | 
 |  | 
 |         if (reset_valid) begin | 
 |           state_d = FastPwrStateReqPwrDn; | 
 |         end | 
 |       end | 
 |  | 
 |       FastPwrStateReqPwrDn: begin | 
 |         req_pwrdn_d = 1'b1; | 
 |  | 
 |         if (ack_pwrdn_i) begin | 
 |           req_pwrdn_d = 1'b0; | 
 |           state_d = FastPwrStateLowPower; | 
 |         end | 
 |       end | 
 |  | 
 |       // Reset Path | 
 |       // This state is TODO, the details are still under discussion | 
 |       FastPwrStateNvmShutDown: begin | 
 |         clr_hint_o = 1'b1; | 
 |         reset_ongoing_d = 1'b1; | 
 |         state_d = FastPwrStateResetPrep; | 
 |       end | 
 |  | 
 |       FastPwrStateResetPrep: begin | 
 |         reset_cause_d = HwReq; | 
 |         rst_lc_req_d = {PowerDomains{1'b1}}; | 
 |         rst_sys_req_d = {PowerDomains{1'b1}}; | 
 |  | 
 |         if (reset_valid) begin | 
 |           state_d = FastPwrStateLowPower; | 
 |         end | 
 |       end | 
 |  | 
 |       // Terminal state, kill everything | 
 |       default: begin | 
 |         rst_lc_req_d = {PowerDomains{1'b1}}; | 
 |         rst_sys_req_d = {PowerDomains{1'b1}}; | 
 |         ip_clk_en_d = 1'b0; | 
 |       end | 
 |  | 
 |     endcase // unique case (state_q) | 
 |   end // always_comb | 
 |  | 
 |   assign ack_pwrup_o = ack_pwrup_q; | 
 |   assign req_pwrdn_o = req_pwrdn_q; | 
 |   assign low_power_o = low_power_q; | 
 |  | 
 |   assign pwr_rst_o.rst_lc_req = rst_lc_req_q; | 
 |   assign pwr_rst_o.rst_sys_req = rst_sys_req_q; | 
 |   assign pwr_rst_o.reset_cause = reset_cause_q; | 
 |   assign pwr_rst_o.rstreqs = reset_reqs_i; | 
 |  | 
 |   assign ips_clk_en_o = ip_clk_en_q; | 
 |  | 
 |   prim_flop #( | 
 |     .Width(1), | 
 |     .ResetValue(1'b0) | 
 |   ) u_reg_otp_init ( | 
 |     .clk_i, | 
 |     .rst_ni, | 
 |     .d_i(otp_init), | 
 |     .q_o(otp_init_o) | 
 |   ); | 
 |  | 
 |   prim_flop #( | 
 |     .Width(1), | 
 |     .ResetValue(1'b0) | 
 |   ) u_reg_lc_init ( | 
 |     .clk_i, | 
 |     .rst_ni, | 
 |     .d_i(lc_init), | 
 |     .q_o(lc_init_o) | 
 |   ); | 
 |  | 
 |  | 
 | endmodule |