| // Copyright lowRISC contributors. | 
 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | // The overall clock manager | 
 |  | 
 | `include "prim_assert.sv" | 
 |  | 
 |  | 
 |  | 
 | module clkmgr import clkmgr_pkg::*; ( | 
 |   // Primary module clocks | 
 |   input clk_i, | 
 |   input rst_ni, | 
 |   input clk_main_i, | 
 |   input rst_main_ni, | 
 |   input clk_fixed_i, | 
 |   input rst_fixed_ni, | 
 |   input clk_usb_48mhz_i, | 
 |   input rst_usb_48mhz_ni, | 
 |  | 
 |   // Bus Interface | 
 |   input tlul_pkg::tl_h2d_t tl_i, | 
 |   output tlul_pkg::tl_d2h_t tl_o, | 
 |  | 
 |   // pwrmgr interface | 
 |   input pwrmgr_pkg::pwr_clk_req_t pwr_i, | 
 |   output pwrmgr_pkg::pwr_clk_rsp_t pwr_o, | 
 |  | 
 |   // dft interface | 
 |   input clk_dft_t dft_i, | 
 |  | 
 |   // idle hints | 
 |   input clk_hint_status_t status_i, | 
 |  | 
 |   // clock output interface | 
 |   output clkmgr_out_t clocks_o | 
 |  | 
 | ); | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Register Interface | 
 |   //////////////////////////////////////////////////// | 
 |  | 
 |   clkmgr_reg_pkg::clkmgr_reg2hw_t reg2hw; | 
 |   clkmgr_reg_pkg::clkmgr_hw2reg_t hw2reg; | 
 |  | 
 |   clkmgr_reg_top u_reg ( | 
 |     .clk_i, | 
 |     .rst_ni, | 
 |     .tl_i, | 
 |     .tl_o, | 
 |     .reg2hw, | 
 |     .hw2reg, | 
 |     .devmode_i(1'b1) | 
 |   ); | 
 |  | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Root gating | 
 |   //////////////////////////////////////////////////// | 
 |  | 
 |   // the rst_ni connection below is incorrect, need to find a proper reset in the sequence to use | 
 |   // if the clkmgr is always on, can use por synced directly | 
 |   // if not, then need to generate something ahead of lc/sys | 
 |  | 
 |   logic async_roots_en; | 
 |   logic roots_en_q2, roots_en_q1, roots_en_d; | 
 |   logic clk_main_root; | 
 |   logic clk_main_en; | 
 |   logic clk_fixed_root; | 
 |   logic clk_fixed_en; | 
 |   logic clk_usb_48mhz_root; | 
 |   logic clk_usb_48mhz_en; | 
 |  | 
 |   prim_clock_gating_sync i_main_cg ( | 
 |     .clk_i(clk_main_i), | 
 |     .rst_ni(rst_main_ni), | 
 |     .test_en_i(dft_i.test_en), | 
 |     .async_en_i(pwr_i.ip_clk_en), | 
 |     .en_o(clk_main_en), | 
 |     .clk_o(clk_main_root) | 
 |   ); | 
 |   prim_clock_gating_sync i_fixed_cg ( | 
 |     .clk_i(clk_fixed_i), | 
 |     .rst_ni(rst_fixed_ni), | 
 |     .test_en_i(dft_i.test_en), | 
 |     .async_en_i(pwr_i.ip_clk_en), | 
 |     .en_o(clk_fixed_en), | 
 |     .clk_o(clk_fixed_root) | 
 |   ); | 
 |   prim_clock_gating_sync i_usb_48mhz_cg ( | 
 |     .clk_i(clk_usb_48mhz_i), | 
 |     .rst_ni(rst_usb_48mhz_ni), | 
 |     .test_en_i(dft_i.test_en), | 
 |     .async_en_i(pwr_i.ip_clk_en), | 
 |     .en_o(clk_usb_48mhz_en), | 
 |     .clk_o(clk_usb_48mhz_root) | 
 |   ); | 
 |  | 
 |   // an async OR of all the synchronized enables | 
 |   assign async_roots_en = | 
 |     clk_main_en | | 
 |     clk_fixed_en | | 
 |     clk_usb_48mhz_en; | 
 |  | 
 |   // Sync the OR back into clkmgr domain for feedback to pwrmgr. | 
 |   // Since the signal is combo / converged on the other side, de-bounce | 
 |   // the signal prior to output | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_roots_en_sync ( | 
 |     .clk_i, | 
 |     .rst_ni, | 
 |     .d_i(async_roots_en), | 
 |     .q_o(roots_en_d) | 
 |   ); | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       roots_en_q1 <= 1'b0; | 
 |       roots_en_q2 <= 1'b0; | 
 |     end else begin | 
 |       roots_en_q1 <= roots_en_d; | 
 |  | 
 |       if (roots_en_q1 == roots_en_d) begin | 
 |         roots_en_q2 <= roots_en_q1; | 
 |       end | 
 |     end | 
 |   end | 
 |  | 
 |   assign pwr_o.roots_en = roots_en_q2; | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Clocks with only root gate | 
 |   //////////////////////////////////////////////////// | 
 |   assign clocks_o.clk_main_infra = clk_main_root; | 
 |   assign clocks_o.clk_fixed_infra = clk_fixed_root; | 
 |   assign clocks_o.clk_fixed_secure = clk_fixed_root; | 
 |   assign clocks_o.clk_main_secure = clk_main_root; | 
 |   assign clocks_o.clk_fixed_timers = clk_fixed_root; | 
 |   assign clocks_o.clk_proc_main = clk_main_root; | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Software direct control group | 
 |   //////////////////////////////////////////////////// | 
 |  | 
 |   // the rst_ni connection below is incorrect, need to find a proper reset in the sequence to use | 
 |   // if the clkmgr is always on, can use por synced directly | 
 |   // if not, then need to generate something ahead of lc/sys | 
 |   logic clk_fixed_peri_sw_en; | 
 |   logic clk_usb_48mhz_peri_sw_en; | 
 |  | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_clk_fixed_peri_sw_en_sync ( | 
 |     .clk_i(clk_fixed_i), | 
 |     .rst_ni(rst_fixed_ni), | 
 |     .d_i(reg2hw.clk_enables.clk_fixed_peri_en.q), | 
 |     .q_o(clk_fixed_peri_sw_en) | 
 |   ); | 
 |  | 
 |   prim_clock_gating i_clk_fixed_peri_cg ( | 
 |     .clk_i(clk_fixed_i), | 
 |     .en_i(clk_fixed_peri_sw_en & clk_fixed_en), | 
 |     .test_en_i(dft_i.test_en), | 
 |     .clk_o(clocks_o.clk_fixed_peri) | 
 |   ); | 
 |  | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_clk_usb_48mhz_peri_sw_en_sync ( | 
 |     .clk_i(clk_usb_48mhz_i), | 
 |     .rst_ni(rst_usb_48mhz_ni), | 
 |     .d_i(reg2hw.clk_enables.clk_usb_48mhz_peri_en.q), | 
 |     .q_o(clk_usb_48mhz_peri_sw_en) | 
 |   ); | 
 |  | 
 |   prim_clock_gating i_clk_usb_48mhz_peri_cg ( | 
 |     .clk_i(clk_usb_48mhz_i), | 
 |     .en_i(clk_usb_48mhz_peri_sw_en & clk_usb_48mhz_en), | 
 |     .test_en_i(dft_i.test_en), | 
 |     .clk_o(clocks_o.clk_usb_48mhz_peri) | 
 |   ); | 
 |  | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Software hint group | 
 |   // The idle hint feedback is assumed to be synchronous to the | 
 |   // clock target | 
 |   //////////////////////////////////////////////////// | 
 |  | 
 |   // the rst_ni connection below is incorrect, need to find a proper reset in the sequence to use | 
 |   // if the clkmgr is always on, can use por synced directly | 
 |   // if not, then need to generate something ahead of lc/sys | 
 |  | 
 |   logic clk_main_aes_hint; | 
 |   logic clk_main_aes_en; | 
 |   logic clk_main_hmac_hint; | 
 |   logic clk_main_hmac_en; | 
 |  | 
 |   assign clk_main_aes_en = clk_main_aes_hint | ~status_i.idle[0]; | 
 |  | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_clk_main_aes_hint_sync ( | 
 |     .clk_i(clk_main_i), | 
 |     .rst_ni(rst_main_ni), | 
 |     .d_i(reg2hw.clk_hints.clk_main_aes_hint.q), | 
 |     .q_o(clk_main_aes_hint) | 
 |   ); | 
 |  | 
 |   prim_clock_gating i_clk_main_aes_cg ( | 
 |     .clk_i(clk_main_i), | 
 |     .en_i(clk_main_aes_en & clk_main_en), | 
 |     .test_en_i(dft_i.test_en), | 
 |     .clk_o(clocks_o.clk_main_aes) | 
 |   ); | 
 |  | 
 |   assign clk_main_hmac_en = clk_main_hmac_hint | ~status_i.idle[1]; | 
 |  | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_clk_main_hmac_hint_sync ( | 
 |     .clk_i(clk_main_i), | 
 |     .rst_ni(rst_main_ni), | 
 |     .d_i(reg2hw.clk_hints.clk_main_hmac_hint.q), | 
 |     .q_o(clk_main_hmac_hint) | 
 |   ); | 
 |  | 
 |   prim_clock_gating i_clk_main_hmac_cg ( | 
 |     .clk_i(clk_main_i), | 
 |     .en_i(clk_main_hmac_en & clk_main_en), | 
 |     .test_en_i(dft_i.test_en), | 
 |     .clk_o(clocks_o.clk_main_hmac) | 
 |   ); | 
 |  | 
 |  | 
 |   // state readback | 
 |   assign hw2reg.clk_hints_status.clk_main_aes_val.de = 1'b1; | 
 |   assign hw2reg.clk_hints_status.clk_main_aes_val.d = clk_main_aes_en; | 
 |   assign hw2reg.clk_hints_status.clk_main_hmac_val.de = 1'b1; | 
 |   assign hw2reg.clk_hints_status.clk_main_hmac_val.d = clk_main_hmac_en; | 
 |  | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Assertions | 
 |   //////////////////////////////////////////////////// | 
 |  | 
 |  | 
 | endmodule // clkmgr |