Timothy Chen | fe5d032 | 2021-08-30 15:55:37 -0700 | [diff] [blame] | 1 | // Copyright lowRISC contributors. |
| 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | // SPDX-License-Identifier: Apache-2.0 |
| 4 | // |
| 5 | // prim_clk_meas is a generic module that measures two clocks against each other. |
| 6 | // One clock is the reference, and another is the input. |
| 7 | // If the input clocks becomes too fast or too slow, an error condition is created. |
| 8 | |
| 9 | // The default parameters assume the reference clock is significantly slower than |
| 10 | // the input clock |
| 11 | |
| 12 | |
| 13 | `include "prim_assert.sv" |
| 14 | |
| 15 | module prim_clock_meas #( |
| 16 | // Maximum value of input clock counts over measurement period |
| 17 | parameter int Cnt = 16, |
| 18 | // Maximum value of reference clock counts over measurement period |
| 19 | parameter int RefCnt = 1, |
Timothy Chen | 6e61902 | 2021-11-08 16:42:52 -0800 | [diff] [blame] | 20 | parameter bit ClkTimeOutChkEn = 1, |
| 21 | parameter bit RefTimeOutChkEn = 1, |
Timothy Chen | fe5d032 | 2021-08-30 15:55:37 -0700 | [diff] [blame] | 22 | localparam int CntWidth = prim_util_pkg::vbits(Cnt), |
| 23 | localparam int RefCntWidth = prim_util_pkg::vbits(RefCnt) |
| 24 | ) ( |
| 25 | input clk_i, |
| 26 | input rst_ni, |
| 27 | input clk_ref_i, |
| 28 | input rst_ref_ni, |
| 29 | input en_i, |
| 30 | input [CntWidth-1:0] max_cnt, |
| 31 | input [CntWidth-1:0] min_cnt, |
| 32 | // the output valid and fast/slow indications are all on the |
| 33 | // input clock domain |
| 34 | output logic valid_o, |
| 35 | output logic fast_o, |
Timothy Chen | 6e61902 | 2021-11-08 16:42:52 -0800 | [diff] [blame] | 36 | output logic slow_o, |
| 37 | |
| 38 | // signal on clk_i domain that indicates clk_ref has timed out |
| 39 | output logic timeout_clk_ref_o, |
| 40 | |
| 41 | // signal on clk_ref_i domain that indicates clk has timed out |
| 42 | output logic ref_timeout_clk_o |
Timothy Chen | fe5d032 | 2021-08-30 15:55:37 -0700 | [diff] [blame] | 43 | ); |
| 44 | |
| 45 | ////////////////////////// |
| 46 | // Reference clock logic |
| 47 | ////////////////////////// |
| 48 | |
| 49 | logic ref_en; |
| 50 | prim_flop_2sync #( |
| 51 | .Width(1) |
| 52 | ) u_ref_meas_en_sync ( |
| 53 | .d_i(en_i), |
| 54 | .clk_i(clk_ref_i), |
| 55 | .rst_ni(rst_ref_ni), |
| 56 | .q_o(ref_en) |
| 57 | ); |
| 58 | |
| 59 | logic ref_valid; |
| 60 | logic [RefCntWidth-1:0] ref_cnt; |
| 61 | always_ff @(posedge clk_ref_i or negedge rst_ref_ni) begin |
| 62 | if (!rst_ref_ni) begin |
| 63 | ref_cnt <= '0; |
| 64 | ref_valid <= '0; |
| 65 | end else if (!ref_en && |ref_cnt) begin |
| 66 | ref_cnt <= '0; |
| 67 | ref_valid <= '0; |
| 68 | end else if (ref_en && (ref_cnt == RefCnt - 1)) begin |
| 69 | // restart count and measure |
| 70 | ref_cnt <= '0; |
| 71 | ref_valid <= 1'b1; |
| 72 | end else if (ref_en) begin |
| 73 | ref_cnt <= ref_cnt + 1'b1; |
| 74 | ref_valid <= 1'b0; |
| 75 | end |
| 76 | end |
| 77 | |
Timothy Chen | 6e61902 | 2021-11-08 16:42:52 -0800 | [diff] [blame] | 78 | |
Timothy Chen | fe5d032 | 2021-08-30 15:55:37 -0700 | [diff] [blame] | 79 | ////////////////////////// |
| 80 | // Input Clock Logic |
| 81 | ////////////////////////// |
| 82 | |
| 83 | |
| 84 | logic valid; |
| 85 | |
| 86 | // The valid pulse causes the count to reset and start counting again |
| 87 | // for each reference cycle. |
| 88 | // The count obtained during the the last refernece cycle is then used |
| 89 | // to measure how fast/slow the input clock is. |
| 90 | prim_pulse_sync u_sync_ref ( |
| 91 | .clk_src_i(clk_ref_i), |
| 92 | .rst_src_ni(rst_ref_ni), |
| 93 | .src_pulse_i(ref_valid), |
| 94 | .clk_dst_i(clk_i), |
| 95 | .rst_dst_ni(rst_ni), |
| 96 | .dst_pulse_o(valid) |
| 97 | ); |
| 98 | |
| 99 | logic cnt_en; |
| 100 | always_ff @(posedge clk_i or negedge rst_ni) begin |
| 101 | if (!rst_ni) begin |
| 102 | cnt_en <= '0; |
| 103 | end else if (!en_i) begin |
| 104 | cnt_en <= '0; |
| 105 | end else if (en_i && valid) begin |
| 106 | cnt_en <= 1'b1; |
| 107 | end |
| 108 | end |
| 109 | |
| 110 | logic cnt_ovfl; |
| 111 | logic [CntWidth-1:0] cnt; |
| 112 | always_ff @(posedge clk_i or negedge rst_ni) begin |
| 113 | if (!rst_ni) begin |
| 114 | cnt <= '0; |
| 115 | cnt_ovfl <= '0; |
| 116 | end else if (!cnt_en && |cnt) begin |
| 117 | cnt <= '0; |
| 118 | cnt_ovfl <= '0; |
| 119 | end else if (valid_o) begin |
| 120 | cnt <= '0; |
| 121 | cnt_ovfl <= '0; |
| 122 | end else if (cnt_en && cnt_ovfl) begin |
| 123 | cnt <= '{default: '1}; |
| 124 | end else if (cnt_en) begin |
| 125 | {cnt_ovfl, cnt} <= cnt + 1'b1; |
| 126 | end |
| 127 | end |
| 128 | |
| 129 | assign valid_o = en_i & valid & |cnt; |
Timothy Chen | 6153a62 | 2021-09-28 14:11:14 -0700 | [diff] [blame] | 130 | assign fast_o = valid_o & ((cnt > max_cnt) | cnt_ovfl); |
| 131 | assign slow_o = valid_o & (cnt < min_cnt); |
Timothy Chen | fe5d032 | 2021-08-30 15:55:37 -0700 | [diff] [blame] | 132 | |
| 133 | ////////////////////////// |
Timothy Chen | 6e61902 | 2021-11-08 16:42:52 -0800 | [diff] [blame] | 134 | // Timeout Handling |
| 135 | ////////////////////////// |
| 136 | |
| 137 | localparam bit TimeOutChkEn = ClkTimeOutChkEn | RefTimeOutChkEn; |
| 138 | |
| 139 | // determine ratio between |
| 140 | localparam int ClkRatio = Cnt / RefCnt; |
| 141 | |
| 142 | // maximum cdc latency from the perspective of the measured clock |
| 143 | // 1 cycle to output request |
| 144 | // 2 ref cycles for synchronization |
| 145 | // 1 ref cycle to send ack |
| 146 | // 2 cycles to sync ack |
| 147 | // Double for margin |
| 148 | localparam int MaxClkCdcLatency = (1 + 2*ClkRatio + 1*ClkRatio + 2)*2; |
| 149 | |
| 150 | // maximum cdc latency from the perspective of the reference clock |
| 151 | // 1 ref cycle to output request |
Timothy Chen | b61a219 | 2022-01-11 15:51:21 -0800 | [diff] [blame] | 152 | // 2 cycles to sync + 1 cycle to ack are less than 1 cycle of ref based on assertion requirement |
Timothy Chen | 6e61902 | 2021-11-08 16:42:52 -0800 | [diff] [blame] | 153 | // 2 ref cycles to sync ack |
Timothy Chen | b61a219 | 2022-01-11 15:51:21 -0800 | [diff] [blame] | 154 | // 2 extra ref cycles for margin |
| 155 | localparam int MaxRefCdcLatency = 1 + 1 + 2 + 2; |
Timothy Chen | 6e61902 | 2021-11-08 16:42:52 -0800 | [diff] [blame] | 156 | |
| 157 | if (RefTimeOutChkEn) begin : gen_ref_timeout_chk |
| 158 | // check whether reference clock has timed out |
| 159 | prim_clock_timeout #( |
| 160 | .TimeOutCnt(MaxClkCdcLatency) |
| 161 | ) u_timeout_clk_to_ref ( |
| 162 | .clk_chk_i(clk_ref_i), |
| 163 | .rst_chk_ni(rst_ref_ni), |
| 164 | .clk_i, |
| 165 | .rst_ni, |
| 166 | .en_i, |
| 167 | .timeout_o(timeout_clk_ref_o) |
| 168 | ); |
| 169 | end else begin : gen_unused_ref_timeout |
| 170 | assign timeout_clk_ref_o = 1'b0; |
| 171 | end |
| 172 | |
| 173 | if (ClkTimeOutChkEn) begin : gen_clk_timeout_chk |
| 174 | // check whether clock has timed out |
| 175 | prim_clock_timeout #( |
| 176 | .TimeOutCnt(MaxRefCdcLatency) |
| 177 | ) u_timeout_ref_to_clk ( |
| 178 | .clk_chk_i(clk_i), |
| 179 | .rst_chk_ni(rst_ni), |
| 180 | .clk_i(clk_ref_i), |
| 181 | .rst_ni(rst_ref_ni), |
| 182 | .en_i(ref_en), |
| 183 | .timeout_o(ref_timeout_clk_o) |
| 184 | ); |
| 185 | end else begin : gen_unused_clk_timeout |
| 186 | assign ref_timeout_clk_o = 1'b0; |
| 187 | end |
| 188 | |
| 189 | |
| 190 | ////////////////////////// |
Timothy Chen | fe5d032 | 2021-08-30 15:55:37 -0700 | [diff] [blame] | 191 | // Assertions |
| 192 | ////////////////////////// |
| 193 | |
Timothy Chen | 6e61902 | 2021-11-08 16:42:52 -0800 | [diff] [blame] | 194 | if (TimeOutChkEn) begin : gen_timeout_assert |
| 195 | // the measured clock must be faster than the reference clock |
| 196 | `ASSERT_INIT(ClkRatios_A, ClkRatio > 2) |
| 197 | end |
| 198 | |
| 199 | // reference count has to be at least 1 |
Timothy Chen | fe5d032 | 2021-08-30 15:55:37 -0700 | [diff] [blame] | 200 | `ASSERT_INIT(RefCntVal_A, RefCnt >= 1) |
| 201 | |
| 202 | // if we've reached the max count, enable must be 0 next. |
| 203 | // Otherwise the width of the counter is too small to accommodate the usecase |
| 204 | `ASSERT(MaxWidth_A, (cnt == Cnt-1) |=> !cnt_en ) |
| 205 | |
| 206 | |
| 207 | |
| 208 | |
| 209 | endmodule // prim_clk_meas |