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