| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // Handle clock manager bypass requests |
| |
| module clkmgr_byp |
| import clkmgr_pkg::*; |
| import lc_ctrl_pkg::lc_tx_t; |
| import prim_mubi_pkg::mubi4_t; |
| # ( |
| parameter int NumDivClks = 1 |
| ) ( |
| input clk_i, |
| input rst_ni, |
| // interaction with lc_ctrl |
| input lc_tx_t en_i, |
| input lc_tx_t lc_clk_byp_req_i, |
| output lc_tx_t lc_clk_byp_ack_o, |
| // interaction with software |
| input mubi4_t byp_req_i, |
| output mubi4_t byp_ack_o, |
| input mubi4_t hi_speed_sel_i, |
| // interaction with ast |
| output mubi4_t all_clk_byp_req_o, |
| input mubi4_t all_clk_byp_ack_i, |
| output mubi4_t io_clk_byp_req_o, |
| input mubi4_t io_clk_byp_ack_i, |
| output mubi4_t hi_speed_sel_o, |
| // interaction with dividers |
| input [NumDivClks-1:0] step_down_acks_i |
| ); |
| |
| import prim_mubi_pkg::MuBi4Width; |
| import prim_mubi_pkg::MuBi4True; |
| import prim_mubi_pkg::MuBi4False; |
| import prim_mubi_pkg::mubi4_and_hi; |
| import prim_mubi_pkg::mubi4_test_true_strict; |
| |
| // synchornize incoming lc signals |
| lc_tx_t en; |
| prim_lc_sync #( |
| .NumCopies(1), |
| .AsyncOn(1), |
| .ResetValueIsOn(0) |
| ) u_en_sync ( |
| .clk_i, |
| .rst_ni, |
| .lc_en_i(en_i), |
| .lc_en_o({en}) |
| ); |
| |
| typedef enum logic [1:0] { |
| LcClkBypReqIoReq, |
| LcClkBypReqLcAck, |
| LcClkBypReqLast |
| } lc_clk_byp_req_e; |
| |
| lc_tx_t [LcClkBypReqLast-1:0] lc_clk_byp_req; |
| prim_lc_sync #( |
| .NumCopies(int'(LcClkBypReqLast)), |
| .AsyncOn(1), |
| .ResetValueIsOn(0) |
| ) u_lc_byp_req ( |
| .clk_i, |
| .rst_ni, |
| .lc_en_i(lc_clk_byp_req_i), |
| .lc_en_o(lc_clk_byp_req) |
| ); |
| |
| // synchronize step down acks |
| logic [NumDivClks-1:0] step_down_acks_sync; |
| prim_flop #( |
| .Width(NumDivClks), |
| .ResetValue(0) |
| ) u_step_down_acks_sync ( |
| .clk_i, |
| .rst_ni, |
| .d_i(step_down_acks_i), |
| .q_o(step_down_acks_sync) |
| ); |
| |
| // life cycle handling |
| mubi4_t io_clk_byp_req_d; |
| assign io_clk_byp_req_d = (lc_clk_byp_req[LcClkBypReqIoReq] == lc_ctrl_pkg::On) ? |
| MuBi4True : |
| MuBi4False; |
| |
| prim_mubi4_sender #( |
| .ResetValue(MuBi4False), |
| .EnSecBuf(1) |
| ) u_io_byp_req ( |
| .clk_i, |
| .rst_ni, |
| .mubi_i(io_clk_byp_req_d), |
| .mubi_o(io_clk_byp_req_o) |
| ); |
| |
| // only ack the lc_ctrl if it made a request. |
| mubi4_t io_clk_byp_ack; |
| prim_lc_sender u_send ( |
| .clk_i, |
| .rst_ni, |
| .lc_en_i(&step_down_acks_sync & mubi4_test_true_strict(io_clk_byp_ack) ? |
| lc_clk_byp_req[LcClkBypReqLcAck] : lc_ctrl_pkg::Off), |
| .lc_en_o(lc_clk_byp_ack_o) |
| ); |
| |
| // software switch request handling |
| mubi4_t dft_en; |
| assign dft_en = lc_ctrl_pkg::lc_to_mubi4(en); |
| |
| mubi4_t all_clk_byp_req_d; |
| assign all_clk_byp_req_d = mubi4_and_hi(byp_req_i, dft_en); |
| |
| prim_mubi4_sender #( |
| .AsyncOn(1), |
| .ResetValue(MuBi4False), |
| .EnSecBuf(1) |
| ) u_all_byp_req ( |
| .clk_i, |
| .rst_ni, |
| .mubi_i(all_clk_byp_req_d), |
| .mubi_o(all_clk_byp_req_o) |
| ); |
| |
| prim_mubi4_sync #( |
| .AsyncOn(1), |
| .StabilityCheck(1), |
| .ResetValue(MuBi4False) |
| ) u_io_ack_sync ( |
| .clk_i, |
| .rst_ni, |
| .mubi_i(io_clk_byp_ack_i), |
| .mubi_o({io_clk_byp_ack}) |
| ); |
| |
| // since div_step_down_req is now directly fed externally, there is no longer |
| // a use for the related 'ack' signals |
| prim_mubi4_sync #( |
| .AsyncOn(1), |
| .StabilityCheck(1), |
| .ResetValue(MuBi4False) |
| ) u_all_ack_sync ( |
| .clk_i, |
| .rst_ni, |
| .mubi_i(all_clk_byp_ack_i), |
| .mubi_o({byp_ack_o}) |
| ); |
| |
| // the software high speed select is valid only when software requests clock |
| // bypass |
| prim_mubi4_sender #( |
| .AsyncOn(1), |
| .ResetValue(MuBi4True) |
| ) u_hi_speed_sel ( |
| .clk_i, |
| .rst_ni, |
| .mubi_i(mubi4_and_hi(all_clk_byp_req_d, hi_speed_sel_i)), |
| .mubi_o(hi_speed_sel_o) |
| ); |
| |
| endmodule // clkmgr_byp |