blob: 32ef30292efdafbcba3bc10a991c6b18bb8a8145 [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 [TotalResetWidth-1:0] reset_reqs_i,
input fsm_invalid_i,
output logic clr_slow_req_o,
input usb_ip_clk_en_i,
output logic usb_ip_clk_status_o,
// 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 pwr_clk_req_t ips_clk_en_o,
input pwr_clk_rsp_t 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,
input lc_ctrl_pkg::lc_tx_t lc_dft_en_i,
input lc_ctrl_pkg::lc_tx_t lc_hw_debug_en_i,
// flash
input flash_idle_i,
// rom_ctrl
input prim_mubi_pkg::mubi4_t rom_ctrl_done_i,
input prim_mubi_pkg::mubi4_t 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
);
import prim_mubi_pkg::mubi4_t;
import prim_mubi_pkg::mubi4_test_true_strict;
import prim_mubi_pkg::mubi4_or_hi;
import prim_mubi_pkg::mubi4_and_hi;
import lc_ctrl_pkg::lc_tx_and_hi;
import lc_ctrl_pkg::lc_tx_test_true_strict;
// 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;
logic direct_rst_req;
logic ndmreset_req;
logic hw_rst_req;
logic sw_rst_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;
logic lc_rsts_valid;
assign lc_rsts_valid = ((rst_lc_req_q & ~pwr_rst_i.rst_lc_src_n) |
(~rst_lc_req_q & pwr_rst_i.rst_lc_src_n)) == {PowerDomains{1'b1}};
logic sys_rsts_valid;
assign sys_rsts_valid = ((rst_sys_req_q & ~pwr_rst_i.rst_sys_src_n) |
(~rst_sys_req_q & pwr_rst_i.rst_sys_src_n)) == {PowerDomains{1'b1}};
assign all_rsts_asserted = lc_rsts_valid & sys_rsts_valid;
// Any reset request was asserted.
assign reset_req = |reset_reqs_i;
// Any peripheral triggererd hardware reset request.
assign hw_rst_req = |reset_reqs_i[NumRstReqs-1:0];
// Direct reset request that bypass checks.
assign direct_rst_req = reset_reqs_i[ResetEscIdx] |
reset_reqs_i[ResetMainPwrIdx];
// Ndm reset request.
assign ndmreset_req = reset_reqs_i[ResetNdmIdx];
// Software triggered reset request.
assign sw_rst_req = reset_reqs_i[ResetSwReqIdx];
// 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
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
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
// SEC_CM: FSM.SPARSE
`PRIM_FLOP_SPARSE_FSM(u_state_regs, state_d, state_q, fast_pwr_state_e, FastPwrStateLowPower)
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
strap_sampled <= 1'b0;
end else if (&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)
);
logic clks_enabled;
logic clks_disabled;
// clocks all enabled computed as follows:
// if enable is high, meaning clock is requested to turn on, the status must
// also be 1.
// if enable is low, meaning clock is not requested to turn on, the status is
// don't care.
// the bit-wise OR of both conditions must be all true.
assign clks_enabled = ip_clk_en_q &&
&((ips_clk_en_o & clk_en_status_i) | ~ips_clk_en_o);
// clocks all disabled is the opposite:
// if enable is low the status must also be low.
// if enable is high, the status is don't care.
// the bit-wise OR of both conditions must be all true.
assign clks_disabled = ~ip_clk_en_q &&
&((~ips_clk_en_o & ~clk_en_status_i) | ips_clk_en_o);
// rom integrity checks are disabled during TEST / RMA states
// During TEST / RMA states, both dft_en and hw_debug_en are On.
// During DEV / PROD states, either both signals are Off, or only
// hw_debug_en is On
mubi4_t rom_intg_chk_dis;
assign rom_intg_chk_dis = lc_tx_test_true_strict(lc_tx_and_hi(lc_dft_en_i, lc_hw_debug_en_i)) ?
prim_mubi_pkg::MuBi4True :
prim_mubi_pkg::MuBi4False;
mubi4_t rom_intg_chk_done;
mubi4_t rom_intg_chk_good;
assign rom_intg_chk_done = mubi4_or_hi(mubi4_and_hi(rom_intg_chk_dis, rom_ctrl_done_i),
rom_ctrl_done_i);
assign rom_intg_chk_good = mubi4_or_hi(rom_intg_chk_dis, rom_ctrl_good_i);
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;
clr_slow_req_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 (clks_enabled) begin
state_d = FastPwrStateReleaseLcRst;
end
end
FastPwrStateReleaseLcRst: begin
rst_lc_req_d = '0; // release rst_lc_n for all power domains
rst_sys_req_d = '0; // release rst_sys_n for all power domains
// once all resets are released continue to otp initilization
if (&pwr_rst_i.rst_lc_src_n) begin
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 = FastPwrStateAckPwrUp;
end
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;
// generate a wakeup interrupt if we intended to go to low power
// and we were woken from low power with a wakeup and not reset
wkup_o = (pwrup_cause_i == Wake) & (reset_cause_q == LowPwrEntry);
// This constitutes the end of a reset cycle
reset_ongoing_d = 1'b0;
state_d = FastPwrStateStrap;
end
end
FastPwrStateStrap: begin
strap_o = ~strap_sampled;
state_d = FastPwrStateRomCheckDone;
end
FastPwrStateRomCheckDone: begin
// zero outgoing low power indication
low_power_d = '0;
reset_cause_d = ResetNone;
// When done is observed, advance to good check
if (mubi4_test_true_strict(rom_intg_chk_done)) begin
state_d = FastPwrStateRomCheckGood;
end
end
FastPwrStateRomCheckGood: begin
if (mubi4_test_true_strict(rom_intg_chk_good)) begin
state_d = FastPwrStateActive;
end
end
FastPwrStateActive: begin
// only in active state, allow processor to execute
fetch_en_d = lc_ctrl_pkg::On;
// when handling reset request or low power entry of any
// kind, stop processor from fetching
if (reset_req || low_power_entry_i) begin
fetch_en_d = lc_ctrl_pkg::Off;
reset_cause_d = ResetUndefined;
state_d = FastPwrStateDisClks;
end
end
FastPwrStateDisClks: begin
ip_clk_en_d = 1'b0;
if (clks_disabled) begin
state_d = reset_req ? FastPwrStateNvmShutDown : FastPwrStateFallThrough;
low_power_d = ~reset_req;
end else begin
// escalation was received, skip all handshaking and directly reset
state_d = direct_rst_req ? FastPwrStateNvmShutDown : state_q;
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 = FastPwrStateRomCheckDone;
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 = FastPwrStateRomCheckDone;
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
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{(hw_rst_req |
direct_rst_req |
sw_rst_req) |
(ndmreset_req &
lc_ctrl_pkg::lc_tx_test_false_loose(lc_dft_en_i))}};
state_d = FastPwrStateResetWait;
end
FastPwrStateResetWait: begin
rst_lc_req_d = {PowerDomains{1'b1}};
clr_slow_req_o = reset_reqs_i[ResetMainPwrIdx];
// The main power reset request is checked here specifically because it is
// the only reset request in the system that operates on the POR domain.
// This has to be the case since it would otherwise not be able to monitor
// the non-always-on domains.
//
// As a result of this, the normal reset process does not automatically
// wipe out the reset request, so we specifically clear it and wait for it to be
// cleared before proceeding. This also implies if the system is under a persistent
// glitch, or if someone just turned off the power before pwrmgr turns it off itself,
// we will stay stuck here and perpetually hold the system in reset.
if (reset_valid && !reset_reqs_i[ResetMainPwrIdx]) begin
state_d = FastPwrStateLowPower;
end
end
// Terminal state, kill everything
// SEC_CM: FSM.TERMINAL
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)
if (fsm_invalid_i) begin
// the slow fsm is completely out of sync, transition to terminal state
state_d = FastPwrStateInvalid;
end
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[HwResetWidth-1:0];
// main and io clocks are only turned on/off as part of normal
// power sequence
assign ips_clk_en_o.main_ip_clk_en = ip_clk_en_q;
assign ips_clk_en_o.io_ip_clk_en = ip_clk_en_q;
prim_flop #(
.Width(1),
.ResetValue(1'b0)
) u_usb_ip_clk_en (
.clk_i,
.rst_ni,
.d_i(ip_clk_en_d & usb_ip_clk_en_i),
.q_o(ips_clk_en_o.usb_ip_clk_en)
);
assign usb_ip_clk_status_o = clk_en_status_i.usb_status;
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