| // 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, |
| input mubi4_t low_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, |
| // interaction with dividers |
| input [NumDivClks-1:0] step_down_acks_i, |
| output logic step_down_req_o |
| ); |
| |
| 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_or_hi; |
| import prim_mubi_pkg::mubi4_test_false_strict; |
| import prim_mubi_pkg::mubi4_test_true_strict; |
| |
| // life cycle handling |
| mubi4_t io_clk_byp_req_d; |
| assign io_clk_byp_req_d = (lc_clk_byp_req_i == lc_ctrl_pkg::On) ? |
| MuBi4True : |
| MuBi4False; |
| |
| prim_mubi4_sender #( |
| .ResetValue(MuBi4False) |
| ) 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. |
| prim_lc_sender u_send ( |
| .clk_i, |
| .rst_ni, |
| .lc_en_i((&step_down_acks_i) ? lc_clk_byp_req_i : lc_ctrl_pkg::Off), |
| .lc_en_o(lc_clk_byp_ack_o) |
| ); |
| |
| // software switch request handling |
| mubi4_t dft_en; |
| assign dft_en = (en_i == lc_ctrl_pkg::On) ? MuBi4True : MuBi4False; |
| |
| mubi4_t all_clk_byp_req_d; |
| assign all_clk_byp_req_d = mubi4_and_hi(byp_req_i, dft_en); |
| |
| prim_mubi4_sender #( |
| .ResetValue(MuBi4False) |
| ) u_all_byp_req ( |
| .clk_i, |
| .rst_ni, |
| .mubi_i(all_clk_byp_req_d), |
| .mubi_o(all_clk_byp_req_o) |
| ); |
| |
| // divider step down handling |
| mubi4_t io_clk_byp_ack; |
| mubi4_t all_clk_byp_ack; |
| |
| prim_mubi4_sync #( |
| .AsyncOn(1), |
| .ResetValue(MuBi4False) |
| ) u_io_ack_sync ( |
| .clk_i, |
| .rst_ni, |
| .mubi_i(io_clk_byp_ack_i), |
| .mubi_o(io_clk_byp_ack) |
| ); |
| |
| prim_mubi4_sync #( |
| .AsyncOn(1), |
| .ResetValue(MuBi4False) |
| ) u_all_ack_sync ( |
| .clk_i, |
| .rst_ni, |
| .mubi_i(all_clk_byp_ack_i), |
| .mubi_o(all_clk_byp_ack) |
| ); |
| |
| // create individual requests |
| mubi4_t lc_step_down_req; |
| assign lc_step_down_req = mubi4_and_hi(io_clk_byp_req_o, io_clk_byp_ack); |
| |
| // When requesting a switch, the low speed indication is used to determine step down. |
| // Once switched, the low speed indication is not looked at again until software request |
| // is de-asserted. |
| mubi4_t sw_step_down_en; |
| mubi4_t sw_step_down_req; |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| sw_step_down_en <= MuBi4False; |
| end else if (mubi4_test_true_strict(all_clk_byp_req_o) && |
| mubi4_test_true_strict(all_clk_byp_ack)) begin |
| sw_step_down_en <= MuBi4True; |
| end else if ( |
| mubi4_test_true_strict(sw_step_down_en) && |
| mubi4_test_false_strict(all_clk_byp_req_o) && |
| mubi4_test_false_strict(all_clk_byp_ack)) begin |
| sw_step_down_en <= MuBi4False; |
| end |
| end |
| // when in external clock state, allow low speed select to directly control |
| // clock divider. |
| assign sw_step_down_req = mubi4_and_hi(sw_step_down_en, low_speed_sel_i); |
| |
| // combine requests |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| step_down_req_o <= '0; |
| end else begin |
| step_down_req_o <= |
| mubi4_test_true_strict(mubi4_or_hi(lc_step_down_req, sw_step_down_req)); |
| end |
| end |
| |
| |
| endmodule // clkmgr_byp |