../src/lowrisc_ip_pwrmgr_0.1/rtl/pwrmgr_slow_fsm.sv Cov: 100%
1: // Copyright lowRISC contributors.
2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3: // SPDX-License-Identifier: Apache-2.0
4: //
5: // Power Manager Slow FSM
6: //
7:
8: `include "prim_assert.sv"
9:
10: module pwrmgr_slow_fsm import pwrmgr_pkg::*; (
11: input clk_i,
12: input rst_ni,
13:
14: // sync'ed requests from peripherals
15: input wakeup_i,
16: input reset_req_i,
17:
18: // interface with fast fsm
19: output logic req_pwrup_o,
20: output logic pwrup_cause_toggle_o,
21: output pwrup_cause_e pwrup_cause_o,
22: input ack_pwrup_i,
23: input req_pwrdn_i,
24: output logic ack_pwrdn_o,
25:
26: // low power entry configuration
27: input main_pd_ni,
28: input io_clk_en_i,
29: input core_clk_en_i,
30:
31: // AST interface
32: input pwr_ast_rsp_t ast_i,
33: output pwr_ast_req_t ast_o
34: );
35:
36: // state enum
37: typedef enum logic [3:0] {
38: StReset,
39: StLowPower,
40: StMainPowerOn,
41: StClocksOn,
42: StReqPwrUp,
43: StIdle,
44: StAckPwrDn,
45: StClocksOff,
46: StMainPowerOff
47: } state_e;
48:
49: state_e state_q, state_d;
50:
51: // All signals crossing over to other domain must be flopped
52: pwrup_cause_e cause_q, cause_d;
53: logic cause_toggle_q, cause_toggle_d;
54: logic req_pwrup_q, req_pwrup_d;
55: logic ack_pwrdn_q, ack_pwrdn_d;
56:
57: // All power signals and signals going to analog logic are flopped to avoid transitional glitches
58: logic pd_nq, pd_nd;
59: logic pwr_clamp_q, pwr_clamp_d;
60: logic core_clk_en_q, core_clk_en_d;
61: logic io_clk_en_q, io_clk_en_d;
62:
63: logic all_clks_valid;
64: logic all_clks_invalid;
65:
66: // TODO: This should come from an AST package long term and not be hardcoded.
67: // Tracked in #2010
68: assign all_clks_valid = ast_i.core_clk_val == 2'b10 && ast_i.io_clk_val == 2'b10;
69:
70: // if clock were configured to turn off, make sure val is 2'b01
71: assign all_clks_invalid = (core_clk_en_i | ast_i.core_clk_val == 2'b01) &&
72: (io_clk_en_i | ast_i.io_clk_val == 2'b01);
73:
74: always_ff @(posedge clk_i or negedge rst_ni) begin
75: if (!rst_ni) begin
76: state_q <= StReset;
77: cause_q <= Por;
78: cause_toggle_q <= 1'b0;
79: pd_nq <= 1'b0;
80: pwr_clamp_q <= 1'b1;
81: core_clk_en_q <= 1'b0;
82: io_clk_en_q <= 1'b0;
83: req_pwrup_q <= 1'b0;
84: ack_pwrdn_q <= 1'b0;
85: end else begin
86: state_q <= state_d;
87: cause_q <= cause_d;
88: cause_toggle_q <= cause_toggle_d;
89: pd_nq <= pd_nd;
90: pwr_clamp_q <= pwr_clamp_d;
91: core_clk_en_q <= core_clk_en_d;
92: io_clk_en_q <= io_clk_en_d;
93: req_pwrup_q <= req_pwrup_d;
94: ack_pwrdn_q <= ack_pwrdn_d;
95: end
96: end
97:
98: always_comb begin
99: state_d = state_q;
100: cause_d = cause_q;
101: pd_nd = pd_nq;
102: cause_toggle_d = cause_toggle_q;
103: pwr_clamp_d = pwr_clamp_q;
104: core_clk_en_d = core_clk_en_q;
105: io_clk_en_d = io_clk_en_q;
106:
107: req_pwrup_d = req_pwrup_q;
108: ack_pwrdn_d = ack_pwrdn_q;
109:
110: unique case(state_q)
111:
112: StReset: begin
113: state_d = StMainPowerOn;
114: cause_d = Por;
115: end
116:
117: StLowPower: begin
118: // reset request behaves identically to a wakeup, other than the power-up cause being
119: // different
120: if (wakeup_i || reset_req_i) begin
121: state_d = StMainPowerOn;
122: cause_toggle_d = ~cause_toggle_q;
123: cause_d = reset_req_i ? Reset : Wake;
124: end
125: end
126:
127: StMainPowerOn: begin
128: pd_nd = 1'b1;
129:
130: if (ast_i.main_pok) begin
131: pwr_clamp_d = 1'b0;
132: state_d = StClocksOn;
133: end
134: end
135:
136: StClocksOn: begin
137: core_clk_en_d = 1'b1;
138: io_clk_en_d = 1'b1;
139:
140: if (all_clks_valid) begin
141: state_d = StReqPwrUp;
142: end
143: end
144:
145: StReqPwrUp: begin
146: req_pwrup_d = 1'b1;
147:
148: // req_pwrdn_i should be 0 here to indicate
149: // the request from the previous round has definitely completed
150: if (ack_pwrup_i && !req_pwrdn_i) begin
151: req_pwrup_d = 1'b0;
152: state_d = StIdle;
153: end
154: end
155:
156: StIdle: begin
157: // ack_pwrup_i should be 0 here to indicate
158: // the ack from the previous round has definitively completed
159: if (req_pwrdn_i && !ack_pwrup_i) begin
160: state_d = StAckPwrDn;
161: end
162: end
163:
164: StAckPwrDn: begin
165: ack_pwrdn_d = 1'b1;
166:
167: if (!req_pwrdn_i) begin
168: ack_pwrdn_d = 1'b0;
169: state_d = StClocksOff;
170: end
171: end
172:
173: StClocksOff: begin
174: core_clk_en_d = core_clk_en_i;
175: io_clk_en_d = io_clk_en_i;
176:
177: if (all_clks_invalid) begin
178: // if main power is turned off, assert clamp ahead
179: pwr_clamp_d = ~main_pd_ni;
180: state_d = StMainPowerOff;
181: end
182: end
183:
184: StMainPowerOff: begin
185: pd_nd = main_pd_ni;
186:
187: // if power is never turned off, proceed directly to low power state
188: if (!ast_i.main_pok | main_pd_ni) begin
189: state_d = StLowPower;
190: end
191: end
192:
193: // Very terminal state, kill everything
194: default: begin
195: pd_nd = 1'b0;
196: pwr_clamp_d = 1'b1;
197: core_clk_en_d = 1'b0;
198: io_clk_en_d = 1'b0;
199: end
200:
201:
202: endcase // unique case (state_q)
203: end // always_comb
204:
205:
206: assign pwrup_cause_o = cause_q;
207: assign pwrup_cause_toggle_o = cause_toggle_q;
208: assign req_pwrup_o = req_pwrup_q;
209: assign ack_pwrdn_o = ack_pwrdn_q;
210:
211: assign ast_o.core_clk_en = core_clk_en_q;
212: assign ast_o.io_clk_en = io_clk_en_q;
213: assign ast_o.main_pd_n = pd_nq;
214: assign ast_o.pwr_clamp = pwr_clamp_q;
215:
216: // This is hardwired to 1 all the time
217: assign ast_o.slow_clk_en = 1'b1;
218:
219:
220: ////////////////////////////
221: /// Unused
222: ////////////////////////////
223:
224: logic [1:0] unused_slow_clk_val;
225: assign unused_slow_clk_val = ast_i.slow_clk_val;
226:
227:
228: endmodule
229: