blob: 1c78eadb714e2c94b8f6525bd4f20f5fff683d38 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// prim_clk_meas is a generic module that measures two clocks against each other.
// One clock is the reference, and another is the input.
// If the input clocks becomes too fast or too slow, an error condition is created.
// The default parameters assume the reference clock is significantly slower than
// the input clock
`include "prim_assert.sv"
module prim_clock_meas #(
// 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),
localparam int RefCntWidth = prim_util_pkg::vbits(RefCnt)
) (
input clk_i,
input rst_ni,
input clk_ref_i,
input rst_ref_ni,
input en_i,
input [CntWidth-1:0] max_cnt,
input [CntWidth-1:0] min_cnt,
// the output valid and fast/slow indications are all on the
// input clock domain
output logic valid_o,
output logic fast_o,
output logic slow_o
);
//////////////////////////
// Reference clock logic
//////////////////////////
logic ref_en;
prim_flop_2sync #(
.Width(1)
) u_ref_meas_en_sync (
.d_i(en_i),
.clk_i(clk_ref_i),
.rst_ni(rst_ref_ni),
.q_o(ref_en)
);
logic ref_valid;
logic [RefCntWidth-1:0] ref_cnt;
always_ff @(posedge clk_ref_i or negedge rst_ref_ni) begin
if (!rst_ref_ni) begin
ref_cnt <= '0;
ref_valid <= '0;
end else if (!ref_en && |ref_cnt) begin
ref_cnt <= '0;
ref_valid <= '0;
end else if (ref_en && (ref_cnt == RefCnt - 1)) begin
// restart count and measure
ref_cnt <= '0;
ref_valid <= 1'b1;
end else if (ref_en) begin
ref_cnt <= ref_cnt + 1'b1;
ref_valid <= 1'b0;
end
end
//////////////////////////
// Input Clock Logic
//////////////////////////
logic valid;
// The valid pulse causes the count to reset and start counting again
// for each reference cycle.
// The count obtained during the the last refernece cycle is then used
// to measure how fast/slow the input clock is.
prim_pulse_sync u_sync_ref (
.clk_src_i(clk_ref_i),
.rst_src_ni(rst_ref_ni),
.src_pulse_i(ref_valid),
.clk_dst_i(clk_i),
.rst_dst_ni(rst_ni),
.dst_pulse_o(valid)
);
logic cnt_en;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cnt_en <= '0;
end else if (!en_i) begin
cnt_en <= '0;
end else if (en_i && valid) begin
cnt_en <= 1'b1;
end
end
logic cnt_ovfl;
logic [CntWidth-1:0] cnt;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cnt <= '0;
cnt_ovfl <= '0;
end else if (!cnt_en && |cnt) begin
cnt <= '0;
cnt_ovfl <= '0;
end else if (valid_o) begin
cnt <= '0;
cnt_ovfl <= '0;
end else if (cnt_en && cnt_ovfl) begin
cnt <= '{default: '1};
end else if (cnt_en) begin
{cnt_ovfl, cnt} <= cnt + 1'b1;
end
end
assign valid_o = en_i & valid & |cnt;
assign fast_o = valid_o & ((cnt > max_cnt) | cnt_ovfl);
assign slow_o = valid_o & (cnt < min_cnt);
//////////////////////////
// Assertions
//////////////////////////
`ASSERT_INIT(RefCntVal_A, RefCnt >= 1)
// if we've reached the max count, enable must be 0 next.
// Otherwise the width of the counter is too small to accommodate the usecase
`ASSERT(MaxWidth_A, (cnt == Cnt-1) |=> !cnt_en )
endmodule // prim_clk_meas