Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: cluster_hdr_chkr.vr | |
4 | // Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved | |
5 | // 4150 Network Circle, Santa Clara, California 95054, U.S.A. | |
6 | // | |
7 | // * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
8 | // | |
9 | // This program is free software; you can redistribute it and/or modify | |
10 | // it under the terms of the GNU General Public License as published by | |
11 | // the Free Software Foundation; version 2 of the License. | |
12 | // | |
13 | // This program is distributed in the hope that it will be useful, | |
14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | // GNU General Public License for more details. | |
17 | // | |
18 | // You should have received a copy of the GNU General Public License | |
19 | // along with this program; if not, write to the Free Software | |
20 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | // | |
22 | // For the avoidance of doubt, and except that if any non-GPL license | |
23 | // choice is available it will apply instead, Sun elects to use only | |
24 | // the General Public License version 2 (GPLv2) at this time for any | |
25 | // software where a choice of GPL license versions is made | |
26 | // available with the language indicating that GPLv2 or any later version | |
27 | // may be used, or where a choice of which version of the GPL is applied is | |
28 | // otherwise unspecified. | |
29 | // | |
30 | // Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, | |
31 | // CA 95054 USA or visit www.sun.com if you need additional information or | |
32 | // have any questions. | |
33 | // | |
34 | // ========== Copyright Header End ============================================ | |
35 | #include <vera_defines.vrh> | |
36 | #include "std_display_class.vrh" | |
37 | #include "ccu_top.vri" | |
38 | #include "cluster_hdr_top.vri" | |
39 | #include "ccu_clk_packet.vrh" | |
40 | #include "ccu_clks_states.vrh" | |
41 | ||
42 | class CLUSTER_hdr_chkr { | |
43 | local string name; // name of the cluster header | |
44 | local string dispScope; // standard display: display scope | |
45 | local integer verbose; // 0: disable verbose mode; otherwise, enable | |
46 | ||
47 | //---ports and classes--- | |
48 | local CLKGEN_port clkgen_port; // port of this cluster header | |
49 | local CLKGEN_port clkgen_dr_port; // DR cluster hdr for checking DR sync locations | |
50 | local CLKGEN_port clkgen_io_port; // IO cluster hdr for checking IO sync locations | |
51 | local CLKGEN_port clkgen_io2x_port; // IO2X cluster hdr for checking IO2X sync locations | |
52 | local CCU_clk_port ccu_clk_port; | |
53 | local StandardDisplay dbg; // Standard display for printing | |
54 | local CCU_clks_states ccu_states; // keep track CCU states | |
55 | ||
56 | //--- vars for start/stop checker/checking---- | |
57 | local integer running; // 0: not running; otherwise, running | |
58 | local event stop_checker_e; // triggered by stop_checker() to terminate start_checker() | |
59 | local event stop_checking_e; // triggered by stop_checking() to terminate start_checking() | |
60 | local event clkstop_e; // triggered when tcu_clk_stop detected | |
61 | ||
62 | //---vars for error reporting--- | |
63 | local integer error_cnt; // Global error count. Init to 0. WARN: negative if exeed max integer | |
64 | local integer max_error_printed; // stop print out error if num errors exceed this value | |
65 | local integer max_debug_printed; // stop print out debug msg if debug cnt exceeds this value | |
66 | local integer dr_sync_loc_err_cnt, dr_sync_loc_debug_cnt; | |
67 | ||
68 | //---vars for cluster parameters--- | |
69 | local cluster_hdr_type_e hdr_type; // cluster type | |
70 | local CCU_clk_packet clk_pkt; // clock-related paramters | |
71 | local integer l2clk_per_nom, l2clk_per_min, l2clk_per_max; // l2clk period: nominal, min, max | |
72 | local integer gclk_per_nom, gclk_per_min, gclk_per_max; // gclk period: nominal, min, max | |
73 | local integer pw_dev; // deviation of l2clk pulse width (in %) | |
74 | local integer cmpslow_sync_is_io2x; // cmp_slow_sync_en is actually io2x_sync_en (MIO block does this) | |
75 | ||
76 | //---vars for enable/disable checking sync pulses---- | |
77 | local integer chk_cmpslow_sync, chk_slowcmp_sync; // 0: not check; otherwise, check | |
78 | local integer chk_dr_sync, chk_io2x_sync; // 0: not check; otherwise, check | |
79 | local integer chk_iosync_loc, chk_io2xsync_loc, chk_drsync_loc; // 0: not check; otherwise, check | |
80 | ||
81 | //--public tasks--- | |
82 | task new(string name, StandardDisplay dbg, | |
83 | CCU_clks_states ccu_states, cluster_hdr_type_e hdr_type, | |
84 | CLKGEN_port clkgen_port, CLKGEN_port clkgen_dr_port, | |
85 | CLKGEN_port clkgen_io_port, CLKGEN_port clkgen_io2x_port, | |
86 | integer chk_cmpslow_sync, | |
87 | integer chk_slowcmp_sync, integer chk_dr_sync, | |
88 | integer chk_io2x_sync, | |
89 | integer cmpslow_sync_is_io2x=0, integer start_it=1); | |
90 | task start_checker(); | |
91 | task stop_checker(); | |
92 | ||
93 | //--control tasks--- | |
94 | local task start_checking(); | |
95 | local task stop_checking(); | |
96 | local task check_l2clk_and_sync_pulses(); | |
97 | ||
98 | //---tasks do the checks--- | |
99 | local task check_gclk_toggle(); | |
100 | local task check_gclk_freq_dutycycle(); | |
101 | ||
102 | local task check_l2clk_toggle(); | |
103 | local task check_l2clk_freq_dutycyc(); | |
104 | ||
105 | local task check_iox_sync(string sync_name); | |
106 | ||
107 | local task check_dr_sync(); | |
108 | ||
109 | local task check_cmp_io_sync_loc(integer nchecks=5); | |
110 | local task check_io_cmp_sync_loc(integer nchecks=5); | |
111 | local task check_io2x_sync_loc(integer nchecks=5); | |
112 | local task check_dr_sync_loc(integer nchecks=20); | |
113 | local task check_next_dr_sync_loc(); | |
114 | ||
115 | //---- supporting subroutines ---- | |
116 | local task compute_expected_params(); | |
117 | local task print_error_msg(integer err_cnt, string error_msg); | |
118 | local task print_debug_msg(integer debug_cnt, string debug_msg); | |
119 | local function integer is_outside_min_max(integer min_val, integer value, integer max_val); | |
120 | local function integer compute_abs(integer n); | |
121 | local task wait_1st_posedge(string sig_name, integer timeout_val); | |
122 | } | |
123 | ||
124 | //################################################################ | |
125 | //######### implementation of subroutines ########### | |
126 | //################################################################ | |
127 | ||
128 | task CLUSTER_hdr_chkr::new(string name, StandardDisplay dbg, | |
129 | CCU_clks_states ccu_states, cluster_hdr_type_e hdr_type, | |
130 | CLKGEN_port clkgen_port, CLKGEN_port clkgen_dr_port, | |
131 | CLKGEN_port clkgen_io_port, CLKGEN_port clkgen_io2x_port, | |
132 | integer chk_cmpslow_sync, | |
133 | integer chk_slowcmp_sync, integer chk_dr_sync, | |
134 | integer chk_io2x_sync, | |
135 | integer cmpslow_sync_is_io2x=0, integer start_it=1) { | |
136 | ||
137 | //---from arg list--- | |
138 | this.name = name; | |
139 | this.dbg = dbg; | |
140 | this.ccu_states = ccu_states; | |
141 | this.hdr_type = hdr_type; | |
142 | this.clkgen_port = clkgen_port; | |
143 | this.clkgen_dr_port = clkgen_dr_port; | |
144 | this.clkgen_io_port = clkgen_io_port; | |
145 | this.clkgen_io2x_port = clkgen_io2x_port; | |
146 | this.chk_cmpslow_sync = chk_cmpslow_sync; | |
147 | this.chk_slowcmp_sync = chk_slowcmp_sync; | |
148 | this.chk_dr_sync = chk_dr_sync; | |
149 | this.chk_io2x_sync = chk_io2x_sync; | |
150 | this.cmpslow_sync_is_io2x = cmpslow_sync_is_io2x; | |
151 | ||
152 | //---the rest--- | |
153 | this.dispScope = this.name; | |
154 | this.verbose = 0; //default is disabling verbose mode | |
155 | this.ccu_clk_port = ccu_clk_bind; | |
156 | this.running = 0; | |
157 | this.error_cnt = 0; | |
158 | this.max_error_printed = 20; | |
159 | this.max_debug_printed = 20; | |
160 | this.dr_sync_loc_err_cnt = 0; // reset error counter | |
161 | this.dr_sync_loc_debug_cnt = 0; // reset debug counter | |
162 | this.clk_pkt = null; // init to illegal value | |
163 | this.l2clk_per_nom = -1; // init to illegal value | |
164 | this.l2clk_per_min = -1; // init to illegal value | |
165 | this.l2clk_per_max = -1; // init to illegal value | |
166 | this.gclk_per_nom = -1; // init to illegal value | |
167 | this.gclk_per_min = -1; // init to illegal value | |
168 | this.gclk_per_max = -1; // init to illegal value | |
169 | this.pw_dev = -1; // init to illegal value | |
170 | ||
171 | //--checking sync pulse locations--- | |
172 | this.chk_iosync_loc = 1; // by default: checkin sync locations | |
173 | this.chk_io2xsync_loc = 1; | |
174 | this.chk_drsync_loc = 1; | |
175 | ||
176 | //---override if runtime options specified--- | |
177 | if (get_plus_arg(CHECK, "clstrHdrChkr_maxError=n")) | |
178 | this.max_error_printed = get_plus_arg(NUM, "clstrHdrChkr_maxError=n"); | |
179 | if (get_plus_arg(CHECK, "clstrHdrChkr_noSyncLocChk")) { | |
180 | this.chk_iosync_loc = 0; | |
181 | this.chk_io2xsync_loc = 0; | |
182 | this.chk_drsync_loc = 0; | |
183 | } | |
184 | if (get_plus_arg(CHECK, "clstrHdrChkr_verbose")) | |
185 | this.verbose = 1; | |
186 | ||
187 | //---start background threads--- | |
188 | if (start_it) | |
189 | this.start_checker(); | |
190 | } | |
191 | ||
192 | //============================================================= | |
193 | // WHAT: start the checker | |
194 | //============================================================= | |
195 | task CLUSTER_hdr_chkr::start_checker() { | |
196 | reg bit_value; | |
197 | ||
198 | if (this.running) | |
199 | return; // it's already running | |
200 | dbg.dispmon(this.dispScope, MON_INFO, "starts ..."); | |
201 | this.running = 1; | |
202 | fork { | |
203 | fork { | |
204 | while (1) { | |
205 | bit_value = ccu_clk_port.$ccu_rst_sync_stable; | |
206 | if (bit_value !== 1'b1) | |
207 | @(posedge ccu_clk_port.$ccu_rst_sync_stable); // Sync is stable, so start checking | |
208 | delay(3); // avoid race condition with ccu_states.get_clk_pkt() | |
209 | this.clk_pkt = ccu_states.get_exp_clk_pkt(); | |
210 | compute_expected_params(); | |
211 | start_checking(); | |
212 | @(negedge ccu_clk_port.$rst_ccu_pll_); // reset PLL | |
213 | stop_checking(); | |
214 | } | |
215 | } | |
216 | join none | |
217 | sync(ALL, this.stop_checker_e); // stop_checker() triggers this event | |
218 | terminate; | |
219 | dbg.dispmon(this.dispScope, MON_INFO, "stopped"); | |
220 | this.running = 0; // checker is not running | |
221 | } join none | |
222 | } | |
223 | ||
224 | //============================================================= | |
225 | // WHAT: stop the checker | |
226 | //============================================================= | |
227 | task CLUSTER_hdr_chkr::stop_checker() { | |
228 | if (this.running) | |
229 | trigger(this.stop_checker_e); // this event terminates start_checker() | |
230 | } | |
231 | ||
232 | //============================================================= | |
233 | // Start to check | |
234 | //============================================================= | |
235 | task CLUSTER_hdr_chkr::start_checking() { | |
236 | dbg.dispmon(this.dispScope, MON_INFO, "start checking ..."); | |
237 | fork { | |
238 | fork | |
239 | { | |
240 | check_gclk_toggle(); | |
241 | } | |
242 | { | |
243 | check_gclk_freq_dutycycle(); | |
244 | } | |
245 | { | |
246 | check_l2clk_and_sync_pulses(); | |
247 | } | |
248 | join none | |
249 | sync(ALL, this.stop_checking_e); // stop_checking() triggers this event | |
250 | terminate; | |
251 | dbg.dispmon(this.dispScope, MON_INFO, "checking stopped"); | |
252 | } join none | |
253 | } | |
254 | ||
255 | //============================================================= | |
256 | // WHAT: stop checking by terminating start_checking() | |
257 | //============================================================= | |
258 | task CLUSTER_hdr_chkr::stop_checking() { | |
259 | trigger(this.stop_checking_e); // this event terminates start_checking() | |
260 | } | |
261 | ||
262 | //============================================================= | |
263 | // WHAT: control task for start/stop checking l2clk and sync pulses | |
264 | //============================================================= | |
265 | task CLUSTER_hdr_chkr::check_l2clk_and_sync_pulses() { | |
266 | event stop_check_l2clk_sync; | |
267 | ||
268 | while (1) { | |
269 | //----not checking during clock stop or cluster_arst_l---- | |
270 | while (1) { | |
271 | if ((clkgen_port.$tcu_clk_stop__gclk == 1'b0) | |
272 | && (clkgen_port.$cluster_arst_l__gclk == 1'b1)) | |
273 | break; | |
274 | @(posedge clkgen_port.$gclk); | |
275 | } | |
276 | //----check l2clk---- | |
277 | fork { check_l2clk_toggle(); } join none | |
278 | fork { check_l2clk_freq_dutycyc(); } join none | |
279 | //--- check sync pulse --- | |
280 | if (this.chk_cmpslow_sync) { | |
281 | fork { check_iox_sync("cmp_slow_sync_en"); } join none | |
282 | if (this.chk_iosync_loc) { | |
283 | if (this.cmpslow_sync_is_io2x) | |
284 | fork { check_io2x_sync_loc(); } join none | |
285 | else | |
286 | fork { check_cmp_io_sync_loc(); } join none | |
287 | } | |
288 | } | |
289 | if (this.chk_slowcmp_sync) { | |
290 | fork { check_iox_sync("slow_cmp_sync_en"); } join none | |
291 | if (this.chk_iosync_loc) | |
292 | fork { check_io_cmp_sync_loc(); } join none | |
293 | } | |
294 | if (this.chk_io2x_sync) { | |
295 | fork { check_iox_sync("io2x_sync_en"); } join none | |
296 | if (this.chk_io2xsync_loc) | |
297 | fork { check_io2x_sync_loc(); } join none | |
298 | } | |
299 | if (this.chk_dr_sync) { | |
300 | fork { check_dr_sync(); } join none | |
301 | if (this.chk_drsync_loc) | |
302 | fork { check_dr_sync_loc(); } join none | |
303 | } | |
304 | //--- stop checking when tcu_clk_stop or scan_en asserted--- | |
305 | fork { | |
306 | @(posedge clkgen_port.$tcu_clk_stop__gclk or negedge clkgen_port.$cluster_arst_l__gclk); | |
307 | trigger(stop_check_l2clk_sync); | |
308 | } join none | |
309 | sync(ALL, stop_check_l2clk_sync); | |
310 | terminate; | |
311 | } | |
312 | } | |
313 | ||
314 | //============================================================= | |
315 | // Compute expected values for this checker | |
316 | //============================================================= | |
317 | task CLUSTER_hdr_chkr::compute_expected_params() { | |
318 | ||
319 | //---compute l2clk period--- | |
320 | case (this.hdr_type) { | |
321 | CLUSTER_HDR_CMP, CLUSTER_HDR_CMP1 : { | |
322 | this.l2clk_per_nom = clk_pkt.cmp_clk_per_nom; | |
323 | this.l2clk_per_min = clk_pkt.cmp_clk_per_min; | |
324 | this.l2clk_per_max = clk_pkt.cmp_clk_per_max; | |
325 | } | |
326 | CLUSTER_HDR_DR : { | |
327 | this.l2clk_per_nom = clk_pkt.dr_clk_per_nom; | |
328 | this.l2clk_per_min = clk_pkt.dr_clk_per_min; | |
329 | this.l2clk_per_max = clk_pkt.dr_clk_per_max; | |
330 | } | |
331 | CLUSTER_HDR_IO : { | |
332 | this.l2clk_per_nom = clk_pkt.io_out_per_nom; | |
333 | this.l2clk_per_min = clk_pkt.io_out_per_min; | |
334 | this.l2clk_per_max = clk_pkt.io_out_per_max; | |
335 | } | |
336 | CLUSTER_HDR_IO2X : { | |
337 | this.l2clk_per_nom = clk_pkt.io2x_out_per_nom; | |
338 | this.l2clk_per_min = clk_pkt.io2x_out_per_min; | |
339 | this.l2clk_per_max = clk_pkt.io2x_out_per_max; | |
340 | } | |
341 | default: | |
342 | dbg.dispmon(this.dispScope, MON_ERR, psprintf("%0d is illegal value for hdr type", hdr_type)); | |
343 | } | |
344 | //---compute gclk period and pw_dev--- | |
345 | if (hdr_type == CLUSTER_HDR_DR) { | |
346 | this.gclk_per_nom = clk_pkt.dr_clk_per_nom; | |
347 | this.gclk_per_min = clk_pkt.dr_clk_per_min; | |
348 | this.gclk_per_max = clk_pkt.dr_clk_per_max; | |
349 | this.pw_dev = clk_pkt.dr_pw_dev; | |
350 | } | |
351 | else { // cmp/io/io2x header | |
352 | this.gclk_per_nom = clk_pkt.cmp_clk_per_nom; | |
353 | this.gclk_per_min = clk_pkt.cmp_clk_per_min; | |
354 | this.gclk_per_max = clk_pkt.cmp_clk_per_max; | |
355 | this.pw_dev = clk_pkt.cmp_pw_dev; | |
356 | } | |
357 | } | |
358 | ||
359 | //============================================================= | |
360 | // WHAT: check gclk is toggles in every 1.5 ccu.dr_pll_clk (for DR | |
361 | // cluster hdr) or ccu.cmp_pll_clk (for other cluster hdrs) | |
362 | // ASSUMPTION: ccu.cmp_pll_clk and ccu.dr_pll_clk clocks toggle. | |
363 | // NOTE: ccu generates sync_stable from gclk, so gclk must toggle | |
364 | //============================================================= | |
365 | task CLUSTER_hdr_chkr::check_gclk_toggle() { | |
366 | integer old_cyc, new_cyc, ncycs; // gclk cycle counts | |
367 | string ref_clk_name; | |
368 | integer err_cnt=0, debug_cnt=0; // error count | |
369 | ||
370 | dbg.dispmon(this.dispScope, MON_INFO, "monitor: gclk toggles"); | |
371 | case (this.hdr_type) { | |
372 | CLUSTER_HDR_DR: { | |
373 | ref_clk_name = "ccu.dr_pll_clk"; | |
374 | @(posedge ccu_clk_port.$dr_pll_clk); | |
375 | } | |
376 | default: { | |
377 | ref_clk_name = "ccu.cmp_pll_clk"; | |
378 | @(posedge ccu_clk_port.$cmp_pll_clk); | |
379 | } | |
380 | } | |
381 | old_cyc = get_cycle(clkgen_port.$gclk); | |
382 | while (1) { | |
383 | case (this.hdr_type) { | |
384 | CLUSTER_HDR_DR: repeat (3) @(ccu_clk_port.$dr_pll_clk); // 1.5 clk cycles | |
385 | default: repeat (3) @(ccu_clk_port.$cmp_pll_clk); // 1.5 clk cycles | |
386 | } | |
387 | new_cyc = get_cycle(clkgen_port.$gclk); | |
388 | ncycs = new_cyc - old_cyc; | |
389 | if (is_outside_min_max(1, ncycs, 2)) | |
390 | print_error_msg(err_cnt++, psprintf("check_gclk_toggle: num gclk clks in 1.5 %s clk cycles: %0d. Expect: 1 or 2", ref_clk_name, ncycs)); | |
391 | old_cyc = new_cyc; | |
392 | } | |
393 | } | |
394 | ||
395 | //============================================================= | |
396 | // WHAT: check gclk period and duty cycle. | |
397 | // NOTE: gclk should be stable when ccu_rst_sync_stable asserted. | |
398 | //============================================================= | |
399 | task CLUSTER_hdr_chkr::check_gclk_freq_dutycycle() { | |
400 | integer per; // gclk period | |
401 | integer pw_hi, pw_lo, pw_min, pw_nom, pw_max; // gclk pulse width. | |
402 | integer pos_edge, neg_edge; // clk posedge and negedge | |
403 | integer per_err_cnt = 0, pw_err_cnt=0; // error counts | |
404 | integer debug_cnt=0; // debug msg counts | |
405 | ||
406 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("monitor: gclk period (min=%0d, max=%0d) and duty cycle", this.gclk_per_min, this.gclk_per_max)); | |
407 | @(posedge clkgen_port.$gclk); | |
408 | pos_edge = get_time(LO); | |
409 | while (1) { | |
410 | @(negedge clkgen_port.$gclk); | |
411 | neg_edge = get_time(LO); | |
412 | pw_hi = neg_edge - pos_edge; | |
413 | @(posedge clkgen_port.$gclk); | |
414 | pos_edge = get_time(LO); | |
415 | pw_lo = pos_edge - neg_edge; | |
416 | per = pw_hi + pw_lo; | |
417 | pw_nom = per / 2; // nominal is 50% duty cycle | |
418 | pw_min = (pw_nom - 1) - (((this.gclk_per_nom / 100) + 1) * this.pw_dev); // rounding down | |
419 | pw_max = (pw_nom + 1) + (((this.gclk_per_nom / 100) + 1) * this.pw_dev); // rounding up | |
420 | if (is_outside_min_max(this.gclk_per_min, per, this.gclk_per_max)) | |
421 | print_error_msg(per_err_cnt++, psprintf("check_gclk_freq_pw: gclk: period=%0d, expect: min=%0d, max=%0d", per, this.gclk_per_min, this.gclk_per_max)); | |
422 | if (is_outside_min_max(pw_min, pw_lo, pw_max) || is_outside_min_max(pw_min, pw_hi, pw_max)) | |
423 | print_error_msg(pw_err_cnt++, psprintf("check_gclk_freq_pw: gclk: pw_hi=%0d, pw_lo=%0d. expect: pw_min=%0d, pw_max=%0d", | |
424 | pw_hi, pw_lo, pw_min, pw_max)); | |
425 | if (this.verbose) | |
426 | print_debug_msg(debug_cnt++, psprintf("check_gclk_freq_pw: gclk: per=%0d, pw_hi=%0d, pw_lo=%0d. expect: per_min=%0d, per_max=%0d, pw_min=%0d, pw_max=%0d", | |
427 | per, pw_hi, pw_lo, this.gclk_per_min, this.gclk_per_max, pw_min, pw_max)); | |
428 | } | |
429 | } | |
430 | ||
431 | //============================================================= | |
432 | // WHAT: check l2clk toggles in each gclk (for cmp and dr headers), | |
433 | // every clk_pkt.cmp2io_ratio gclk (for IO header) or every | |
434 | // clk_pkt.cmp2io2x_ratio gclk (for IO2X header) | |
435 | //============================================================= | |
436 | task CLUSTER_hdr_chkr::check_l2clk_toggle() { | |
437 | integer gclk_ncycs; // number of gclk cycles that l2clk toggles once | |
438 | integer old_cyc, new_cyc, ncycs; // cycle count of l2clk | |
439 | integer err_cnt=0, debug_cnt=0; | |
440 | ||
441 | case (this.hdr_type) { // note: +1 is needed in case clk edges are perfectly aligned. | |
442 | CLUSTER_HDR_IO: gclk_ncycs = clk_pkt.cmp2io_ratio + 1; // gclk is cmp clk and l2clk is IO clk | |
443 | CLUSTER_HDR_IO2X: gclk_ncycs = clk_pkt.cmp2io2x_ratio + 1; // gclk is cmp clk and l2clk is IO2X clk | |
444 | default: gclk_ncycs = 1 + 1; // gclk is cmp clk or dr clk | |
445 | } | |
446 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("monitor: l2clk toggles at least once for every %0d gclk cycles", gclk_ncycs)); | |
447 | ||
448 | //---wait for first l2clk--- | |
449 | wait_1st_posedge("l2clk", 10 * gclk_ncycs); // timeout in gclk cycs | |
450 | ||
451 | //--- checking --- | |
452 | @(posedge clkgen_port.$gclk); | |
453 | old_cyc = get_cycle(clkgen_port.$l2clk); | |
454 | while (1) { | |
455 | repeat (gclk_ncycs) @(posedge clkgen_port.$gclk); | |
456 | new_cyc = get_cycle(clkgen_port.$l2clk); | |
457 | ncycs = new_cyc - old_cyc; | |
458 | if (is_outside_min_max(1, ncycs, 2)) | |
459 | print_error_msg(err_cnt++, psprintf("check_l2clk_toggle: num l2clk clk in %0d gclk: %0d. Expect: 1 or 2", gclk_ncycs, ncycs)); | |
460 | else if (this.verbose) | |
461 | print_debug_msg(debug_cnt++, psprintf("check_l2clk_toggle: num l2clk clk in %0d gclk: %0d. Expect: 1 or 2", gclk_ncycs, ncycs)); | |
462 | old_cyc = new_cyc; | |
463 | } | |
464 | } | |
465 | ||
466 | //============================================================= | |
467 | // WHAT: check freq and duty cycle of l2clk. | |
468 | //============================================================= | |
469 | task CLUSTER_hdr_chkr::check_l2clk_freq_dutycyc() { | |
470 | integer clk_posedge, clk_negedge, period; // l2clk | |
471 | integer pw_hi, pw_lo, pw_nom, pw_min, pw_max; // pw: pulse width | |
472 | integer per_err_cnt=0, pw_err_cnt=0; // error counts in period or pulse width | |
473 | integer debug_cnt=0; | |
474 | ||
475 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("monitor: l2clk period (min=%0d, max=%0d) and duty cycle", this.l2clk_per_min, this.l2clk_per_max)); | |
476 | @(posedge clkgen_port.$l2clk); // wait for first l2clk | |
477 | clk_posedge = get_time(LO); | |
478 | while (1) { | |
479 | @(negedge clkgen_port.$l2clk); | |
480 | clk_negedge = get_time(LO); | |
481 | pw_hi = clk_negedge - clk_posedge; | |
482 | ||
483 | @(posedge clkgen_port.$l2clk); | |
484 | clk_posedge = get_time(LO); | |
485 | pw_lo = clk_posedge - clk_negedge; | |
486 | period = pw_hi + pw_lo; | |
487 | ||
488 | pw_nom = period / 2; // nominal is 50% duty cycle | |
489 | pw_min = (pw_nom - 1) - (((this.l2clk_per_nom / 100) + 1) * this.pw_dev); // rounding down | |
490 | pw_max = (pw_nom + 1) + (((this.l2clk_per_nom / 100) + 1) * this.pw_dev); // rounding down | |
491 | ||
492 | if (is_outside_min_max(this.l2clk_per_min, period, this.l2clk_per_max)) | |
493 | print_error_msg(per_err_cnt++, psprintf("check_l2clk_freq_pw: l2clk: period=%0d, expected: min=%0d, max=%0d", period, this.l2clk_per_min, this.l2clk_per_max)); | |
494 | if (is_outside_min_max(pw_min, pw_hi, pw_max) || is_outside_min_max(pw_min, pw_lo, pw_max)) | |
495 | print_error_msg(pw_err_cnt++, psprintf("check_l2clk_freq_pw: l2clk duty cycle: pw_hi=%0d, pw_lo=%0d, expected: min=%0d, max=%0d (per=%0d)", | |
496 | pw_hi, pw_lo, pw_min, pw_max, period)); | |
497 | if (this.verbose) | |
498 | print_debug_msg(debug_cnt++, psprintf("check_l2clk_freq_pw: l2clk: per: %0d. Expect: min=%0d, max=%0d. pw_hi=%0d, pw_lo=%0d. Expect: min=%0d, max=%0d", | |
499 | period, this.l2clk_per_min, this.l2clk_per_max, pw_hi, pw_lo, pw_min, pw_max)); | |
500 | } | |
501 | } | |
502 | ||
503 | //============================================================= | |
504 | // WHAT: check IO and IO2x sync pulse | |
505 | // -there is one sync pulse in each IO/IO2X clk cycle | |
506 | // -sync pulse is one cmp clk | |
507 | // -next sync pulse is separated from prev sync pulse by one IO/IO2X clk cycle. | |
508 | // ARGs: sync_name must be "cmp_slow_sync_en", "slow_cmp_sync_en" or "io2x_sync_en" | |
509 | // WARNING: this task does NOT check sync pulse locations | |
510 | //============================================================= | |
511 | task CLUSTER_hdr_chkr::check_iox_sync(string sync_name) { | |
512 | integer cmp2slowclk_ratio; // cmp-to-io or cmp-to-io2x clk ratio | |
513 | integer ncycs_no_sync; // number of l2clk cycs that sync is low | |
514 | integer err_cnt = 0, debug_cnt = 0, i; | |
515 | bit sync_val; | |
516 | string special_note = ((sync_name == "cmp_slow_sync_en") && (this.cmpslow_sync_is_io2x))? "(actually is IO2X sync)" : ""; | |
517 | ||
518 | case (sync_name) { | |
519 | "cmp_slow_sync_en": { | |
520 | if (this.cmpslow_sync_is_io2x) cmp2slowclk_ratio = clk_pkt.cmp2io2x_ratio; // cmp_slow_sync_en is io2x sync | |
521 | else cmp2slowclk_ratio = clk_pkt.cmp2io_ratio; | |
522 | } | |
523 | "slow_cmp_sync_en": cmp2slowclk_ratio = clk_pkt.cmp2io_ratio; | |
524 | "io2x_sync_en": cmp2slowclk_ratio = clk_pkt.cmp2io2x_ratio; | |
525 | default: { | |
526 | dbg.dispmon(this.dispScope, MON_ERR, psprintf("CLUSTER_hdr_chkr::check_iox_sync(sync_name=%s) <= bad arg", sync_name)); | |
527 | this.error_cnt++; | |
528 | return; // ignore | |
529 | } | |
530 | } | |
531 | ncycs_no_sync = cmp2slowclk_ratio - 1; | |
532 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("monitor: %s %s: 1 sync pulse in %0d l2clk cycs", sync_name, special_note, cmp2slowclk_ratio)); | |
533 | ||
534 | //--wait for first sync pulse--- | |
535 | wait_1st_posedge(sync_name, 10 * cmp2slowclk_ratio ); // timeout in gclks | |
536 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("check_iox_sync: first %s <= WARNING: not checking", sync_name)); | |
537 | wait_1st_posedge(sync_name, 2 * cmp2slowclk_ratio ); // timeout in gclks | |
538 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("check_iox_sync: this %s pulse is used as reference data point", sync_name)); | |
539 | //--check sync pulse---- | |
540 | while (1) { | |
541 | for (i = 0; i < ncycs_no_sync; i++) { | |
542 | @(posedge clkgen_port.$l2clk); | |
543 | case (sync_name) { | |
544 | "cmp_slow_sync_en": sync_val = clkgen_port.$cmp_slow_sync_en__l2clk; | |
545 | "slow_cmp_sync_en": sync_val = clkgen_port.$slow_cmp_sync_en__l2clk; | |
546 | "io2x_sync_en": sync_val = clkgen_port.$io2x_sync_en__l2clk; | |
547 | } | |
548 | if (sync_val !== 1'b0) | |
549 | print_error_msg(err_cnt++, psprintf("check_iox_sync: %s %s is %b. Expect: low", sync_name, special_note, sync_val)); | |
550 | } | |
551 | @(posedge clkgen_port.$l2clk); | |
552 | case (sync_name) { | |
553 | "cmp_slow_sync_en": sync_val = clkgen_port.$cmp_slow_sync_en__l2clk; | |
554 | "slow_cmp_sync_en": sync_val = clkgen_port.$slow_cmp_sync_en__l2clk; | |
555 | "io2x_sync_en": sync_val = clkgen_port.$io2x_sync_en__l2clk; | |
556 | } | |
557 | if (sync_val !== 1'b1) | |
558 | print_error_msg(err_cnt++, psprintf("check_iox_sync: %s %s is %b. Expect: high", sync_name, special_note, sync_val)); | |
559 | else if (this.verbose) | |
560 | print_debug_msg(debug_cnt++, psprintf("check_iox_sync: %s %s is %b. Expect: high", sync_name, special_note, sync_val)); | |
561 | } | |
562 | } | |
563 | ||
564 | //============================================================= | |
565 | // WHAT: check dr_sync_en | |
566 | // -DR sync pulse width is one cmp clk cycle | |
567 | // -Number of DR sync pulses in 1.5 DR clk cycles is 1 or 2 | |
568 | // WARN: this task does NOT check DR sync locations. | |
569 | //============================================================= | |
570 | task CLUSTER_hdr_chkr::check_dr_sync() { | |
571 | integer err_cnt=0, debug_cnt=0; | |
572 | integer old_sync_cnt, new_sync_cnt, temp_sync_cnt, num_sync; // counts of number of sync pulses | |
573 | integer sync_cnt; | |
574 | ||
575 | wait_1st_posedge("dr_sync_en", 30); // timeout after 30 gclks | |
576 | @(posedge clkgen_port.$dr_sync_en__l2clk); // skip the 1st sync since sync_en can be during clk stop (ie. WMR) | |
577 | dbg.dispmon(this.dispScope, MON_INFO, "check_dr_sync: first dr_sync_en used as reference data point"); | |
578 | //--- check dr_sync_en width is one cmp clk --- | |
579 | fork { | |
580 | while (1) { | |
581 | @(posedge clkgen_port.$l2clk); | |
582 | if (clkgen_port.$dr_sync_en__l2clk != 1'b0) | |
583 | print_error_msg(err_cnt++, "check_dr_sync: dr_sync_en is high for more than one cmp cycle"); | |
584 | @(posedge clkgen_port.$dr_sync_en__l2clk); // cmp posedge at which dr_sync is high | |
585 | } | |
586 | } join none | |
587 | //--- increment counter for every dr_sync pulse--- | |
588 | sync_cnt = 1; // first sync pulse counted | |
589 | fork { | |
590 | while (1) { | |
591 | @(posedge clkgen_port.$dr_sync_en__l2clk); | |
592 | sync_cnt++; | |
593 | } | |
594 | } join none | |
595 | //--- check number of dr_sync pulses in 1 dr clk + 0.5 cmp clk--- | |
596 | @(posedge clkgen_dr_port.$l2clk); | |
597 | old_sync_cnt = sync_cnt; | |
598 | while (1) { | |
599 | @(posedge clkgen_dr_port.$l2clk); | |
600 | temp_sync_cnt = sync_cnt; | |
601 | repeat (2) @(clkgen_port.$l2clk); // repeat (1): not work for div2_eff is 9 | |
602 | new_sync_cnt = sync_cnt; | |
603 | num_sync = new_sync_cnt - old_sync_cnt; | |
604 | if (is_outside_min_max(1, num_sync, 2)) | |
605 | print_error_msg(err_cnt++, psprintf("check_dr_sync: number of dr_sync_en pulses in 1 DR clk + 1 cmp clk: %0d. Expect: 1 or 2", num_sync)); | |
606 | else if (this.verbose) | |
607 | print_debug_msg(debug_cnt++, psprintf("check_dr_sync: number of dr_sync_en pulses in 1 DR clk + 1 cmp clk: %0d. Expect: 1 or 2", num_sync)); | |
608 | old_sync_cnt = temp_sync_cnt; | |
609 | } | |
610 | } | |
611 | ||
612 | //############################################################################ | |
613 | //############################################################################ | |
614 | //#### tasks to check sync pulse locations #################### | |
615 | //############################################################################ | |
616 | //############################################################################ | |
617 | ||
618 | //============================================================= | |
619 | // WHAT: check locations of cmp_io_sync_en | |
620 | // WARNING: do NOT use this task for MIO cmp hdrs since cmp_slow_sync_en is IO2X sync. | |
621 | // Use check_io2x_sync_loc() instead. | |
622 | //============================================================= | |
623 | task CLUSTER_hdr_chkr::check_cmp_io_sync_loc(integer nchecks=10) { | |
624 | integer err_cnt=0, debug_cnt=0; | |
625 | reg sync_value; | |
626 | integer i; | |
627 | integer sync_time=0, clk_edge, old_clk_edge=0, clk_mid; | |
628 | integer sync_loc_dev; // deviation in sync pulse location | |
629 | ||
630 | if (this.cmpslow_sync_is_io2x) // call wrong task | |
631 | check_io2x_sync_loc(nchecks); // do a favor here | |
632 | ||
633 | //---wait for first IO sync, cmp clk and IO clk--- | |
634 | fork { | |
635 | @(clkgen_port.$cmp_slow_sync_en__l2clk); // wait for first IO sync pulse | |
636 | } | |
637 | { | |
638 | @(posedge clkgen_port.$l2clk); // wait for first cmp clk | |
639 | } | |
640 | { | |
641 | @(posedge clkgen_io_port.$l2clk); // wait for first IO l2clk | |
642 | } join | |
643 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("checking sync pulse locations of cmp_slow_sync_en ...")); | |
644 | //----check sync location for DTM mode ----- | |
645 | if (this.clk_pkt.mode == CCU_DTM_MODE) { | |
646 | sync_loc_dev = this.clk_pkt.cmp_clk_per_nom; | |
647 | fork { | |
648 | while (1) { | |
649 | @(posedge clkgen_port.$cmp_slow_sync_en__l2clk); | |
650 | @(posedge clkgen_port.$l2clk); | |
651 | sync_time = get_time(LO); // at flop header | |
652 | } | |
653 | } join none | |
654 | @(posedge clkgen_io_port.$l2clk); | |
655 | old_clk_edge = get_time(LO); | |
656 | for (i = 0; i < nchecks; i++) { | |
657 | @(posedge clkgen_io_port.$l2clk); | |
658 | clk_edge = get_time(LO); | |
659 | clk_mid = (clk_edge + old_clk_edge) / 2; | |
660 | if (compute_abs(sync_time - clk_mid) > sync_loc_dev) | |
661 | print_error_msg(err_cnt++, psprintf("sync location check: middle of IO l2clk: %0d, cmp_slow sync pulse at flop input: %0d <= more than %0d", clk_mid, sync_time, sync_loc_dev)); | |
662 | else if (this.verbose) | |
663 | print_debug_msg(debug_cnt++, psprintf("sync location check: middle of IO l2clk: %0d, cmp_slow sync pulse at flop input: %0d", clk_mid, sync_time)); | |
664 | old_clk_edge = clk_edge; | |
665 | } | |
666 | } | |
667 | else { | |
668 | //---check io sync locations--- | |
669 | for (i = 0; i < nchecks; i++) { | |
670 | @(negedge clkgen_io_port.$l2clk); // falling edge of iol2clk | |
671 | repeat (2) @(negedge clkgen_port.$l2clk); // skip 2 cmp clk falling edges | |
672 | @(posedge clkgen_port.$l2clk); // cmp clk rising edge | |
673 | sync_value = clkgen_port.$cmp_slow_sync_en__l2clk; | |
674 | if (sync_value !== 1'b1) | |
675 | print_error_msg(err_cnt++, psprintf("sync location check: cmp_slow_sync_en is %b. Expect: high", sync_value)); | |
676 | else if (this.verbose) | |
677 | print_debug_msg(debug_cnt++, psprintf("sync location check: cmp_slow_sync_en is %b. Expect: high", sync_value)); | |
678 | } | |
679 | } | |
680 | } | |
681 | ||
682 | //============================================================= | |
683 | // WHAT: check locations of io_cmp_sync_en | |
684 | //============================================================= | |
685 | task CLUSTER_hdr_chkr::check_io_cmp_sync_loc(integer nchecks=10) { | |
686 | integer err_cnt=0, debug_cnt=0; | |
687 | reg sync_value; | |
688 | integer i; | |
689 | integer sync_time=0, clk_edge, old_clk_edge=0, clk_mid; | |
690 | integer sync_loc_dev; // deviation in sync pulse location | |
691 | integer sync_at_phase; // phase at which sync pulse is high | |
692 | ||
693 | //---wait for first IO sync, cmp clk and IO clk--- | |
694 | fork { | |
695 | @(clkgen_port.$slow_cmp_sync_en__l2clk); // wait for first IO sync pulse | |
696 | } | |
697 | { | |
698 | @(posedge clkgen_port.$l2clk); // wait for first cmp clk | |
699 | } | |
700 | { | |
701 | @(posedge clkgen_io_port.$l2clk); // wait for first IO l2clk | |
702 | } join | |
703 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("checking sync pulse locations of slow_cmp_sync_en ...")); | |
704 | //----check sync location for DTM mode ----- | |
705 | ||
706 | //if (this.clk_pkt.mode == CCU_DTM_MODE) { //!!!! WARNING: this is OLD when cmp_slow/slow_cmp sync are at same locations | |
707 | // sync_loc_dev = this.clk_pkt.cmp_clk_per_nom; | |
708 | // fork { | |
709 | // while (1) { | |
710 | // @(posedge clkgen_port.$slow_cmp_sync_en__l2clk); | |
711 | // @(posedge clkgen_port.$l2clk); | |
712 | // sync_time = get_time(LO); // at flop header | |
713 | // } | |
714 | // } join none | |
715 | // @(posedge clkgen_io_port.$l2clk); | |
716 | // old_clk_edge = get_time(LO); | |
717 | // for (i = 0; i < nchecks; i++) { | |
718 | // @(posedge clkgen_io_port.$l2clk); | |
719 | // clk_edge = get_time(LO); | |
720 | // clk_mid = (clk_edge + old_clk_edge) / 2; | |
721 | // if (compute_abs(sync_time - clk_mid) > sync_loc_dev) { | |
722 | // print_error_msg(err_cnt++, psprintf("sync location check: middle of IO l2clk: %0d, slow_cmp sync pulse at flop input: %0d <= more than %0d", | |
723 | // clk_mid, sync_time, sync_loc_dev)); | |
724 | // } | |
725 | // old_clk_edge = clk_edge; | |
726 | // } | |
727 | //} | |
728 | if (this.clk_pkt.mode == CCU_DTM_MODE) { | |
729 | sync_at_phase = this.clk_pkt.cmp2io_ratio - 2; // at flop input: sync is high 1 clk before next IO clk posedge | |
730 | for (i = 0; i < nchecks; i++) { // so, atclkgen output, sync is high 2 clks before next IO clk posedge | |
731 | @(posedge clkgen_io_port.$l2clk); | |
732 | @(negedge clkgen_port.$l2clk); | |
733 | repeat (sync_at_phase) @(posedge clkgen_port.$l2clk); | |
734 | sync_value = clkgen_port.$slow_cmp_sync_en__l2clk; | |
735 | if (sync_value !== 1'b1) | |
736 | print_error_msg(err_cnt++, psprintf("sync location check: slow_cmp sync pulse is %b, expect: high", sync_value)); | |
737 | else if (this.verbose) | |
738 | print_debug_msg(debug_cnt++, psprintf("sync location check: slow_cmp sync pulse is %b, expect: high", sync_value)); | |
739 | } | |
740 | } | |
741 | else { | |
742 | //---check io sync locations---- | |
743 | for (i = 0; i < nchecks; i++) { | |
744 | @(posedge clkgen_io_port.$l2clk); | |
745 | repeat (2) @(negedge clkgen_port.$l2clk); // skip 2 cmp clk falling edges | |
746 | @(posedge clkgen_port.$l2clk); // cmp clk rising edge | |
747 | sync_value = clkgen_port.$slow_cmp_sync_en__l2clk; | |
748 | if (sync_value !== 1'b1) | |
749 | print_error_msg(err_cnt++, psprintf("sync location check: slow_cmp_sync_en is %b. Expect: high", sync_value)); | |
750 | else if (this.verbose) | |
751 | print_debug_msg(debug_cnt++, psprintf("sync location check: slow_cmp_sync_en is %b. Expect: high", sync_value)); | |
752 | } | |
753 | } | |
754 | } | |
755 | ||
756 | //============================================================= | |
757 | // WHAT: check locations of IO2X sync pulse | |
758 | // WARNING: cmp_slow_sync_en of MIO cmp clusters is IO2X sync en | |
759 | //============================================================= | |
760 | task CLUSTER_hdr_chkr::check_io2x_sync_loc(integer nchecks=10) { | |
761 | integer err_cnt=0, debug_cnt=0; | |
762 | reg sync_value; | |
763 | integer i; | |
764 | string special_note = (this.cmpslow_sync_is_io2x)? "(actually is IO2X sync)" : ""; | |
765 | string sync_name = (this.cmpslow_sync_is_io2x)? "cmp_slow_sync_en" : "io2x_sync_en"; | |
766 | integer sync_time=0, clk_edge, old_clk_edge=0, clk_mid; | |
767 | integer sync_loc_dev; // deviation in sync pulse location | |
768 | ||
769 | //---wait for first IO2X sync, cmp clk and IO2X clk--- | |
770 | fork { | |
771 | if (this.cmpslow_sync_is_io2x) | |
772 | @(posedge clkgen_port.$cmp_slow_sync_en__l2clk); | |
773 | else | |
774 | @(posedge clkgen_port.$io2x_sync_en__l2clk); // wait for first IO2X sync pulse | |
775 | } | |
776 | { | |
777 | @(posedge clkgen_port.$l2clk); // wait for first cmp clk | |
778 | } | |
779 | { | |
780 | @(posedge clkgen_io2x_port.$l2clk); // wait for first IO2X l2clk | |
781 | } join | |
782 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("checking sync pulse locations of %s %s...", sync_name, special_note)); | |
783 | //----check sync location for DTM mode ----- | |
784 | if (this.clk_pkt.mode == CCU_DTM_MODE) { | |
785 | sync_loc_dev = this.clk_pkt.cmp_clk_per_nom; | |
786 | fork { | |
787 | while (1) { | |
788 | if (this.cmpslow_sync_is_io2x) | |
789 | @(posedge clkgen_port.$cmp_slow_sync_en__l2clk); | |
790 | else | |
791 | @(posedge clkgen_port.$io2x_sync_en__l2clk); | |
792 | @(posedge clkgen_port.$l2clk); | |
793 | sync_time = get_time(LO); // at flop header | |
794 | } | |
795 | } join none | |
796 | @(posedge clkgen_io2x_port.$l2clk); | |
797 | old_clk_edge = get_time(LO); | |
798 | for (i = 0; i < nchecks; i++) { | |
799 | @(posedge clkgen_io2x_port.$l2clk); | |
800 | clk_edge = get_time(LO); | |
801 | clk_mid = (clk_edge + old_clk_edge) / 2; | |
802 | if (compute_abs(sync_time - clk_mid) > sync_loc_dev) | |
803 | print_error_msg(err_cnt++, psprintf("sync location check: middle of IO2X l2clk: %0d, io2x sync pulse at flop input: %0d <= more than %0d", clk_mid, sync_time, sync_loc_dev)); | |
804 | else if (this.verbose) | |
805 | print_debug_msg(debug_cnt++, psprintf("sync location check: middle of IO2X l2clk: %0d, io2x sync pulse at flop input: %0d", clk_mid, sync_time)); | |
806 | old_clk_edge = clk_edge; | |
807 | } | |
808 | } | |
809 | else { | |
810 | //--- check io2x_sync_en location--- | |
811 | for (i = 0; i < nchecks; i++) { | |
812 | @(negedge clkgen_io2x_port.$l2clk); // falling edge of io2x l2clk | |
813 | @(negedge clkgen_port.$l2clk); // falling edge of cmp clk | |
814 | @(posedge clkgen_port.$l2clk); // rising edge of cmp clk. Sync pulse should be high | |
815 | if (this.cmpslow_sync_is_io2x) | |
816 | sync_value = clkgen_port.$cmp_slow_sync_en__l2clk; | |
817 | else | |
818 | sync_value = clkgen_port.$io2x_sync_en__l2clk; | |
819 | if (sync_value !== 1'b1) | |
820 | print_error_msg(err_cnt++, psprintf("sync location check: %s %s is %b. Expect: high", sync_name, special_note, sync_value)); | |
821 | else if (this.verbose) | |
822 | print_debug_msg(debug_cnt++, psprintf("sync location check: %s %s is %b. Expect: high", sync_name, special_note, sync_value)); | |
823 | } | |
824 | } | |
825 | } | |
826 | ||
827 | //============================================================= | |
828 | // WHAT: check expected DR sync location at the flop input. DR sync | |
829 | // is flopped once after clkgen output. | |
830 | // HOW: the expected location of dr sync pulse is the cmp clk posedge | |
831 | // that is closet to the mid point of dr clk cycle (ie. delta). Since | |
832 | // the dr and cmp clk edges can shift a little, so testbench needs to | |
833 | // check the actual delta vs. (delta_min plus 1/3 of cmp clk cycle). | |
834 | //============================================================= | |
835 | task CLUSTER_hdr_chkr::check_dr_sync_loc(integer nchecks=20) { | |
836 | integer i, err_cnt=0, debug_cnt=0; | |
837 | integer sync_time=0, clk_edge, old_clk_edge=0, clk_mid; | |
838 | integer sync_loc_dev; // deviation in sync pulse location | |
839 | ||
840 | this.dr_sync_loc_err_cnt = 0; // reset error counter | |
841 | this.dr_sync_loc_debug_cnt = 0; // reset debug counter | |
842 | //---wait for first DR sync, cmp clk and DR clk--- | |
843 | fork { | |
844 | @(posedge clkgen_port.$dr_sync_en__l2clk); // wait for first dr sync pulse | |
845 | dbg.dispmon(this.dispScope, MON_INFO, "first dr sync pulse"); | |
846 | } | |
847 | { | |
848 | @(posedge clkgen_port.$l2clk); // wait for first cmp clk | |
849 | } | |
850 | { | |
851 | @(posedge clkgen_dr_port.$l2clk); // wait for first dr clk | |
852 | } join | |
853 | dbg.dispmon(this.dispScope, MON_INFO, "monitor: checking dr sync locations"); | |
854 | //----check sync location for DTM mode ----- | |
855 | if (this.clk_pkt.mode == CCU_DTM_MODE) { | |
856 | sync_loc_dev = this.clk_pkt.cmp_clk_per_nom; | |
857 | fork { | |
858 | while (1) { | |
859 | @(posedge clkgen_port.$dr_sync_en__l2clk); | |
860 | @(posedge clkgen_port.$l2clk); | |
861 | sync_time = get_time(LO); // at flop header | |
862 | } | |
863 | } join none | |
864 | @(posedge clkgen_dr_port.$l2clk); | |
865 | old_clk_edge = get_time(LO); | |
866 | for (i = 0; i < nchecks; i++) { | |
867 | @(posedge clkgen_dr_port.$l2clk); | |
868 | clk_edge = get_time(LO); | |
869 | clk_mid = (clk_edge + old_clk_edge) / 2; | |
870 | if (compute_abs(sync_time - clk_mid) > sync_loc_dev) { | |
871 | print_error_msg(err_cnt++, psprintf("sync location check: middle of DR l2clk: %0d, dr sync pulse at flop input: %0d <= more than %0d", clk_mid, sync_time, sync_loc_dev)); | |
872 | } | |
873 | else if (this.verbose) | |
874 | print_debug_msg(debug_cnt++, psprintf("sync location check: middle of DR l2clk: %0d, dr sync pulse at flop input: %0d", clk_mid, sync_time)); | |
875 | ||
876 | old_clk_edge = clk_edge; | |
877 | } | |
878 | } | |
879 | else { | |
880 | //---check DR sync locations--- | |
881 | for (i = 0; i < nchecks; i++) { | |
882 | @(posedge clkgen_port.$dr_sync_en__l2clk); // dr sync is high at clkgen output | |
883 | fork { check_next_dr_sync_loc(); } join none | |
884 | } | |
885 | } | |
886 | } | |
887 | ||
888 | //============================================================= | |
889 | // WHAT: check expected DR sync location at the flop input. DR sync | |
890 | // is flopped once after clkgen output. | |
891 | // HOW: the expected location of dr sync pulse is the cmp clk posedge | |
892 | // that is closet to the mid point of dr clk cycle (ie. delta). Since | |
893 | // the dr and cmp clk edges can shift a little, so testbench needs to | |
894 | // check the actual delta vs. (delta_min plus 1/3 of cmp clk cycle). | |
895 | //============================================================= | |
896 | task CLUSTER_hdr_chkr::check_next_dr_sync_loc() { | |
897 | integer prev_dr_posedge, next_dr_posedge, at_end_dr_cyc=0, dr_clk_midpoint; | |
898 | integer sync_at_clkgen, sync_at_flop; // sync pulse | |
899 | integer cmp_edges[40], n = 0, i; // high ratio is in DTM mode 15:1 ratio | |
900 | integer delta, min_delta, actual_delta; // time difference between cmp edge and dr clk midpoint | |
901 | integer clks_shift_limit; | |
902 | ||
903 | // when this task is called, DR sync is high at clkgen output | |
904 | at_end_dr_cyc = 0; | |
905 | n = 0; | |
906 | fork | |
907 | { // find next sync pulse (at flop input) | |
908 | @(posedge clkgen_port.$dr_sync_en__l2clk); // sync is high at clkgen output | |
909 | sync_at_clkgen = get_time(LO); | |
910 | @(posedge clkgen_port.$l2clk); // sync is high at flop input | |
911 | sync_at_flop = get_time(LO); | |
912 | } | |
913 | { // find next DR clk rising edges | |
914 | @(negedge clkgen_port.$l2clk); // when dr_sync is at phase0 (ie. DR clk edge), avoid this dr clk edge | |
915 | @(posedge clkgen_dr_port.$l2clk); | |
916 | prev_dr_posedge = get_time(LO); | |
917 | @(posedge clkgen_dr_port.$l2clk); | |
918 | next_dr_posedge = get_time(LO); | |
919 | at_end_dr_cyc = 1; | |
920 | } | |
921 | { // record timestamps of all cmp edges | |
922 | while (1) { | |
923 | @(posedge clkgen_port.$l2clk); | |
924 | if (n >= 40) { | |
925 | dbg.dispmon(this.dispScope, MON_ERR, psprintf("CLUSTER_hdrchkr::check_next_dr_sync_loc(n=%0d) <= programming error", n)); | |
926 | break; | |
927 | } | |
928 | cmp_edges[n] = get_time(LO); | |
929 | n++; | |
930 | if (at_end_dr_cyc) break; // reach the end of DR clk cycle | |
931 | } | |
932 | } join | |
933 | //---find cmp posedge that is nearest to the DR clk mid point | |
934 | dr_clk_midpoint = (prev_dr_posedge + next_dr_posedge) / 2; // rounding error is 1 tick | |
935 | min_delta = compute_abs(cmp_edges[0] - dr_clk_midpoint); | |
936 | for (i = 1; i < n ; i++) { | |
937 | delta = compute_abs(cmp_edges[i] - dr_clk_midpoint); | |
938 | if (delta < min_delta) // closer to the mid point | |
939 | min_delta = delta; | |
940 | } | |
941 | //---check location of DR sync pulse--- | |
942 | actual_delta = compute_abs(sync_at_flop - dr_clk_midpoint); | |
943 | clks_shift_limit = this.clk_pkt.cmp_clk_per_nom / 3; // 1/3 of nominal value of cmp clk cycle | |
944 | if (actual_delta > (min_delta + clks_shift_limit)) { | |
945 | this.dr_sync_loc_err_cnt++; | |
946 | if (this.dr_sync_loc_err_cnt <= this.max_error_printed) | |
947 | dbg.dispmon(this.dispScope, MON_ERR, psprintf( | |
948 | "sync location check: dr sync @flop=%0d. dr clk: edges=%0d and %0d, midpoint=%0d. delta2midpoint=%0d <= exceed min_delta=%0d + %0d", | |
949 | sync_at_flop, prev_dr_posedge, next_dr_posedge, dr_clk_midpoint, actual_delta, min_delta, clks_shift_limit)); | |
950 | } | |
951 | else if (this.dr_sync_loc_debug_cnt <= this.max_error_printed) { | |
952 | dbg.dispmon(this.dispScope, MON_INFO, psprintf( | |
953 | "sync location check: dr sync @flop=%0d. dr clk: edges=%0d and %0d, midpoint=%0d, delta2midpoint=%0d, min_delta=%0d, tolerance=%0d", | |
954 | sync_at_flop, prev_dr_posedge, next_dr_posedge, dr_clk_midpoint, actual_delta, min_delta, clks_shift_limit)); | |
955 | this.dr_sync_loc_debug_cnt++; | |
956 | } | |
957 | } | |
958 | ||
959 | //############################################################################ | |
960 | //############################################################################ | |
961 | //######### supporting subroutines ######################################### | |
962 | //############################################################################ | |
963 | //############################################################################ | |
964 | ||
965 | //============================================================= | |
966 | // WHAT: print error message | |
967 | // NOTE: | |
968 | // err_cnt is local error count of a particular error type. | |
969 | // this.error_cnt is global error count of this vera class. | |
970 | //============================================================= | |
971 | task CLUSTER_hdr_chkr::print_error_msg(integer err_cnt, string error_msg) { | |
972 | if (err_cnt <= max_error_printed) | |
973 | dbg.dispmon(this.dispScope, MON_ERR, error_msg); | |
974 | this.error_cnt++; // increment global error count | |
975 | } | |
976 | ||
977 | //============================================================= | |
978 | // WHAT: print messages for debug | |
979 | //============================================================= | |
980 | task CLUSTER_hdr_chkr::print_debug_msg(integer debug_cnt, string debug_msg) { | |
981 | if (debug_cnt <= max_debug_printed) | |
982 | dbg.dispmon(this.dispScope, MON_ALWAYS, debug_msg); | |
983 | } | |
984 | ||
985 | //============================================================= | |
986 | // Return 1 if value is outside min and max; otherwise, return 0. | |
987 | //============================================================= | |
988 | function integer CLUSTER_hdr_chkr::is_outside_min_max(integer min_val, integer value, integer max_val) { | |
989 | is_outside_min_max = ((value < min_val) || (value > max_val))? 1 : 0; | |
990 | } | |
991 | ||
992 | //============================================================= | |
993 | // WHAT: compute absolute value | |
994 | //============================================================= | |
995 | function integer CLUSTER_hdr_chkr::compute_abs(integer n) { | |
996 | compute_abs = (n >= 0)? n : n * -1; | |
997 | } | |
998 | ||
999 | //============================================================= | |
1000 | // WHAT: timeout if not seen posedge of 'sig_name' in 'timeout_val' gclk cycles. | |
1001 | // ARGs: sig_name must be "l2clk", "cmp_slow_sync_en", "slow_cmp_sync_en", | |
1002 | // "io2x_sync_en" or "dr_sync_en" | |
1003 | //============================================================= | |
1004 | task CLUSTER_hdr_chkr::wait_1st_posedge(string sig_name, integer timeout_val) { | |
1005 | integer got_1st_posedge=0; | |
1006 | ||
1007 | if (timeout_val <= 0) | |
1008 | return; | |
1009 | fork { | |
1010 | repeat (timeout_val) @(posedge clkgen_port.$gclk); | |
1011 | if (got_1st_posedge == 0) | |
1012 | dbg.dispmon(this.dispScope, MON_ERR, psprintf("not seen %s posedge in %0d gclks", sig_name, timeout_val)); | |
1013 | } join none | |
1014 | case (sig_name) { | |
1015 | "l2clk": @(posedge clkgen_port.$l2clk); | |
1016 | "cmp_slow_sync_en": @(posedge clkgen_port.$cmp_slow_sync_en__l2clk); | |
1017 | "slow_cmp_sync_en": @(posedge clkgen_port.$slow_cmp_sync_en__l2clk); | |
1018 | "io2x_sync_en": @(posedge clkgen_port.$io2x_sync_en__l2clk); | |
1019 | "dr_sync_en": @(posedge clkgen_port.$dr_sync_en__l2clk); | |
1020 | default: { | |
1021 | dbg.dispmon(this.dispScope, MON_ERR, psprintf("wait_1st_posedge(sig_name=%s) <= bad arg", sig_name)); | |
1022 | this.error_cnt++; | |
1023 | } | |
1024 | } | |
1025 | got_1st_posedge = 1; | |
1026 | terminate; | |
1027 | } | |
1028 |