blob: bfe57e9604ebdc4028bd808f4de75e419e303c10 [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 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