blob: cc0103016c3f487c137383790ce271631842c11a [file] [log] [blame]
// 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
// 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;
% 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
////////////////////////////////////////////////////
// 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
% 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
////////////////////////////////////////////////////
// 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
% 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