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