blob: 776a5c23695d61b8c53b57a595587a606cf440b7 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Handle clock manager transactional clocks
module clkmgr_trans
import clkmgr_pkg::*;
import prim_mubi_pkg::mubi4_t;
# (
parameter bit FpgaBufGlobal = 1
) (
input clk_i,
input clk_gated_i,
input rst_ni,
input en_i,
input mubi4_t idle_i,
input sw_hint_i,
input mubi4_t scanmode_i,
output mubi4_t alert_cg_en_o,
output logic clk_o,
// interface to regfile
input clk_reg_i,
input rst_reg_ni,
output logic reg_en_o,
output logic reg_cnt_err_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_false_loose;
// Note this value is specifically chosen.
// The binary value is 1010, which is a balanced 4-bit value
// that should in theory be resistant to all 0 or all 1 attacks.
localparam int TransIdleCnt = 10;
localparam int IdleCntWidth = $clog2(TransIdleCnt + 1);
logic [IdleCntWidth-1:0] idle_cnt;
logic idle_valid;
logic sw_hint_synced;
logic local_en;
assign idle_valid = (idle_cnt == TransIdleCnt);
assign local_en = sw_hint_synced | ~idle_valid;
prim_flop_2sync #(
.Width(1)
) u_hint_sync (
.clk_i(clk_i),
.rst_ni(rst_ni),
.d_i(sw_hint_i),
.q_o(sw_hint_synced)
);
// Idle sync: Idle signal comes from IP module. The reset of the Idle signal
// may differ from the reset here. Adding mubi sync to synchronize.
prim_mubi_pkg::mubi4_t [0:0] idle;
prim_mubi4_sync #(
.NumCopies ( 1 ),
.AsyncOn ( 1'b 1 ),
.StabilityCheck ( 1'b 1 )
) u_idle_sync (
.clk_i,
.rst_ni,
.mubi_i (idle_i),
.mubi_o (idle)
);
// SEC_CM: IDLE.CTR.REDUN
logic cnt_err;
prim_count #(
.Width(IdleCntWidth)
) u_idle_cnt (
.clk_i(clk_i),
.rst_ni(rst_ni),
// the default condition is to keep the clock enabled
.clr_i(mubi4_test_false_loose(idle[0])),
.set_i('0),
.set_cnt_i('0),
.incr_en_i(mubi4_test_true_strict(idle[0]) & ~idle_valid),
.decr_en_i(1'b0),
.step_i(IdleCntWidth'(1'b1)),
.cnt_o(idle_cnt),
.cnt_next_o(),
.err_o(cnt_err)
);
// Declared as size 1 packed array to avoid FPV warning.
prim_mubi_pkg::mubi4_t [0:0] scanmode;
prim_mubi4_sync #(
.NumCopies(1),
.AsyncOn(0)
) u_scanmode_sync (
.clk_i,
.rst_ni,
.mubi_i(scanmode_i),
.mubi_o(scanmode)
);
// Add a prim buf here to make sure the CG and the lc sender inputs
// are derived from the same physical signal.
logic combined_en_d, combined_en_q;
prim_buf u_prim_buf_en (
.in_i(local_en & en_i),
.out_o(combined_en_d)
);
// clk_gated_i is already controlled by en_i, so there is no need
// to use it in the below gating function
prim_clock_gating #(
.FpgaBufGlobal(FpgaBufGlobal)
) u_cg (
.clk_i(clk_gated_i),
.en_i(local_en),
.test_en_i(mubi4_test_true_strict(scanmode[0])),
.clk_o(clk_o)
);
// clock gated indication for alert handler
prim_mubi4_sender #(
.ResetValue(MuBi4True)
) u_prim_mubi4_sender (
.clk_i(clk_i),
.rst_ni(rst_ni),
.mubi_i(combined_en_d ? MuBi4False : MuBi4True),
.mubi_o(alert_cg_en_o)
);
// we hold the error because there is no guarantee on
// what the timing of cnt_err looks like, it may be a
// pulse or it may be level. If it's for former,
// prim_sync_reqack may miss it, if it's the latter,
// prim_pulse_sync may miss it. As a result, just
// latch forever and sync it over.
logic hold_err;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
hold_err <= '0;
end else if (cnt_err) begin
hold_err <= 1'b1;
end
end
// register facing domain
prim_flop_2sync #(
.Width(1)
) u_err_sync (
.clk_i(clk_reg_i),
.rst_ni(rst_reg_ni),
.d_i(hold_err),
.q_o(reg_cnt_err_o)
);
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
combined_en_q <= '0;
end else begin
combined_en_q <= combined_en_d;
end
end
prim_flop_2sync #(
.Width(1)
) u_en_sync (
.clk_i(clk_reg_i),
.rst_ni(rst_reg_ni),
.d_i(combined_en_q),
.q_o(reg_en_o)
);
endmodule