../src/lowrisc_ip_pwrmgr_0.1/rtl/pwrmgr_cdc.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 CDC handling
6: //
7:
8: `include "prim_assert.sv"
9:
10: module pwrmgr_cdc import pwrmgr_pkg::*;
11: (
12: // Clocks and resets
13: input clk_slow_i,
14: input clk_i,
15: input rst_slow_ni,
16: input rst_ni,
17:
18: // slow domain signals,
19: input slow_req_pwrup_i,
20: input slow_ack_pwrdn_i,
21: input slow_pwrup_cause_toggle_i,
22: input pwrup_cause_e slow_pwrup_cause_i,
23: output pwrmgr_reg_pkg::pwrmgr_reg2hw_wakeup_en_reg_t slow_wakeup_en_o,
24: output pwrmgr_reg_pkg::pwrmgr_reg2hw_reset_en_reg_t slow_reset_en_o,
25: output logic slow_main_pd_no,
26: output logic slow_io_clk_en_o,
27: output logic slow_core_clk_en_o,
28: output logic slow_req_pwrdn_o,
29: output logic slow_ack_pwrup_o,
30: output pwr_ast_rsp_t slow_ast_o,
31: output pwr_peri_t slow_peri_reqs_o,
32: input pwr_peri_t slow_peri_reqs_masked_i,
33:
34: // fast domain signals
35: input req_pwrdn_i,
36: input ack_pwrup_i,
37: input cfg_cdc_sync_i,
38: input pwrmgr_reg_pkg::pwrmgr_reg2hw_wakeup_en_reg_t wakeup_en_i,
39: input pwrmgr_reg_pkg::pwrmgr_reg2hw_reset_en_reg_t reset_en_i,
40: input main_pd_ni,
41: input io_clk_en_i,
42: input core_clk_en_i,
43: output logic ack_pwrdn_o,
44: output logic req_pwrup_o,
45: output pwrup_cause_e pwrup_cause_o,
46: output pwr_peri_t peri_reqs_o,
47: output logic cdc_sync_done_o,
48:
49: // peripheral inputs, mixed domains
50: input pwr_peri_t peri_i,
51:
52: // AST inputs, unknown domain
53: input pwr_ast_rsp_t ast_i
54:
55: );
56:
57: ////////////////////////////////
58: // Sync from clk_i to clk_slow_i
59: ////////////////////////////////
60:
61: logic slow_cdc_sync;
62: pwr_ast_rsp_t slow_ast_q, slow_ast_q2;
63:
64: prim_flop_2sync # (
65: .Width(1)
66: ) i_req_pwrdn_sync (
67: .clk_i(clk_slow_i),
68: .rst_ni(rst_slow_ni),
69: .d(req_pwrdn_i),
70: .q(slow_req_pwrdn_o)
71: );
72:
73: prim_flop_2sync # (
74: .Width(1)
75: ) i_ack_pwrup_sync (
76: .clk_i(clk_slow_i),
77: .rst_ni(rst_slow_ni),
78: .d(ack_pwrup_i),
79: .q(slow_ack_pwrup_o)
80: );
81:
82: prim_pulse_sync i_slow_cdc_sync (
83: .clk_src_i(clk_i),
84: .rst_src_ni(rst_ni),
85: .src_pulse_i(cfg_cdc_sync_i),
86: .clk_dst_i(clk_slow_i),
87: .rst_dst_ni(rst_slow_ni),
88: .dst_pulse_o(slow_cdc_sync)
89: );
90:
91: // Even though this is multi-bit, the bits are individual request lines.
92: // So there is no general concern about recombining as there is
93: // no intent to use them in a related manner.
94: prim_flop_2sync # (
95: .Width(HwRstReqs + WakeUpPeris)
96: ) i_slow_ext_req_sync (
97: .clk_i (clk_slow_i),
98: .rst_ni (rst_slow_ni),
99: .d (peri_i),
100: .q (slow_peri_reqs_o)
101: );
102:
103:
104: // Some of the AST signals are multi-bits themselves (such as clk_val)
105: // thus they need to be delayed one more stage to check for stability
106: prim_flop_2sync # (
107: .Width($bits(pwr_ast_rsp_t)),
108: .ResetValue(PWR_AST_RSP_SYNC_DEFAULT)
109: ) i_ast_sync (
110: .clk_i (clk_slow_i),
111: .rst_ni (rst_slow_ni),
112: .d (ast_i),
113: .q (slow_ast_q)
114: );
115:
116: always_ff @(posedge clk_slow_i or negedge rst_slow_ni) begin
117: if (!rst_slow_ni) begin
118: slow_ast_q2 <= PWR_AST_RSP_SYNC_DEFAULT;
119: end else begin
120: slow_ast_q2 <= slow_ast_q;
121: end
122: end
123:
124: // if possible, we should simulate below with random delays through
125: // flop_2sync
126: always_ff @(posedge clk_slow_i or negedge rst_slow_ni) begin
127: if (!rst_slow_ni) begin
128: slow_ast_o <= PWR_AST_RSP_SYNC_DEFAULT;
129: end else if (slow_ast_q2 == slow_ast_q) begin
130: // Output only updates whenever sync and delayed outputs both agree.
131: // If there are delays in sync, this will result in a 1 cycle difference
132: // and the output will hold the previous value
133: slow_ast_o <= slow_ast_q2;
134: end
135: end
136:
137: // only register configurations can be sync'd using slow_cdc_sync
138: always_ff @(posedge clk_slow_i or negedge rst_slow_ni) begin
139: if (!rst_slow_ni) begin
140: slow_wakeup_en_o <= '0;
141: slow_reset_en_o <= '0;
142: slow_main_pd_no <= '0;
143: slow_io_clk_en_o <= '0;
144: slow_core_clk_en_o <= '0;
145: end else if (slow_cdc_sync) begin
146: slow_wakeup_en_o <= wakeup_en_i;
147: slow_reset_en_o <= reset_en_i;
148: slow_main_pd_no <= main_pd_ni;
149: slow_io_clk_en_o <= io_clk_en_i;
150: slow_core_clk_en_o <= core_clk_en_i;
151: end
152: end
153:
154: ////////////////////////////////
155: // Sync from clk_slow_i to clk_i
156: ////////////////////////////////
157:
158: logic pwrup_cause_toggle_q, pwrup_cause_toggle_q2;
159: logic pwrup_cause_chg;
160:
161: prim_flop_2sync # (
162: .Width(1)
163: ) i_req_pwrup_sync (
164: .clk_i,
165: .rst_ni,
166: .d(slow_req_pwrup_i),
167: .q(req_pwrup_o)
168: );
169:
170: prim_flop_2sync # (
171: .Width(1)
172: ) i_ack_pwrdn_sync (
173: .clk_i,
174: .rst_ni,
175: .d(slow_ack_pwrdn_i),
176: .q(ack_pwrdn_o)
177: );
178:
179: prim_flop_2sync # (
180: .Width(1)
181: ) i_pwrup_chg_sync (
182: .clk_i,
183: .rst_ni,
184: .d(slow_pwrup_cause_toggle_i),
185: .q(pwrup_cause_toggle_q)
186: );
187:
188: prim_pulse_sync i_scdc_sync (
189: .clk_src_i(clk_slow_i),
190: .rst_src_ni(rst_slow_ni),
191: .src_pulse_i(slow_cdc_sync),
192: .clk_dst_i(clk_i),
193: .rst_dst_ni(rst_ni),
194: .dst_pulse_o(cdc_sync_done_o)
195: );
196:
197: always_ff @(posedge clk_i or negedge rst_ni) begin
198: if (!rst_ni) begin
199: pwrup_cause_toggle_q2 <= 1'b0;
200: end else begin
201: pwrup_cause_toggle_q2 <= pwrup_cause_toggle_q;
202: end
203: end
204:
205: assign pwrup_cause_chg = pwrup_cause_toggle_q2 ^ pwrup_cause_toggle_q;
206:
207: always_ff @(posedge clk_i or negedge rst_ni) begin
208: if (!rst_ni) begin
209: pwrup_cause_o <= Por;
210: end else if (pwrup_cause_chg) begin
211: pwrup_cause_o <= slow_pwrup_cause_i;
212: end
213: end
214:
215: prim_flop_2sync # (
216: .Width(HwRstReqs + WakeUpPeris)
217: ) i_ext_req_sync (
218: .clk_i,
219: .rst_ni,
220: .d (slow_peri_reqs_masked_i),
221: .q (peri_reqs_o)
222: );
223:
224:
225: endmodule
226:
227:
228: // An alternative solution relying on finding slow clock edges
229: // Keep it around just in case
230:
231: /*
232: // finds a clk_slow edge in clk domain to know when it is safe to sync over
233: // this signal is only safe to use within the pwrmgr module when the source
234: // and destination clock domains are both clear
235: logic cdc_safe;
236:
237: // pwrup is synced directly as it acts as a start signal to the pulse module
238: prim_flop_2sync # (
239: .Width(1)
240: ) i_pwrup_sync (
241: .clk_i,
242: .rst_ni,
243: .d(slow_req_pwrup),
244: .q(req_pwrup)
245: );
246:
247: pwrmgr_cdc_pulse i_cdc_pulse (
248: .clk_slow_i,
249: .clk_i,
250: .rst_ni,
251: .start_i(req_pwrup),
252: .stop_i(req_pwrdn),
253: .pulse_o(cdc_safe)
254: );
255:
256: always_ff @(posedge clk_i or negedge rst_ni) begin
257: if (!rst_ni) begin
258: ack_pwrdn <= '0;
259: pwrup_cause <= Por;
260: end else if (cdc_safe) begin
261: ack_pwrdn <= slow_ack_pwrdn;
262: pwrup_cause <= slow_pwrup_cause;
263: end
264: end
265:
266: ////////////////////////////
267: /// cdc handling - clk_slow_i
268: ////////////////////////////
269:
270: always_ff @(posedge clk_i or negedge rst_ni) begin
271: if (!rst_ni) begin
272: slow_wakeup_en <= '0;
273: slow_reset_en <= '0;
274: slow_main_pdb <= '0;
275: slow_io_clk_en <= '0;
276: slow_core_clk_en <= '0;
277: slow_ack_pwrup <= '0;
278: slow_req_pwrdn <= '0;
279: end else if (cdc_safe) begin
280: slow_wakeup_en <= reg2hw.wakeup_en.q;
281: slow_reset_en <= reg2hw.reset_en.q;
282: slow_main_pdb <= reg2hw.control.main_pdb.q;
283: slow_io_clk_en <= reg2hw.control.io_clk_en.q;
284: slow_core_clk_en <= reg2hw.control.core_clk_en.q;
285: slow_ack_pwrup <= ack_pwrup;
286: slow_req_pwrdn <= req_pwrdn;
287: end
288: end
289:
290: // TODO
291: // Need to vote on the differential signals to ensure they are stable
292: prim_flop_2sync # (
293: .Width($bits(pwr_ast_rsp_t))
294: ) i_pok_sync (
295: .clk_i (clk_slow_i),
296: .rst_ni (rst_slow_ni),
297: .d (pwr_ast_i),
298: .q (slow_ast_q)
299: );
300: */
301: