blob: 40c95f8f3d101d212713ebad0a3493526a2b0a01 [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,
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