blob: 613f17388c1d8a55fa1d66b0f42e904a71b4fc40 [file] [log] [blame]
Timothy Chenfe5d0322021-08-30 15:55:37 -07001// 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
15module 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 Chen6e619022021-11-08 16:42:52 -080020 parameter bit ClkTimeOutChkEn = 1,
21 parameter bit RefTimeOutChkEn = 1,
Timothy Chenfe5d0322021-08-30 15:55:37 -070022 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 Chen6e619022021-11-08 16:42:52 -080036 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 Chenfe5d0322021-08-30 15:55:37 -070043);
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 Chen6e619022021-11-08 16:42:52 -080078
Timothy Chenfe5d0322021-08-30 15:55:37 -070079 //////////////////////////
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 Chen6153a622021-09-28 14:11:14 -0700130 assign fast_o = valid_o & ((cnt > max_cnt) | cnt_ovfl);
131 assign slow_o = valid_o & (cnt < min_cnt);
Timothy Chenfe5d0322021-08-30 15:55:37 -0700132
133 //////////////////////////
Timothy Chen6e619022021-11-08 16:42:52 -0800134 // 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 Chenb61a2192022-01-11 15:51:21 -0800152 // 2 cycles to sync + 1 cycle to ack are less than 1 cycle of ref based on assertion requirement
Timothy Chen6e619022021-11-08 16:42:52 -0800153 // 2 ref cycles to sync ack
Timothy Chenb61a2192022-01-11 15:51:21 -0800154 // 2 extra ref cycles for margin
155 localparam int MaxRefCdcLatency = 1 + 1 + 2 + 2;
Timothy Chen6e619022021-11-08 16:42:52 -0800156
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 Chenfe5d0322021-08-30 15:55:37 -0700191 // Assertions
192 //////////////////////////
193
Timothy Chen6e619022021-11-08 16:42:52 -0800194 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 Chenfe5d0322021-08-30 15:55:37 -0700200 `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
209endmodule // prim_clk_meas