blob: 1db76b4cb5ef59c8932d2f61acb0da93761221d8 [file] [log] [blame]
// 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