| // 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:OffDomainSelStart] == '0 & |
| pwr_rst_i.rst_sys_src_n[PowerDomains-1:OffDomainSelStart] == '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 = FastPwrStateRomCheck; |
| 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 = FastPwrStateRomCheck; |
| end |
| end |
| |
| FastPwrStateLowPowerPrep: begin |
| // reset cause is set only if main power domain will be turned off |
| 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 |