blob: b020562865fd7b0d9e497feedf56a08627010164 [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
<%
from topgen.lib import Name
%>\
`include "prim_assert.sv"
module clkmgr
import clkmgr_pkg::*;
import clkmgr_reg_pkg::*;
import lc_ctrl_pkg::lc_tx_t;
import prim_mubi_pkg::mubi4_t;
#(
parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}
) (
// Primary module control clocks and resets
// This drives the register interface
input clk_i,
input rst_ni,
input rst_shadowed_ni,
// System clocks and resets
// These are the source clocks for the system
% for src in clocks.srcs.values():
input clk_${src.name}_i,
input rst_${src.name}_ni,
% endfor
// Resets for derived clocks
// clocks are derived locally
% for src_name in clocks.derived_srcs:
input rst_${src_name}_ni,
% endfor
// Resets for derived clock generation, root clock gating and related status
input rst_root_ni,
% for root, clk_family in typed_clocks.parent_child_clks.items():
% for src in clk_family:
input rst_root_${src}_ni,
% endfor
% endfor
// Bus Interface
input tlul_pkg::tl_h2d_t tl_i,
output tlul_pkg::tl_d2h_t tl_o,
// Alerts
input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o,
// pwrmgr interface
input pwrmgr_pkg::pwr_clk_req_t pwr_i,
output pwrmgr_pkg::pwr_clk_rsp_t pwr_o,
// dft interface
input prim_mubi_pkg::mubi4_t scanmode_i,
// idle hints
// SEC_CM: IDLE.INTERSIG.MUBI
input prim_mubi_pkg::mubi4_t [${len(typed_clocks.hint_clks)-1}:0] idle_i,
// life cycle state output
// SEC_CM: LC_CTRL.INTERSIG.MUBI
input lc_tx_t lc_hw_debug_en_i,
// clock bypass control with lc_ctrl
// SEC_CM: LC_CTRL_CLK_HANDSHAKE.INTERSIG.MUBI
input lc_tx_t lc_clk_byp_req_i,
output lc_tx_t lc_clk_byp_ack_o,
// clock bypass control with ast
// SEC_CM: CLK_HANDSHAKE.INTERSIG.MUBI
output mubi4_t io_clk_byp_req_o,
input mubi4_t io_clk_byp_ack_i,
output mubi4_t all_clk_byp_req_o,
input mubi4_t all_clk_byp_ack_i,
output mubi4_t hi_speed_sel_o,
// clock calibration has been done.
// If this is signal is 0, assume clock frequencies to be
// uncalibrated.
input prim_mubi_pkg::mubi4_t calib_rdy_i,
// jittery enable to ast
output mubi4_t jitter_en_o,
// external indication for whether dividers should be stepped down
// SEC_CM: DIV.INTERSIG.MUBI
input mubi4_t div_step_down_req_i,
// clock gated indications going to alert handlers
output clkmgr_cg_en_t cg_en_o,
// clock output interface
% for intf in cfg['exported_clks']:
output clkmgr_${intf}_out_t clocks_${intf}_o,
% endfor
output clkmgr_out_t clocks_o
);
import prim_mubi_pkg::MuBi4False;
import prim_mubi_pkg::MuBi4True;
import prim_mubi_pkg::mubi4_test_true_strict;
import prim_mubi_pkg::mubi4_test_true_loose;
import prim_mubi_pkg::mubi4_test_false_strict;
////////////////////////////////////////////////////
// External step down request
////////////////////////////////////////////////////
% for src_name in clocks.all_derived_srcs():
mubi4_t ${src_name}_step_down_req;
prim_mubi4_sync #(
.NumCopies(1),
.AsyncOn(1),
.StabilityCheck(1),
.ResetValue(MuBi4False)
) u_${src_name}_step_down_req_sync (
.clk_i(clk_${src_name}_i),
.rst_ni(rst_${src_name}_ni),
.mubi_i(div_step_down_req_i),
.mubi_o({${src_name}_step_down_req})
);
% endfor
////////////////////////////////////////////////////
// Divided clocks
// Note divided clocks must use the por version of
// its related reset to ensure clock division
// can happen without any dependency
////////////////////////////////////////////////////
logic [${len(clocks.derived_srcs)-1}:0] step_down_acks;
% for src_name in clocks.derived_srcs:
logic clk_${src_name}_i;
% endfor
% for src in clocks.derived_srcs.values():
// Declared as size 1 packed array to avoid FPV warning.
prim_mubi_pkg::mubi4_t [0:0] ${src.name}_div_scanmode;
prim_mubi4_sync #(
.NumCopies(1),
.AsyncOn(0)
) u_${src.name}_div_scanmode_sync (
.clk_i,
.rst_ni,
.mubi_i(scanmode_i),
.mubi_o({${src.name}_div_scanmode})
);
prim_clock_div #(
.Divisor(${src.div})
) u_no_scan_${src.name}_div (
.clk_i(clk_${src.src.name}_i),
.rst_ni(rst_root_${src.src.name}_ni),
.step_down_req_i(mubi4_test_true_strict(${src.src.name}_step_down_req)),
.step_down_ack_o(step_down_acks[${loop.index}]),
.test_en_i(mubi4_test_true_strict(${src.name}_div_scanmode[0])),
.clk_o(clk_${src.name}_i)
);
% endfor
////////////////////////////////////////////////////
// Register Interface
////////////////////////////////////////////////////
logic [NumAlerts-1:0] alert_test, alerts;
clkmgr_reg_pkg::clkmgr_reg2hw_t reg2hw;
clkmgr_reg_pkg::clkmgr_hw2reg_t hw2reg;
// SEC_CM: MEAS.CONFIG.REGWEN
// SEC_CM: MEAS.CONFIG.SHADOW
// SEC_CM: CLK_CTRL.CONFIG.REGWEN
clkmgr_reg_top u_reg (
.clk_i,
.rst_ni,
.rst_shadowed_ni,
% for src in typed_clocks.rg_srcs:
.clk_${src}_i,
.rst_${src}_ni,
% endfor
.tl_i,
.tl_o,
.reg2hw,
.hw2reg,
.shadowed_storage_err_o(hw2reg.fatal_err_code.shadow_storage_err.de),
.shadowed_update_err_o(hw2reg.recov_err_code.shadow_update_err.de),
// SEC_CM: BUS.INTEGRITY
.intg_err_o(hw2reg.fatal_err_code.reg_intg.de),
.devmode_i(1'b1)
);
assign hw2reg.fatal_err_code.reg_intg.d = 1'b1;
assign hw2reg.recov_err_code.shadow_update_err.d = 1'b1;
assign hw2reg.fatal_err_code.shadow_storage_err.d = 1'b1;
////////////////////////////////////////////////////
// Alerts
////////////////////////////////////////////////////
assign alert_test = {
reg2hw.alert_test.fatal_fault.q & reg2hw.alert_test.fatal_fault.qe,
reg2hw.alert_test.recov_fault.q & reg2hw.alert_test.recov_fault.qe
};
logic recov_alert;
assign recov_alert =
% for src in typed_clocks.rg_srcs:
hw2reg.recov_err_code.${src}_measure_err.de |
hw2reg.recov_err_code.${src}_timeout_err.de |
% endfor
hw2reg.recov_err_code.shadow_update_err.de;
assign alerts = {
|reg2hw.fatal_err_code,
recov_alert
};
localparam logic [NumAlerts-1:0] AlertFatal = {1'b1, 1'b0};
for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx
prim_alert_sender #(
.AsyncOn(AlertAsyncOn[i]),
.IsFatal(AlertFatal[i])
) u_prim_alert_sender (
.clk_i,
.rst_ni,
.alert_test_i ( alert_test[i] ),
.alert_req_i ( alerts[i] ),
.alert_ack_o ( ),
.alert_state_o ( ),
.alert_rx_i ( alert_rx_i[i] ),
.alert_tx_o ( alert_tx_o[i] )
);
end
////////////////////////////////////////////////////
// Clock bypass request
////////////////////////////////////////////////////
mubi4_t extclk_ctrl_sel;
mubi4_t extclk_ctrl_hi_speed_sel;
assign extclk_ctrl_sel = mubi4_t'(reg2hw.extclk_ctrl.sel.q);
assign extclk_ctrl_hi_speed_sel = mubi4_t'(reg2hw.extclk_ctrl.hi_speed_sel.q);
clkmgr_byp #(
.NumDivClks(${len(clocks.derived_srcs)})
) u_clkmgr_byp (
.clk_i,
.rst_ni,
.en_i(lc_hw_debug_en_i),
.lc_clk_byp_req_i,
.lc_clk_byp_ack_o,
.byp_req_i(extclk_ctrl_sel),
.byp_ack_o(hw2reg.extclk_status.d),
.hi_speed_sel_i(extclk_ctrl_hi_speed_sel),
.all_clk_byp_req_o,
.all_clk_byp_ack_i,
.io_clk_byp_req_o,
.io_clk_byp_ack_i,
.hi_speed_sel_o,
// divider step down controls
.step_down_acks_i(step_down_acks)
);
////////////////////////////////////////////////////
// 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 typed_clocks.ft_clks.items():
prim_clock_buf u_${k}_buf (
.clk_i(clk_${v.src.name}_i),
.clk_o(clocks_o.${k})
);
// clock gated indication for alert handler: these clocks are never gated.
assign cg_en_o.${k.split('clk_')[-1]} = MuBi4False;
% endfor
////////////////////////////////////////////////////
// Distribute pwrmgr ip_clk_en requests to each family
////////////////////////////////////////////////////
% for root, clk_family in typed_clocks.parent_child_clks.items():
// clk_${root} family
% for clk in clk_family:
logic pwrmgr_${clk}_en;
% endfor
% for clk in clk_family:
assign pwrmgr_${clk}_en = pwr_i.${root}_ip_clk_en;
% endfor
% endfor
////////////////////////////////////////////////////
// Root gating
////////////////////////////////////////////////////
% for root, clk_family in typed_clocks.parent_child_clks.items():
// clk_${root} family
logic [${len(clk_family)-1}:0] ${root}_ens;
% for src in clk_family:
logic clk_${src}_en;
logic clk_${src}_root;
clkmgr_root_ctrl u_${src}_root_ctrl (
.clk_i(clk_${src}_i),
.rst_ni(rst_root_${src}_ni),
.scanmode_i,
.async_en_i(pwrmgr_${src}_en),
.en_o(clk_${src}_en),
.clk_o(clk_${src}_root)
);
assign ${root}_ens[${loop.index}] = clk_${src}_en;
% endfor
// create synchronized status
clkmgr_clk_status #(
.NumClocks(${len(clk_family)})
) u_${root}_status (
.clk_i,
.rst_ni(rst_root_ni),
.ens_i(${root}_ens),
.status_o(pwr_o.${root}_status)
);
% endfor
////////////////////////////////////////////////////
// Clock Measurement for the roots
// SEC_CM: TIMEOUT.CLK.BKGN_CHK, MEAS.CLK.BKGN_CHK
////////////////////////////////////////////////////
typedef enum logic [${(len(typed_clocks.rg_srcs) + 1).bit_length() - 1}:0] {
BaseIdx,
% for src in typed_clocks.rg_srcs:
Clk${Name.from_snake_case(src).as_camel_case()}Idx,
% endfor
CalibRdyLastIdx
} clkmgr_calib_idx_e;
// if clocks become uncalibrated, allow the measurement control configurations to change
mubi4_t [CalibRdyLastIdx-1:0] calib_rdy;
prim_mubi4_sync #(
.AsyncOn(1),
.NumCopies(int'(CalibRdyLastIdx)),
.ResetValue(MuBi4False)
) u_calib_rdy_sync (
.clk_i,
.rst_ni,
.mubi_i(calib_rdy_i),
.mubi_o({calib_rdy})
);
always_comb begin
hw2reg.measure_ctrl_regwen.de = '0;
hw2reg.measure_ctrl_regwen.d = reg2hw.measure_ctrl_regwen;
if (mubi4_test_false_strict(calib_rdy[BaseIdx])) begin
hw2reg.measure_ctrl_regwen.de = 1'b1;
hw2reg.measure_ctrl_regwen.d = 1'b1;
end
end
<% aon_freq = clocks.all_srcs['aon'].freq %>\
% for i, src in enumerate(typed_clocks.rg_srcs):
<%
freq = clocks.all_srcs[src].freq
cnt = int(freq*2 / aon_freq)
sel_idx = f"Clk{Name.from_snake_case(src).as_camel_case()}Idx"
%>
clkmgr_meas_chk #(
.Cnt(${cnt}),
.RefCnt(1)
) u_${src}_meas (
.clk_i,
.rst_ni,
.clk_src_i(clk_${src}_i),
.rst_src_ni(rst_${src}_ni),
.clk_ref_i(clk_aon_i),
.rst_ref_ni(rst_aon_ni),
// signals on source domain
.src_en_i(clk_${src}_en & mubi4_test_true_loose(mubi4_t'(reg2hw.${src}_meas_ctrl_en))),
.src_max_cnt_i(reg2hw.${src}_meas_ctrl_shadowed.hi.q),
.src_min_cnt_i(reg2hw.${src}_meas_ctrl_shadowed.lo.q),
.src_cfg_meas_en_i(mubi4_t'(reg2hw.${src}_meas_ctrl_en.q)),
.src_cfg_meas_en_valid_o(hw2reg.${src}_meas_ctrl_en.de),
.src_cfg_meas_en_o(hw2reg.${src}_meas_ctrl_en.d),
// signals on local clock domain
.calib_rdy_i(calib_rdy[${sel_idx}]),
.meas_err_o(hw2reg.recov_err_code.${src}_measure_err.de),
.timeout_err_o(hw2reg.recov_err_code.${src}_timeout_err.de)
);
assign hw2reg.recov_err_code.${src}_measure_err.d = 1'b1;
assign hw2reg.recov_err_code.${src}_timeout_err.d = 1'b1;
% endfor
////////////////////////////////////////////////////
// Clocks with only root gate
////////////////////////////////////////////////////
% for k,v in typed_clocks.rg_clks.items():
assign clocks_o.${k} = clk_${v.src.name}_root;
// clock gated indication for alert handler
prim_mubi4_sender #(
.ResetValue(MuBi4True)
) u_prim_mubi4_sender_${k} (
.clk_i(clk_${v.src.name}_i),
.rst_ni(rst_${v.src.name}_ni),
.mubi_i(((clk_${v.src.name}_en) ? MuBi4False : MuBi4True)),
.mubi_o(cg_en_o.${k.split('clk_')[-1]})
);
% endfor
////////////////////////////////////////////////////
// Software direct control group
////////////////////////////////////////////////////
% for k in typed_clocks.sw_clks:
logic ${k}_sw_en;
% endfor
% for k,v in typed_clocks.sw_clks.items():
prim_flop_2sync #(
.Width(1)
) u_${k}_sw_en_sync (
.clk_i(clk_${v.src.name}_i),
.rst_ni(rst_${v.src.name}_ni),
.d_i(reg2hw.clk_enables.${k}_en.q),
.q_o(${k}_sw_en)
);
// Declared as size 1 packed array to avoid FPV warning.
prim_mubi_pkg::mubi4_t [0:0] ${k}_scanmode;
prim_mubi4_sync #(
.NumCopies(1),
.AsyncOn(0)
) u_${k}_scanmode_sync (
.clk_i,
.rst_ni,
.mubi_i(scanmode_i),
.mubi_o(${k}_scanmode)
);
logic ${k}_combined_en;
assign ${k}_combined_en = ${k}_sw_en & clk_${v.src.name}_en;
prim_clock_gating #(
.FpgaBufGlobal(1'b1) // This clock spans across multiple clock regions.
) u_${k}_cg (
.clk_i(clk_${v.src.name}_i),
.en_i(${k}_combined_en),
.test_en_i(mubi4_test_true_strict(${k}_scanmode[0])),
.clk_o(clocks_o.${k})
);
// clock gated indication for alert handler
prim_mubi4_sender #(
.ResetValue(MuBi4True)
) u_prim_mubi4_sender_${k} (
.clk_i(clk_${v.src.name}_i),
.rst_ni(rst_${v.src.name}_ni),
.mubi_i(((${k}_combined_en) ? MuBi4False : MuBi4True)),
.mubi_o(cg_en_o.${k.split('clk_')[-1]})
);
% endfor
////////////////////////////////////////////////////
// Software hint group
// The idle hint feedback is assumed to be synchronous to the
// clock target
////////////////////////////////////////////////////
logic [${len(typed_clocks.hint_clks)-1}:0] idle_cnt_err;
% for clk, sig in typed_clocks.hint_clks.items():
<%assert_name = Name.from_snake_case(clk)
%>
clkmgr_trans #(
% if clk == "clk_main_kmac":
.FpgaBufGlobal(1'b1) // KMAC is getting too big for a single clock region.
% else:
.FpgaBufGlobal(1'b0) // This clock is used primarily locally.
% endif
) u_${clk}_trans (
.clk_i(clk_${sig.src.name}_i),
.clk_gated_i(clk_${sig.src.name}_root),
.rst_ni(rst_${sig.src.name}_ni),
.en_i(clk_${sig.src.name}_en),
.idle_i(idle_i[${hint_names[clk]}]),
% if len(typed_clocks.hint_clks.items()) > 1:
.sw_hint_i(reg2hw.clk_hints.${clk}_hint.q),
% else:
.sw_hint_i(reg2hw.clk_hints.q),
% endif
.scanmode_i,
.alert_cg_en_o(cg_en_o.${clk.split('clk_')[-1]}),
.clk_o(clocks_o.${clk}),
.clk_reg_i(clk_i),
.rst_reg_ni(rst_ni),
% if len(typed_clocks.hint_clks.items()) > 1:
.reg_en_o(hw2reg.clk_hints_status.${clk}_val.d),
% else:
.reg_en_o(hw2reg.clk_hints_status.d),
% endif
.reg_cnt_err_o(idle_cnt_err[${hint_names[clk]}])
);
`ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(
${assert_name.as_camel_case()}CountCheck_A,
u_${clk}_trans.u_idle_cnt,
alert_tx_o[1])
% endfor
assign hw2reg.fatal_err_code.idle_cnt.d = 1'b1;
assign hw2reg.fatal_err_code.idle_cnt.de = |idle_cnt_err;
// state readback
% if len(typed_clocks.hint_clks.items()) > 1:
% for clk in typed_clocks.hint_clks.keys():
assign hw2reg.clk_hints_status.${clk}_val.de = 1'b1;
% endfor
% else:
assign hw2reg.clk_hints_status.de = 1'b1;
% endif
// SEC_CM: JITTER.CONFIG.MUBI
assign jitter_en_o = mubi4_t'(reg2hw.jitter_enable.q);
////////////////////////////////////////////////////
// Exported clocks
////////////////////////////////////////////////////
% for intf, eps in cfg['exported_clks'].items():
% for ep, clks in eps.items():
% for clk in clks:
assign clocks_${intf}_o.clk_${intf}_${ep}_${clk} = clocks_o.clk_${clk};
% endfor
% endfor
% endfor
////////////////////////////////////////////////////
// Assertions
////////////////////////////////////////////////////
`ASSERT_KNOWN(TlDValidKnownO_A, tl_o.d_valid)
`ASSERT_KNOWN(TlAReadyKnownO_A, tl_o.a_ready)
`ASSERT_KNOWN(AlertsKnownO_A, alert_tx_o)
`ASSERT_KNOWN(PwrMgrKnownO_A, pwr_o)
`ASSERT_KNOWN(AllClkBypReqKnownO_A, all_clk_byp_req_o)
`ASSERT_KNOWN(IoClkBypReqKnownO_A, io_clk_byp_req_o)
`ASSERT_KNOWN(LcCtrlClkBypAckKnownO_A, lc_clk_byp_ack_o)
`ASSERT_KNOWN(JitterEnableKnownO_A, jitter_en_o)
% for intf in cfg['exported_clks']:
`ASSERT_KNOWN(ExportClocksKownO_A, clocks_${intf}_o)
% endfor
`ASSERT_KNOWN(ClocksKownO_A, clocks_o)
`ASSERT_KNOWN(CgEnKnownO_A, cg_en_o)
// Alert assertions for reg_we onehot check
`ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[1])
endmodule // clkmgr