| // 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 |