../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: