blob: 64f74df1f7a013c7aedba068065f9f2e64fdc623 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Clock manager measurement and timeout checks
module clkmgr_meas_chk
import prim_mubi_pkg::mubi4_t;
#(
// Maximum value of input clock counts over measurement period
parameter int Cnt = 16,
// Maximum value of reference clock counts over measurement period
parameter int RefCnt = 1,
localparam int CntWidth = prim_util_pkg::vbits(Cnt)
) (
// the local operating clock
input clk_i,
input rst_ni,
// the clock we are measuring
input clk_src_i,
input rst_src_ni,
// the reference clock we are measuring against
input clk_ref_i,
input rst_ref_ni,
// controls all provided on src clock
input src_en_i,
input [CntWidth-1:0] src_max_cnt_i,
input [CntWidth-1:0] src_min_cnt_i,
input mubi4_t src_cfg_meas_en_i,
output logic src_cfg_meas_en_valid_o,
output mubi4_t src_cfg_meas_en_o,
// calibration ready input provided on local operating clock
input mubi4_t calib_rdy_i,
// error output are provided on local operating clock
output logic meas_err_o,
output logic timeout_err_o
);
logic src_fast_err;
logic src_slow_err;
logic ref_timeout_err;
prim_clock_meas #(
.Cnt(Cnt),
.RefCnt(RefCnt),
.ClkTimeOutChkEn(1'b1),
.RefTimeOutChkEn(1'b0)
) u_meas (
.clk_i(clk_src_i),
.rst_ni(rst_src_ni),
.clk_ref_i,
.rst_ref_ni,
.en_i(src_en_i),
.max_cnt(src_max_cnt_i),
.min_cnt(src_min_cnt_i),
.valid_o(),
.fast_o(src_fast_err),
.slow_o(src_slow_err),
.timeout_clk_ref_o(),
.ref_timeout_clk_o(ref_timeout_err)
);
mubi4_t src_calib_rdy;
prim_mubi4_sync #(
.AsyncOn(1),
.ResetValue(prim_mubi_pkg::MuBi4False)
) u_calib_rdy_sync (
.clk_i,
.rst_ni,
.mubi_i(calib_rdy_i),
.mubi_o({src_calib_rdy})
);
// if clocks become uncalibrated, switch off measurement controls
always_comb begin
src_cfg_meas_en_valid_o = '0;
src_cfg_meas_en_o = src_cfg_meas_en_i;
// if calibration is lost when measurement is currently enabled,
// disable measurement enable.
if (prim_mubi_pkg::mubi4_test_false_strict(src_calib_rdy) &&
prim_mubi_pkg::mubi4_test_true_loose(src_cfg_meas_en_o)) begin
src_cfg_meas_en_valid_o = 1'b1;
src_cfg_meas_en_o = prim_mubi_pkg::MuBi4False;
end
end
// A reqack module is used here instead of a pulse_saync
// because the source pulses may toggle too fast for the
// the destination to receive.
logic src_err_req, src_err_ack;
always_ff @(posedge clk_src_i or negedge rst_src_ni) begin
if (!rst_src_ni) begin
src_err_req <= '0;
end else if (src_fast_err || src_slow_err) begin
src_err_req <= 1'b1;
end else if (src_err_req && src_err_ack) begin
src_err_req <= '0;
end
end
prim_sync_reqack u_err_sync (
.clk_src_i,
.rst_src_ni,
.clk_dst_i(clk_i),
.rst_dst_ni(rst_ni),
.req_chk_i(1'b1),
.src_req_i(src_err_req),
.src_ack_o(src_err_ack),
.dst_req_o(meas_err_o),
.dst_ack_i(meas_err_o)
);
prim_edge_detector #(
.Width(1),
.ResetValue('0),
.EnSync(1'b1)
) u_timeout_err_sync (
.clk_i,
.rst_ni,
.d_i(ref_timeout_err),
.q_sync_o(),
.q_posedge_pulse_o(timeout_err_o),
.q_negedge_pulse_o()
);
endmodule // clkmgr_meas_chk