blob: 60d832024f9ace3eaef02e18f91ac9cbb23a5caf [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 clr_slow_ack_i,
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_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 direct_rst_req;
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;
assign direct_rst_req = reset_reqs_i[ResetEscIdx] |
reset_reqs_i[ResetMainPwrIdx];
// 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
logic [FastPwrStateWidth-1:0] state_raw_q;
assign state_q = fast_pwr_state_e'(state_raw_q);
// SEC_CM: FSM.SPARSE
prim_sparse_fsm_flop #(
.StateEnumT(fast_pwr_state_e),
.Width(FastPwrStateWidth),
.ResetValue(FastPwrStateWidth'(FastPwrStateLowPower))
) u_state_regs (
.clk_i,
.rst_ni,
.state_i ( state_d ),
.state_o ( state_raw_q )
);
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)
);
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
logic rom_intg_chk_dis;
assign rom_intg_chk_dis = (lc_dft_en_i == lc_ctrl_pkg::On) &
(lc_hw_debug_en_i == lc_ctrl_pkg::On);
logic rom_intg_chk_done;
assign rom_intg_chk_done = mubi4_test_true_strict(rom_ctrl_done_i);
logic rom_intg_chk_ok;
assign rom_intg_chk_ok = rom_intg_chk_dis ? rom_intg_chk_done :
rom_intg_chk_done & mubi4_test_true_strict(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
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 = 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 = 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_intg_chk_ok) begin
state_d = FastPwrStateStrap;
end
end
FastPwrStateStrap: begin
strap_o = ~strap_sampled;
state_d = FastPwrStateActive;
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 = 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
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}};
clr_slow_req_o = 1'b1;
// okay to be pending here, since reset is already asserted
// if the handshake were attacked in any way, the device
// would simply be dead.
if (reset_valid && (clr_slow_req_o && clr_slow_ack_i)) 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)
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