| // 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" | 
 |  | 
 | <% | 
 | clks_attr = cfg['clocks'] | 
 | srcs = clks_attr['srcs'] | 
 | %> | 
 |  | 
 | module clkmgr import clkmgr_pkg::*; ( | 
 |   // Primary module control clocks and resets | 
 |   // This drives the register interface | 
 |   input clk_i, | 
 |   input rst_ni, | 
 |  | 
 |   // System clocks and resets | 
 |   // These are the source clocks for the system | 
 | % for src in srcs: | 
 |   input clk_${src['name']}_i, | 
 |   % if src['aon'] == 'no': | 
 |   input rst_${src['name']}_ni, | 
 |   % endif | 
 | % endfor | 
 |  | 
 |   // Resets for derived clocks | 
 |   // clocks are derived locally | 
 | % for src in div_srcs: | 
 |   input rst_${src['name']}_ni, | 
 | % endfor | 
 |  | 
 |   // 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) | 
 |   ); | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Divided clocks | 
 |   //////////////////////////////////////////////////// | 
 | % for src in div_srcs: | 
 |   logic clk_${src['name']}_i; | 
 | % endfor | 
 |  | 
 | % for src in div_srcs: | 
 |   assign clk_${src['name']}_i = clk_${src['src']}_i; | 
 | % endfor | 
 |  | 
 |  | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Feed through clocks | 
 |   // Feed through clocks do not actually need to be in clkmgr, as they are | 
 |   // completely untouched. The only reason they are here is for easier | 
 |   // bundling management purposes through clocks_o | 
 |   //////////////////////////////////////////////////// | 
 | % for k,v in ft_clks.items(): | 
 |   assign clocks_o.${k} = clk_${v}_i; | 
 | % endfor | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Root gating | 
 |   //////////////////////////////////////////////////// | 
 |  | 
 |   logic async_roots_en; | 
 |   logic roots_en_q2, roots_en_q1, roots_en_d; | 
 | % for src in rg_srcs: | 
 |   logic clk_${src}_root; | 
 |   logic clk_${src}_en; | 
 | % endfor | 
 |  | 
 | % for src in rg_srcs: | 
 |   prim_clock_gating_sync i_${src}_cg ( | 
 |     .clk_i(clk_${src}_i), | 
 |     .rst_ni(rst_${src}_ni), | 
 |     .test_en_i(dft_i.test_en), | 
 |     .async_en_i(pwr_i.ip_clk_en), | 
 |     .en_o(clk_${src}_en), | 
 |     .clk_o(clk_${src}_root) | 
 |   ); | 
 | % endfor | 
 |  | 
 |   // an async OR of all the synchronized enables | 
 |   assign async_roots_en = | 
 | % for src in rg_srcs: | 
 |     % if loop.last: | 
 |     clk_${src}_en; | 
 |     % else: | 
 |     clk_${src}_en | | 
 |     % endif | 
 | % endfor | 
 |  | 
 |   // 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(async_roots_en), | 
 |     .q(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 | 
 |   //////////////////////////////////////////////////// | 
 | % for k,v in rg_clks.items(): | 
 |   assign clocks_o.${k} = clk_${v}_root; | 
 | % endfor | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Software direct control group | 
 |   //////////////////////////////////////////////////// | 
 |  | 
 | % for k in sw_clks: | 
 |   logic ${k}_sw_en; | 
 | % endfor | 
 |  | 
 | % for k,v in sw_clks.items(): | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_${k}_sw_en_sync ( | 
 |     .clk_i(clk_${v}_i), | 
 |     .rst_ni(rst_${v}_ni), | 
 |     .d(reg2hw.clk_enables.${k}_en.q), | 
 |     .q(${k}_sw_en) | 
 |   ); | 
 |  | 
 |   prim_clock_gating i_${k}_cg ( | 
 |     .clk_i(clk_${v}_i), | 
 |     .en_i(${k}_sw_en & clk_${v}_en), | 
 |     .test_en_i(dft_i.test_en), | 
 |     .clk_o(clocks_o.${k}) | 
 |   ); | 
 |  | 
 | % endfor | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Software hint group | 
 |   // The idle hint feedback is assumed to be synchronous to the | 
 |   // clock target | 
 |   //////////////////////////////////////////////////// | 
 |  | 
 | % for k in hint_clks: | 
 |   logic ${k}_hint; | 
 |   logic ${k}_en; | 
 | % endfor | 
 |  | 
 | % for k,v in hint_clks.items(): | 
 |   assign ${k}_en = ${k}_hint | ~status_i.idle[${loop.index}]; | 
 |  | 
 |   prim_flop_2sync #( | 
 |     .Width(1) | 
 |   ) i_${k}_hint_sync ( | 
 |     .clk_i(clk_${v}_i), | 
 |     .rst_ni(rst_${v}_ni), | 
 |     .d(reg2hw.clk_hints.${k}_hint.q), | 
 |     .q(${k}_hint) | 
 |   ); | 
 |  | 
 |   prim_clock_gating i_${k}_cg ( | 
 |     .clk_i(clk_${v}_i), | 
 |     .en_i(${k}_en & clk_${v}_en), | 
 |     .test_en_i(dft_i.test_en), | 
 |     .clk_o(clocks_o.${k}) | 
 |   ); | 
 |  | 
 | % endfor | 
 |  | 
 |   // state readback | 
 | % for k,v in hint_clks.items(): | 
 |   assign hw2reg.clk_hints_status.${k}_val.de = 1'b1; | 
 |   assign hw2reg.clk_hints_status.${k}_val.d = ${k}_en; | 
 | % endfor | 
 |  | 
 |  | 
 |   //////////////////////////////////////////////////// | 
 |   // Assertions | 
 |   //////////////////////////////////////////////////// | 
 |  | 
 |  | 
 | endmodule // rstmgr |