Initial commit of OpenSPARC T2 design and verification files.
[OpenSPARC-T2-DV] / verif / env / tcu / vera / classes / ccu_checker.vr
CommitLineData
86530b38
AT
1// ========== Copyright Header Begin ==========================================
2//
3// OpenSPARC T2 Processor File: ccu_checker.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 "ucb_top.vri"
38#include "ccu_top.vri"
39#include "ccu_clk_packet.vrh"
40#include "ccu_clks_states.vrh"
41
42class CCU_checker {
43 //--public vars to control this checker---
44 integer is_chk_iosync_loc; // 0: not check; otherwise, check
45 integer is_chk_io2xsync_loc; // 0: not check; otherwise, check
46 integer is_chk_drsync_loc; // 0: not check; otherwise, check
47 integer is_chk_syssync_loc; // 0: not check; otherwise, check
48
49 //---ports and classes--
50 local CCU_clk_port clk_port; // each signal is a Vera CLOCK
51 local CCU_mon_port mon_port; // Vera clock is cmp_pll_clk
52 local CCU_clks_internal_port ccu_clks_internal_port;
53 local UCB_port ucb_port; // UCB interface signals (iol2clk)
54 local StandardDisplay dbg; // Standard display for printing
55 local CCU_clks_states ccu_states; // keep track expected values
56
57 //---vars---
58 local string name; // name of this object
59 local string dispScope; // for standard display
60 local integer verbose; // 0: disable verbose mode; otherwise, enable
61 local integer error_cnt; // Error count. Init to 0. WARN: become negative if exeed max integer
62 local integer max_error_printed; // not print error msg if (error_cnt > max_error_printed)
63 local integer max_debug_printed; // not print debug msg if (debug_cnt > max_debug_printed)
64 local integer running; // 1: checker is running, 0: not running
65 local CCU_clk_packet clk_pkt; // expected values for clock parameters
66
67 //---expected locations of IO and IO2X sync pulses---
68 local integer cmp_io_sync_loc, io_cmp_sync_loc, io2x_sync_loc; // 0: core and IO/IO2X clks are aligned
69 local integer cmp_sys_sync_loc, sys_cmp_sync_loc; // 0: cmp_pll_clk and ccu_rst_sys_clk are aligned
70 local integer dr_sync_loc[4]; // locations of dr sync at CCU boundary
71 local integer total_stage_flops; // total staging flops from CCU output to flop input
72
73 //---event vars to control checkers---
74 local event stop_checker_e; // triggered by stop_checker() to terminate start_checker()
75 local event stop_checking_e; // triggered by stop_checking() to terminate start_public()
76
77 //---public tasks---
78 task new(string name="CCU_checker", StandardDisplay dbg, CCU_clks_states ccu_states, integer start_it=0);
79 task start_checker(); // start the ccu checker
80 task stop_checker(); // stop the ccu checker
81 task start_clk_stretch_checker(); // start clk stretch checker
82
83 //---local subroutines---
84 local task start_checking(); // start checking
85 local task stop_checking(); // stop checking
86 local task check_clk_toggle(string clk_name);
87 local task check_clk_freq_pw(string clk_name);
88 local task check_io_io2x_sync_cnt_pw(string sync_name);
89 local task check_io_io2x_sync_loc(string sync_name);
90 local task check_sys_sync_cnt_pw(string sync_name);
91 local task check_sys_sync_loc(string sync_name);
92 local task check_dr_sync_toggle();
93 local task check_dr_sync_pw();
94 local task check_dr_sync_loc();
95
96 //---local tasks for clk stretch checker---
97 local task clk_stretch_checker();
98 local function integer clk_stretch_chkr_1clk(string clkname, bit st_phase_hi, integer st_delay, integer max_pw_dev, integer max_cycs, integer st_start_cyc);
99
100 //---supporting subroutines---
101 local task print_error_msg(integer err_cnt, string error_msg);
102 local task print_debug_msg(integer debug_cnt, string debug_msg);
103 local function integer is_outside_min_max(integer min_val, integer value, integer max_val);
104 local task compute_expected_dr_sync_loc();
105 local task show_dr_sync_loc();
106
107 //---one-line tasks----
108 function integer abs(integer n) { abs = (n >= 0)? n : n * -1; } // compute absolute value
109}
110
111//################################################################
112//######### implementation of subroutines ###########
113//################################################################
114
115task CCU_checker::new(string name="ccu_checker", StandardDisplay dbg, CCU_clks_states ccu_states, integer start_it=0) {
116 integer i;
117
118 //---from arg list---
119 this.name = name;
120 this.dbg = dbg;
121 this.ccu_states = ccu_states;
122
123 //---control vars---
124 if (get_plus_arg(CHECK, "ccuChkr_chkIoIo2xDrSyncLoc")) {
125 this.is_chk_iosync_loc = 1;
126 this.is_chk_io2xsync_loc = 1;
127 this.is_chk_drsync_loc = 1;
128 }
129 else { //default is NOT checking sync locations at CCU
130 this.is_chk_iosync_loc = 0;
131 this.is_chk_io2xsync_loc = 0;
132 this.is_chk_drsync_loc = 0;
133 }
134 if (get_plus_arg(CHECK, "ccuChkr_notChkSysSyncLoc"))
135 this.is_chk_syssync_loc = 0;
136 else
137 this.is_chk_syssync_loc = 1; // default is checking locations of sys sync pulses
138
139 //---others---
140 this.clk_port = ccu_clk_bind;
141 this.mon_port = ccu_mon_bind;
142 this.ccu_clks_internal_port = ccu_clks_internal_bind;
143 this.ucb_port = ccu_ucb_mon_bind;
144 this.dispScope = this.name;
145 this.verbose = (get_plus_arg(CHECK, "ccuChkr_verbose"))? 1 : 0;
146 this.error_cnt = 0;
147 this.max_error_printed = 20;
148 this.max_debug_printed = 20;
149 this.running = 0;
150 this.clk_pkt = null; // illegal value
151 this.total_stage_flops = 8; // 5 flops in GCT, 2 flops in cluster hdr, 1 after cluster hdr
152 for (i = 0; i < 4; i++)
153 this.dr_sync_loc[i] = -1; // illegal value.
154
155 //---sync locations----
156 this.cmp_io_sync_loc = 3; // 3rd posedge of cmp_pll_clk after core and io clks aligned
157 this.io_cmp_sync_loc = 1; // 1st posedge of cmp_pll_clk after core and io clks aligned
158 this.io2x_sync_loc = 1; // 1st posedge of cmp_pll_clk after core and io2x clks aligned
159 this.cmp_sys_sync_loc = 1; // 1st posedge of cmp_pll_clk after cmp_pll_clk and ccu_rst_sys_clk aligned
160 this.sys_cmp_sync_loc = 1; // 1st posedge of cmp_pll_clk after cmp_pll_clk and ccu_rst_sys_clk aligned
161
162 //----override if runtime options specified---
163 if (get_plus_arg(CHECK, "ccuChkr_cmpiosync_loc="))
164 this.cmp_io_sync_loc = get_plus_arg(NUM, "ccuChkr_cmpiosync_loc=");
165 if (get_plus_arg(CHECK, "ccuChkr_iocmpsync_loc="))
166 this.io_cmp_sync_loc = get_plus_arg(NUM, "ccuChkr_iocmpsync_loc=");
167 if (get_plus_arg(CHECK, "ccuChkr_io2xsync_loc="))
168 this.io2x_sync_loc = get_plus_arg(NUM, "ccuChkr_io2xsync_loc=");
169 if (get_plus_arg(CHECK, "ccuChkr_maxError="))
170 this.max_error_printed = get_plus_arg(NUM, "ccuChkr_maxError=");
171
172 //---start background threads ---
173 if (start_it)
174 start_checker();
175}
176
177//=====================================================================
178// WHAT: start CCU checker
179//=====================================================================
180task CCU_checker::start_checker() {
181 reg bit_value;
182
183 if (running)
184 return; // already running
185 dbg.dispmon(this.dispScope, MON_INFO, "starts ...");
186 this.running = 1; // checker is running
187 fork {
188 fork {
189 while (1) {
190 bit_value = clk_port.$rst_ccu_ async;
191 if (bit_value !== 1'b1)
192 @(posedge clk_port.$rst_ccu_); // PLL is stable, so start checking
193 delay(3); // avoid race condition with ccu_states.get_clk_pkt()
194 this.clk_pkt = ccu_states.get_exp_clk_pkt();
195 compute_expected_dr_sync_loc();
196 ccu_states.show_clk_pkt(this.clk_pkt);
197 this.show_dr_sync_loc();
198 start_checking();
199 @(negedge clk_port.$rst_ccu_pll_); // Reset PLL, so stop checking
200 stop_checking();
201 }
202 } join none
203 sync(ALL, this.stop_checker_e); // stop() triggers this event
204 terminate;
205 dbg.dispmon(this.dispScope, MON_INFO, "stopped");
206 this.running = 0; // checker is not running
207 } join none
208}
209
210//=====================================================================
211// WHAT: stop CCU checker (ie. terminate start_checker())
212//=====================================================================
213task CCU_checker::stop_checker() {
214 if (this.running)
215 trigger(this.stop_checker_e); // this event terminates start_checker()
216}
217
218//=============================================================
219// Start to check
220//=============================================================
221task CCU_checker::start_checking() {
222
223 dbg.dispmon(this.dispScope, MON_INFO, "start checking ...");
224 fork {
225 fork
226 //-----cmp/dr/io/io2x clocks: check toggle, freq, pw---
227 {
228 check_clk_toggle("cmp_clk");
229 }
230 {
231 check_clk_freq_pw("cmp_clk");
232 }
233 {
234 @(posedge clk_port.$ccu_rst_sync_stable);
235 check_clk_toggle("dr_clk");
236 }
237 {
238 @(posedge clk_port.$ccu_rst_sync_stable);
239 check_clk_freq_pw("dr_clk");
240 }
241 {
242 @(posedge clk_port.$ccu_rst_sync_stable);
243 check_clk_toggle("io_out");
244 }
245 {
246 @(posedge clk_port.$ccu_rst_sync_stable);
247 check_clk_freq_pw("io_out");
248 }
249 {
250 @(posedge clk_port.$ccu_rst_sync_stable);
251 check_clk_toggle("io2x_out");
252 }
253 {
254 @(posedge clk_port.$ccu_rst_sync_stable);
255 check_clk_freq_pw("io2x_out");
256 }
257 //------ check IO sync and IO2X sync pulses --------
258 {
259 @(posedge clk_port.$ccu_rst_sync_stable);
260 check_io_io2x_sync_cnt_pw("cmp_io_sync_en");
261 }
262 {
263 @(posedge clk_port.$ccu_rst_sync_stable);
264 check_io_io2x_sync_cnt_pw("io_cmp_sync_en");
265 }
266 {
267 @(posedge clk_port.$ccu_rst_sync_stable);
268 check_io_io2x_sync_cnt_pw("io2x_sync_en");
269 }
270 {
271 if (this.is_chk_iosync_loc) {
272 @(posedge clk_port.$ccu_rst_sync_stable);
273 check_io_io2x_sync_loc("cmp_io_sync_en");
274 }
275 else
276 dbg.dispmon(this.dispScope, MON_ALWAYS, "warning: not checking cmp_io_sync_en locations");
277 }
278 {
279 if (this.is_chk_iosync_loc) {
280 @(posedge clk_port.$ccu_rst_sync_stable);
281 check_io_io2x_sync_loc("io_cmp_sync_en");
282 }
283 else
284 dbg.dispmon(this.dispScope, MON_ALWAYS, "warning: not checking io_cmp_sync_en locations");
285 }
286 {
287 if (this.is_chk_io2xsync_loc) {
288 @(posedge clk_port.$ccu_rst_sync_stable);
289 check_io_io2x_sync_loc("io2x_sync_en");
290 }
291 else
292 dbg.dispmon(this.dispScope, MON_ALWAYS, "warning: not checking io2x_sync_en locations");
293 }
294 //---- check SYS sync pulses ----
295 {
296 @(posedge clk_port.$ccu_rst_sync_stable);
297 check_sys_sync_cnt_pw("cmp_sys_sync_en");
298 }
299 {
300 @(posedge clk_port.$ccu_rst_sync_stable);
301 check_sys_sync_cnt_pw("sys_cmp_sync_en");
302 }
303 {
304 if (this.is_chk_syssync_loc) {
305 @(posedge clk_port.$ccu_rst_sync_stable);
306 check_sys_sync_loc("cmp_sys_sync_en");
307 }
308 else
309 dbg.dispmon(this.dispScope, MON_ALWAYS, "warning: not checking cmp_sys_sync_en locations");
310 }
311 {
312 if (this.is_chk_syssync_loc) {
313 @(posedge clk_port.$ccu_rst_sync_stable);
314 check_sys_sync_loc("sys_cmp_sync_en");
315 }
316 else
317 dbg.dispmon(this.dispScope, MON_ALWAYS, "warning: not checking sys_cmp_sync_en locations");
318 }
319 //----- check DR sync pulses -----
320 {
321 @(posedge clk_port.$ccu_rst_sync_stable);
322 check_dr_sync_toggle();
323 }
324 {
325 @(posedge clk_port.$ccu_rst_sync_stable);
326 check_dr_sync_pw();
327 }
328 {
329 if (this.is_chk_drsync_loc) {
330 @(posedge clk_port.$ccu_rst_sync_stable);
331 check_dr_sync_loc();
332 }
333 else
334 dbg.dispmon(this.dispScope, MON_ALWAYS, "warning: not checking dr_sync_en locations");
335 }
336 join none
337 sync(ALL, this.stop_checking_e); // stop_checking() triggers this event
338 terminate;
339 dbg.dispmon(this.dispScope, MON_INFO, "checking stopped");
340 } join none
341}
342
343//=============================================================
344//
345//=============================================================
346task CCU_checker::stop_checking() {
347 trigger(this.stop_checking_e); // this event terminates start_checking()
348}
349
350//=============================================================
351// WHAT: Check the specified clock for toggling.
352// ARGs: clk_name: "cmp_clk", "dr_clk", "io_out" or "io2x_out"
353//=============================================================
354task CCU_checker::check_clk_toggle(string clk_name) {
355 string golden_clk; // golden clock to use for checking other clock
356 integer num_golden_clks;
357 integer old_cyc, new_cyc, ncycs, exp_min_ncycs, exp_max_ncycs;
358 integer err_cnt = 0, debug_cnt=0;
359
360 case (clk_name) {
361 "cmp_clk": {
362 golden_clk = "sys_clk";
363 num_golden_clks = 1;
364 exp_min_ncycs = this.clk_pkt.cmp_mult - 1; // '-1' : account sys and cmp clk edges are PERFECTLY aligned
365 exp_max_ncycs = this.clk_pkt.cmp_mult + 1; // '+1' : account sys and cmp clk edges are PERFECTLY aligned
366 @(posedge clk_port.$sys_clk);
367 old_cyc = get_cycle(clk_port.$cmp_pll_clk);
368 }
369 "dr_clk": {
370 golden_clk = "sys_clk";
371 num_golden_clks = 2;
372 exp_min_ncycs = (this.clk_pkt.dr_mult * 2) - 1; // '-1' : account sys and dr clk edges are PERFECTLY aligned
373 exp_max_ncycs = (this.clk_pkt.dr_mult * 2) + 1; // '+1' : account sys and dr clk edges are PERFECTLY aligned
374 @(posedge clk_port.$sys_clk); // in DTM mode, dr-to-sys clk ratio is 1:1, so use 2 sys clk cycles
375 old_cyc = get_cycle(clk_port.$dr_pll_clk);
376 }
377 "io_out": {
378 golden_clk = "cmp_pll_clk";
379 num_golden_clks = this.clk_pkt.cmp2io_ratio;
380 exp_min_ncycs = 1;
381 exp_max_ncycs = 1;
382 @(posedge clk_port.$cmp_pll_clk);
383 old_cyc = get_cycle(clk_port.$ccu_io_out);
384 }
385 "io2x_out": {
386 golden_clk = "cmp_pll_clk";
387 num_golden_clks = this.clk_pkt.cmp2io2x_ratio;
388 exp_min_ncycs = 1;
389 exp_max_ncycs = 1;
390 @(posedge clk_port.$cmp_pll_clk);
391 old_cyc = get_cycle(clk_port.$ccu_io2x_out);
392 }
393 default: {
394 dbg.dispmon(this.dispScope, MON_ERR, psprintf("CCU_checker::check_clk_toggle(%s) <= bad arg", clk_name));
395 this.error_cnt++;
396 return; // ignore
397 }
398 }
399 while (1) {
400 case (clk_name) {
401 "cmp_clk": {
402 repeat (num_golden_clks) @(posedge clk_port.$sys_clk);
403 new_cyc = get_cycle(clk_port.$cmp_pll_clk);
404 }
405 "dr_clk": {
406 repeat (num_golden_clks) @(posedge clk_port.$sys_clk);
407 new_cyc = get_cycle(clk_port.$dr_pll_clk);
408 }
409 "io_out": {
410 repeat (num_golden_clks) @(posedge clk_port.$cmp_pll_clk);
411 new_cyc = get_cycle(clk_port.$ccu_io_out);
412 }
413 "io2x_out": {
414 repeat (num_golden_clks) @(posedge clk_port.$cmp_pll_clk);
415 new_cyc = get_cycle(clk_port.$ccu_io2x_out);
416 }
417 }
418 ncycs = new_cyc - old_cyc;
419 if (is_outside_min_max(exp_min_ncycs, ncycs, exp_max_ncycs))
420 print_error_msg(err_cnt++, psprintf("number %s pulses in %0d %s is %0d. Expect: %0d to %0d",
421 clk_name, num_golden_clks, golden_clk, ncycs, exp_min_ncycs, exp_max_ncycs));
422 else if (this.verbose)
423 print_debug_msg(debug_cnt++, psprintf("number %s pulses in %0d %s is %0d. Expect: %0d to %0d",
424 clk_name, num_golden_clks, golden_clk, ncycs, exp_min_ncycs, exp_max_ncycs));
425 old_cyc = new_cyc;
426 }
427}
428
429//=============================================================
430// WHAT: Check freq and pulse width of the specified clk
431// ARGs: clk_name: "cmp_clk", "dr_clk", "io_out" or "io2x_out"
432// ASSUMPTION: checking that the specified clk toggles has been done.
433//=============================================================
434task CCU_checker::check_clk_freq_pw(string clk_name) {
435 integer pw_dev; // deviation in clk pw
436 integer per, per_min, per_max; // clk period
437 integer pw_hi, pw_lo, pw_nom, pw_min, pw_max; // pulse width
438 integer clk_posedge, clk_negedge;
439 integer nom_4_min, nom_4_max; // nominal for min and max to minimize Vera rounding effect
440 integer per_error_cnt=0, pw_error_cnt=0; // error in period or pulse width
441 integer debug_cnt = 0;
442
443 case (clk_name) {
444 "cmp_clk": {
445 per_min = this.clk_pkt.cmp_clk_per_min;
446 per_max = this.clk_pkt.cmp_clk_per_max;
447 pw_dev = this.clk_pkt.cmp_pw_dev;
448 @(posedge clk_port.$cmp_pll_clk);
449 }
450 "dr_clk": {
451 per_min = this.clk_pkt.dr_clk_per_min;
452 per_max = this.clk_pkt.dr_clk_per_max;
453 pw_dev = this.clk_pkt.dr_pw_dev;
454 @(posedge clk_port.$dr_pll_clk);
455 }
456 "io_out": {
457 per_min = this.clk_pkt.io_out_per_min;
458 per_max = this.clk_pkt.io_out_per_max;
459 pw_dev = this.clk_pkt.iox_pw_dev;
460 @(posedge clk_port.$ccu_io_out);
461 }
462 "io2x_out": {
463 per_min = this.clk_pkt.io2x_out_per_min;
464 per_max = this.clk_pkt.io2x_out_per_max;
465 pw_dev = this.clk_pkt.iox_pw_dev;
466 @(posedge clk_port.$ccu_io2x_out);
467 }
468 default: {
469 dbg.dispmon(this.dispScope, MON_ERR, psprintf("CCU_checker::check_clk_freq_pw(%s) <= bad arg", clk_name));
470 this.error_cnt++;
471 return; // ignore
472 }
473 }
474 clk_posedge = get_time(LO);
475 while (1) {
476 case (clk_name) {
477 "cmp_clk": @(negedge clk_port.$cmp_pll_clk);
478 "dr_clk": @(negedge clk_port.$dr_pll_clk);
479 "io_out": @(negedge clk_port.$ccu_io_out);
480 "io2x_out": @(negedge clk_port.$ccu_io2x_out);
481 }
482 clk_negedge = get_time(LO);
483 pw_hi = clk_negedge - clk_posedge;
484 case (clk_name) {
485 "cmp_clk": @(posedge clk_port.$cmp_pll_clk);
486 "dr_clk": @(posedge clk_port.$dr_pll_clk);
487 "io_out": @(posedge clk_port.$ccu_io_out);
488 "io2x_out": @(posedge clk_port.$ccu_io2x_out);
489 }
490 clk_posedge = get_time(LO);
491 pw_lo = clk_posedge - clk_negedge;
492 per = pw_hi + pw_lo;
493 //---check---
494 if (is_outside_min_max(per_min, per, per_max))
495 print_error_msg(per_error_cnt++, psprintf("%s period: %0d, expect: min=%0d, max=%0d", clk_name, per, per_min, per_max));
496 pw_nom = per / 2; // 50% duty cycle
497 nom_4_min = pw_nom - 1; // rounding down to account vera integer division
498 nom_4_max = pw_nom + 1; // rounding up to account vera integer division
499 pw_min = nom_4_min - (((per / 100) + 1) * pw_dev); // +1: account vera integer division
500 pw_max = nom_4_max + (((per / 100) + 1) * pw_dev); // +1: account vera integer division
501 if (is_outside_min_max(pw_min, pw_hi, pw_max) || is_outside_min_max(pw_min, pw_lo, pw_max))
502 print_error_msg(pw_error_cnt++, psprintf("%s pulse width: pw_hi=%0d, pw_lo=%0d, expect: min=%0d, max=%0d",
503 clk_name, pw_hi, pw_lo, pw_min, pw_max));
504 else if (this.verbose)
505 print_debug_msg(debug_cnt++, psprintf("%s per: %0d, expect: min=%0d, max=%0d. pw_hi=%0d, pw_lo=%0d. Expect: min=%0d, max=%0d",
506 clk_name, per, per_min, per_max, pw_hi, pw_lo, pw_min, pw_max));
507 }
508}
509
510//=============================================================
511// WHAT: check io_sync_en and io2x_sync_en
512// -Sync pulse width is one cmp clk.
513// -One sync pulse for each slow clock
514// ARGs: sync_name must be "cmp_io_sync_en", "io_cmp_sync_en" or "io2x_sync_en"
515//=============================================================
516task CCU_checker::check_io_io2x_sync_cnt_pw(string sync_name) {
517 integer cmp2slow_clk_ratio; // cmp-to-slow clk ratio
518 integer num_cycs_sync_low; // number of cmp clks when sync pulse is low
519 integer first_sync_timeout; // timeout if not seen 1st sync
520 integer got_1st_sync=0; // set to 1 when detect first sync pulse
521 integer err_lo_cnt=0, err_hi_cnt=0, debug_cnt=0; // error/debug counts of sync are low and high
522 bit sync_val; // value of sync_en signal
523 integer i;
524
525 case (sync_name) {
526 "io2x_sync_en": cmp2slow_clk_ratio = this.clk_pkt.cmp2io2x_ratio;
527 "cmp_io_sync_en", "io_cmp_sync_en": cmp2slow_clk_ratio = this.clk_pkt.cmp2io_ratio;
528 default: {
529 dbg.dispmon(this.dispScope, MON_ERR, psprintf("CCU_checker::check_io_io2x_sync_cnt_pw(%s) <= bad arg", sync_name));
530 this.error_cnt++;
531 return; // ignore
532 }
533 }
534 num_cycs_sync_low = cmp2slow_clk_ratio - 1;
535 first_sync_timeout = cmp2slow_clk_ratio + 3; // sync pulse should be stable when ccu_rst_sync_stable asserted
536 //---ensure 1st sync pulse occurs within time limit---
537 fork {
538 repeat (first_sync_timeout) @(posedge clk_port.$cmp_pll_clk);
539 if (got_1st_sync == 0)
540 dbg.dispmon(this.dispScope, MON_ERR, psprintf("not seen first %s in %0d cmp_pll_clk cycles", sync_name, first_sync_timeout));
541 } join none
542 //---wait for 1st sync pulse---
543 case (sync_name) {
544 "cmp_io_sync_en": @(posedge mon_port.$ccu_cmp_io_sync_en); // WARN: mon_port, not clk_port.
545 "io_cmp_sync_en": @(posedge mon_port.$ccu_io_cmp_sync_en); // WARN: mon_port, not clk_port.
546 "io2x_sync_en": @(posedge mon_port.$ccu_io2x_sync_en); // WARN: mon_port, not clk_port.
547 }
548 dbg.dispmon(this.dispScope, MON_INFO, psprintf("first %s sync pulse used as ref data point", sync_name));
549 got_1st_sync = 1;
550 //---check sync pulse ---
551 while (1) {
552 for (i = 0; i < num_cycs_sync_low; i++) {
553 @(posedge mon_port.$cmp_pll_clk); // WARN: mon_port
554 case (sync_name) {
555 "cmp_io_sync_en": sync_val = mon_port.$ccu_cmp_io_sync_en;
556 "io_cmp_sync_en": sync_val = mon_port.$ccu_io_cmp_sync_en;
557 "io2x_sync_en": sync_val = mon_port.$ccu_io2x_sync_en;
558 }
559 if (sync_val !== 1'b0)
560 print_error_msg(err_lo_cnt++, psprintf("%s should be low, but not", sync_name));
561 }
562 @(posedge mon_port.$cmp_pll_clk); // WARN: mon_port
563 case (sync_name) {
564 "cmp_io_sync_en": sync_val = mon_port.$ccu_cmp_io_sync_en;
565 "io_cmp_sync_en": sync_val = mon_port.$ccu_io_cmp_sync_en;
566 "io2x_sync_en": sync_val = mon_port.$ccu_io2x_sync_en;
567 }
568 if (sync_val !== 1'b1)
569 print_error_msg(err_hi_cnt++, psprintf("%s should be high, but not", sync_name));
570 else if (this.verbose)
571 print_debug_msg(debug_cnt++, psprintf("%s is %b. Expect: high", sync_name, sync_val));
572 }
573}
574
575//=============================================================
576// WHAT: check location of IO or IO2X sync pulse
577// ARGs: sync_name must be "cmp_io_sync_en", "io_cmp_sync_en" or "io2x_sync_en"
578// WARNING: since this checker, by default, does NOT check locations of io/io2x/dr
579// sync pulses, so this task is NOT maintained and may be out of date.
580//=============================================================
581task CCU_checker::check_io_io2x_sync_loc(string sync_name) {
582 integer num_core_clks;
583 bit sync_val;
584
585 //--- sanity check: this task is NOT maintained ---
586 dbg.dispmon(this.dispScope, MON_ERR, "ccu_checker::check_io_io2x_sync_loc() is NOT maintained. Do NOT used it");
587
588 //---go to posedge of io_out or io2x_out---
589 case (sync_name) {
590 "cmp_io_sync_en": {
591 num_core_clks = cmp_io_sync_loc;
592 @(posedge mon_port.$ccu_cmp_io_sync_en);
593 @(posedge clk_port.$ccu_io_out);
594 }
595 "io_cmp_sync_en": {
596 num_core_clks = io_cmp_sync_loc;
597 @(posedge mon_port.$ccu_io_cmp_sync_en);
598 @(posedge clk_port.$ccu_io_out);
599 }
600 "io2x_sync_en": {
601 num_core_clks = io2x_sync_loc;
602 @(posedge mon_port.$ccu_io2x_sync_en);
603 @(posedge clk_port.$ccu_io2x_out);
604 }
605 default: {
606 dbg.dispmon(this.dispScope, MON_ERR, psprintf("CCU_checker::check_io_io2x_sync_loc(sync_name=%s) <= bad arg", sync_name));
607 return; // ignore
608 }
609 }
610 //---find cmp clk edge where it's aligned with io/io2x_out clk---
611 @(negedge clk_port.$cmp_pll_clk);
612 repeat (num_core_clks) @(posedge clk_port.$cmp_pll_clk);
613 //---check sync pulse location only once---
614 case (sync_name) {
615 "cmp_io_sync_en": sync_val = mon_port.$ccu_cmp_io_sync_en; // WARN: mon_port, not clk_port.
616 "io_cmp_sync_en": sync_val = mon_port.$ccu_io_cmp_sync_en; // WARN: mon_port, not clk_port.
617 "io2x_sync_en": sync_val = mon_port.$ccu_io2x_sync_en; // WARN: mon_port, not clk_port.
618 }
619 if (sync_val !== 1'b1)
620 dbg.dispmon(this.dispScope, MON_ERR, psprintf("location of %s : expect high, but it is not", sync_name));
621}
622
623//=============================================================
624// WHAT: check cmp_sys_sync_en and sys_cmp_sync_en
625// -Sync pulse width is one cmp clk.
626// -One sync pulse for every ref_clk cycle
627// ARGs: sync_name must be "cmp_sys_sync_en" or "sys_cmp_sync_en"
628// NOTE: there is one sys sync pulse for every ref clk cycle. The number
629// of cmp clk cycles in one ref clk cycles is equal to div2_effective.
630//=============================================================
631task CCU_checker::check_sys_sync_cnt_pw(string sync_name) {
632 integer div2_eff = this.clk_pkt.pll_ctl[CCU__PLL_CTL__PLL_DIV2__MSB : CCU__PLL_CTL__PLL_DIV2__POS] + 1;
633 integer first_sync_timeout; // timeout (in sys clk cycs) if not seen 1st sync
634 integer got_1st_sync=0; // set to 1 when detect first sync pulse
635 integer num_cycs_sync_low; // number of cmp clks when sync pulse is low
636 integer err_lo_cnt=0, err_hi_cnt=0, debug_cnt=0; // error/debug counts of sync are low and high
637 bit sync_val; // value of sync_en signal
638 integer i;
639
640 first_sync_timeout = 4 + 1;
641 num_cycs_sync_low = div2_eff - 1;
642 //---ensure 1st sync pulse occurs within time limit---
643 fork {
644 repeat (first_sync_timeout) @(posedge clk_port.$sys_clk);
645 if (got_1st_sync == 0)
646 dbg.dispmon(this.dispScope, MON_ERR, psprintf("not seen first %s in %0d sys clk cycles", sync_name, first_sync_timeout));
647 } join none
648 //---wait for 1st sync pulse---
649 case (sync_name) {
650 "cmp_sys_sync_en": @(posedge mon_port.$ccu_cmp_sys_sync_en);
651 "sys_cmp_sync_en": @(posedge mon_port.$ccu_sys_cmp_sync_en);
652 default: {
653 dbg.dispmon(this.dispScope, MON_ERR, psprintf("CCU_checker::check_sys_sync_cnt_pw(%s) <= bad arg", sync_name));
654 this.error_cnt++;
655 return; // ignore
656 }
657 }
658 dbg.dispmon(this.dispScope, MON_INFO, psprintf("first %s sync pulse used as ref data point", sync_name));
659 got_1st_sync = 1;
660 //---check---
661 while (1) {
662 for (i = 0; i < num_cycs_sync_low; i++) {
663 @(posedge mon_port.$cmp_pll_clk); // WARN: mon_port
664 case (sync_name) {
665 "cmp_sys_sync_en": sync_val = mon_port.$ccu_cmp_sys_sync_en;
666 "sys_cmp_sync_en": sync_val = mon_port.$ccu_sys_cmp_sync_en;
667 }
668 if (sync_val !== 1'b0)
669 print_error_msg(err_lo_cnt++, psprintf("%s should be low, but not", sync_name));
670 }
671 @(posedge mon_port.$cmp_pll_clk); // WARN: mon_port
672 case (sync_name) {
673 "cmp_sys_sync_en": sync_val = mon_port.$ccu_cmp_sys_sync_en;
674 "sys_cmp_sync_en": sync_val = mon_port.$ccu_sys_cmp_sync_en;
675 }
676 if (sync_val !== 1'b1)
677 print_error_msg(err_hi_cnt++, psprintf("%s should be high, but not", sync_name));
678 else if (this.verbose)
679 print_debug_msg(debug_cnt++, psprintf("%s is %b. Expect: high", sync_name, sync_val));
680 }
681}
682
683//=============================================================
684// WHAT: check location of sys sync sync pulses at ccu boundary and inside RST block.
685// ARGs: sync_name must be "cmp_sys_sync_en" or "sys_cmp_sync_en"
686// NOTE: locations of cmp_sys and sys_cmp sync pulses are at phase 0.
687// RST flops them twice with l2clk, so they should be at phase 2
688// inside RST block.
689//=============================================================
690task CCU_checker::check_sys_sync_loc(string sync_name) {
691 integer i, debug_cnt=0;
692 integer refclk_posedge, sync_is_high, cmp_clk_per;
693 integer max_clks_skew; // max skew allowed between ccu.ref_clk and ccu.cmp_pll_clk
694 bit rst_sync_val, rst_sync_nchecks; // for checking sys sync inside RST blk
695
696 @(posedge clk_port.$cmp_pll_clk);
697 cmp_clk_per = get_time(LO);
698 @(posedge clk_port.$cmp_pll_clk);
699 cmp_clk_per = get_time(LO) - cmp_clk_per;
700 max_clks_skew = cmp_clk_per / 8;
701
702 //--- check sys sync pulses inside the RST block ---
703 rst_sync_nchecks = 0;
704 fork {
705 while (1) {
706 case (sync_name) {
707 "cmp_sys_sync_en": @(posedge mon_port.$ccu_cmp_sys_sync_en); // sys sync is high at CCU boundary
708 "sys_cmp_sync_en": @(posedge mon_port.$ccu_sys_cmp_sync_en); // sys sync is high at CCU boundary
709 }
710 @(negedge clk_port.$cmp_pll_clk); // avoid the skew between ccu.cmp_pll_clk and rst.l2clk
711 repeat (2) @(posedge ccu_clks_internal_port.$l2clk); // RST flops sys sync pulse twice
712 case (sync_name) {
713 "cmp_sys_sync_en": rst_sync_val = ccu_clks_internal_port.$rst_ccu_cmp_sys_sync_en3;
714 "sys_cmp_sync_en": rst_sync_val = ccu_clks_internal_port.$rst_ccu_sys_cmp_sync_en3;
715 }
716 if (rst_sync_val !== 1'b1)
717 dbg.dispmon(this.dispScope, MON_ERR, psprintf("check sys sync location: rst.rst_%s%s is %b, expect: high", sync_name, 3, rst_sync_val));
718 rst_sync_nchecks++;
719 if (rst_sync_nchecks > 5) break;
720 }
721 } join none
722 //---check sys sync location---
723 for (i = 0; i < 5; i++) { // check few times is enough since check_sys_sync_cnt_pw() checks sync pulse spacing/count.
724 //---check ccu_cmp_sys_en or ccu_sys_cmp_sync_en---
725 fork {
726 @(posedge clk_port.$ref_clk);
727 refclk_posedge = get_time(LO);
728 }
729 {
730 case (sync_name) {
731 "cmp_sys_sync_en": @(posedge mon_port.$ccu_cmp_sys_sync_en);
732 "sys_cmp_sync_en": @(posedge mon_port.$ccu_sys_cmp_sync_en);
733 }
734 sync_is_high = get_time(LO);
735 if (this.verbose)
736 print_debug_msg(debug_cnt++, psprintf("check sys sync location: %s is high", sync_name));
737 } join
738 if (abs(refclk_posedge - sync_is_high) > max_clks_skew)
739 dbg.dispmon(this.dispScope, MON_ERR, psprintf("check sys sync location: ref_clk_posedge=%0d, %s high at %0d <= not correct location",
740 refclk_posedge, sync_name, sync_is_high));
741 }
742}
743
744//=============================================================
745// WHAT: check dr_sync_en toggling
746//=============================================================
747task CCU_checker::check_dr_sync_toggle() {
748 integer old_cyc, new_cyc, temp_cyc, sync_cnts;
749 integer err_cnt=0;
750
751 @(posedge clk_port.$dr_pll_clk);
752 old_cyc = get_cycle(clk_port.$ccu_dr_sync_en);
753 while (1) {
754 @(posedge clk_port.$dr_pll_clk);
755 temp_cyc = get_cycle(clk_port.$ccu_dr_sync_en);
756 @(negedge clk_port.$cmp_pll_clk); // account the case of dr_pll_clk and cmp_pll_clk are PERFECTLY aligned
757 new_cyc = get_cycle(clk_port.$ccu_dr_sync_en);
758 sync_cnts = new_cyc - old_cyc;
759 if ((sync_cnts < 1) || (sync_cnts > 2))
760 print_error_msg(err_cnt++, psprintf("number of dr_sync pulses in 1 DR clk cyc + 0.5 cmp clk cycle: %0d. Expect: 1 or 2", sync_cnts));
761 old_cyc = temp_cyc;
762 }
763}
764
765//=============================================================
766// WHAT: check pulse width of dr_sync_en
767//=============================================================
768task CCU_checker::check_dr_sync_pw() {
769 bit sync_val;
770 integer err_cnt=0;
771
772 while (1) {
773 @(posedge mon_port.$ccu_dr_sync_en);
774 @(posedge mon_port.$cmp_pll_clk);
775 sync_val = mon_port.$ccu_dr_sync_en;
776 if (sync_val !== 1'b0)
777 print_error_msg(err_cnt++, psprintf("dr_sync_en is high for more than 1 cmp_clk cycles"));
778 }
779}
780
781//=============================================================
782// WHAT: check locations of dr_sync_en at CCU boundary.
783// This indirectly check DR sync pulse is one cmp clk.
784//=============================================================
785task CCU_checker::check_dr_sync_loc() {
786 integer div2_eff = this.clk_pkt.pll_ctl[CCU__PLL_CTL__PLL_DIV2__MSB : CCU__PLL_CTL__PLL_DIV2__POS] + 1;
787 integer phase_num; // phase number: 0, 1, .., div2_eff. 0 when clks are aligned
788 bit dr_sync_val, expected_dr_sync_val;
789 integer err_cnt=0, debug_cnt=0;
790
791 dbg.dispmon(this.dispScope, MON_ERR, "CCU_checker::check_dr_sync_loc() is NOT maintained. Do NOT used it");
792
793 //---find first phase---
794 @(posedge mon_port.$ccu_vco_aligned); // note: vco_aligned is generated 1 clk earlier
795 @(posedge clk_port.$cmp_pll_clk); // this is real VCO aligned
796 phase_num = 0; // first clock phase
797 //--- checking DR sync location---
798 while (1) {
799 dr_sync_val = mon_port.$ccu_dr_sync_en;
800 if ((phase_num == this.dr_sync_loc[0]) || (phase_num == this.dr_sync_loc[1])
801 || (phase_num == this.dr_sync_loc[2]) || (phase_num == this.dr_sync_loc[3]))
802 expected_dr_sync_val = 1'b1;
803 else
804 expected_dr_sync_val = 1'b0;
805 if (dr_sync_val != expected_dr_sync_val)
806 print_error_msg(err_cnt++, psprintf("ccu_dr_sync_en=%b, expect: %b", dr_sync_val, expected_dr_sync_val));
807 if (this.verbose)
808 print_debug_msg(debug_cnt++, psprintf("ccu_dr_sync_en=%b, expect: %b", dr_sync_val, expected_dr_sync_val));
809 @(posedge clk_port.$cmp_pll_clk); // this is real VCO aligned
810 phase_num++;
811 if (phase_num == div2_eff)
812 phase_num = 0;
813 }
814}
815
816//########################################################################
817//########################################################################
818//####### supporting subroutines #############
819//########################################################################
820//########################################################################
821
822//=============================================================
823// WHAT: print error message
824// NOTE:
825// err_cnt is local error count of a particular error type.
826// this.error_cnt is global error count of this vera class.
827//=============================================================
828task CCU_checker::print_error_msg(integer err_cnt, string error_msg) {
829 if (err_cnt <= this.max_error_printed)
830 dbg.dispmon(this.dispScope, MON_ERR, error_msg);
831 this.error_cnt++; // increment global error count
832}
833
834//=============================================================
835// WHAT: print messages for debug
836//=============================================================
837task CCU_checker::print_debug_msg(integer debug_cnt, string debug_msg) {
838 if (debug_cnt <= max_debug_printed)
839 dbg.dispmon(this.dispScope, MON_ALWAYS, debug_msg);
840}
841
842//=============================================================
843// Return 1 if value is outside min and max; otherwise, return 0.
844//=============================================================
845function integer CCU_checker::is_outside_min_max(integer min_val, integer value, integer max_val) {
846 is_outside_min_max = ((value < min_val) || (value > max_val))? 1 : 0;
847}
848
849//=============================================================
850// WHAT: compute expected locations of DR sync at CCU boundary
851//=============================================================
852task CCU_checker::compute_expected_dr_sync_loc() {
853 integer div2_effective = this.clk_pkt.pll_ctl[CCU__PLL_CTL__PLL_DIV2__MSB : CCU__PLL_CTL__PLL_DIV2__POS] + 1;
854 integer i;
855 for (i = 0; i < 4; i++) {
856 this.dr_sync_loc[i] = this.clk_pkt.dr_sync_loc[i] - this.total_stage_flops; // back track from flop header
857 if (this.dr_sync_loc[i] < 0) // cross ref_clk boundary to previous ref_clk cycle
858 this.dr_sync_loc[i] = this.dr_sync_loc[i] + div2_effective;
859 }
860}
861
862//=============================================================
863// WHAT: show expected DR sync locations at CCU boundary
864//=============================================================
865task CCU_checker::show_dr_sync_loc() {
866 dbg.dispmon(this.dispScope, MON_INFO, psprintf("Expected DR sync locations at CCU output: %0d, %0d, %0d and %0d",
867 this.dr_sync_loc[0], this.dr_sync_loc[1], this.dr_sync_loc[2], this.dr_sync_loc[3]));
868}
869
870//########################################################################
871//########################################################################
872//####### tasks for clk stretch monitor/checker #############
873//########################################################################
874//########################################################################
875
876//=============================================================
877// WHAT: clock stretch checker: public/wrapper task
878// USAGE: diag should call this task before TCU initiates clk stretch (ie. assert tcu_ccu_clk_stretch)
879//=============================================================
880task CCU_checker::start_clk_stretch_checker() {
881 bit sig;
882 sig = mon_port.$tcu_ccu_clk_stretch async;
883 if (sig === 1'b1) {
884 dbg.dispmon(this.dispScope, MON_ERR, "tcu_ccu_clk_stretch is 1. Call start_clk_stretch_checker() too late. Exit task");
885 return;
886 }
887 fork
888 clk_stretch_checker();
889 join none
890}
891
892//=============================================================
893// WHAT: clock strech checker: main task
894//=============================================================
895task CCU_checker::clk_stretch_checker() {
896 bit [63:0] pll_ctl; // effective value of pll_ctl
897 bit st_phase_hi;
898 integer st_delay_cmp, st_delay_dr; // must be 40, 80, 120 or 160ps
899 integer max_cycs = 12; // number of clk cycs to be checked/monitored
900 integer max_pw_dev = 10; // max deviation in pulse width. Assume clk ctrech diags only use nice pll_div, so no rounding error in clk per.
901 integer st_cmpclk_time, st_ioclk_time, st_io2xclk_time, st_drclk_time; // time when clk is stretched
902 integer cmp2iox_clk_skew_max = 2; // clk skew between cmp and io/io2x clk
903 integer cmp_st_cyc, dr_st_cyc; // cmp/dr cyc when tcu asserts tcu_ccu_clk_stretch
904
905 dbg.dispmon(this.dispScope, MON_INFO, "starts clk_stretch checker");
906 //--- wait for TCU initiates the clk stretch ---
907 @(posedge mon_port.$tcu_ccu_clk_stretch);
908 dbg.dispmon(this.dispScope, MON_INFO, "tcu asserts tcu_ccu_clk_stretch to initiate clk stretch");
909 cmp_st_cyc = get_cycle(this.clk_port.$cmp_pll_clk); // cmp cycle when TCU asserts clk stretch sig
910 dr_st_cyc = get_cycle(this.clk_port.$dr_pll_clk); // dr cycle when TCU asserts clk stretch sig
911 //--- compute expected values ---
912 pll_ctl = ccu_states.get_pll_ctl();
913 st_phase_hi = pll_ctl[CCU__PLL_CTL__ST_PHASE_HI__POS];
914 case (pll_ctl[CCU__PLL_CTL__ST_DELAY_CMP__MSB : CCU__PLL_CTL__ST_DELAY_CMP__POS]) {
915 2'b00: st_delay_cmp = 40; // in ps
916 2'b01: st_delay_cmp = 80;
917 2'b10: st_delay_cmp = 120;
918 2'b11: st_delay_cmp = 160;
919 }
920 case (pll_ctl[CCU__PLL_CTL__ST_DELAY_DR__MSB : CCU__PLL_CTL__ST_DELAY_DR__POS]) {
921 2'b00: st_delay_dr = 40; // in ps
922 2'b01: st_delay_dr = 80;
923 2'b10: st_delay_dr = 120;
924 2'b11: st_delay_dr = 160;
925 }
926 //--- check clk stretch for cmp/dr/io/io2x clk ---
927 fork
928 st_cmpclk_time = this.clk_stretch_chkr_1clk("cmp_clk", st_phase_hi, st_delay_cmp, max_pw_dev, max_cycs, cmp_st_cyc);
929 st_ioclk_time = this.clk_stretch_chkr_1clk("io_out", st_phase_hi, st_delay_cmp, max_pw_dev, max_cycs, cmp_st_cyc);
930 st_io2xclk_time = this.clk_stretch_chkr_1clk("io2x_out", st_phase_hi, st_delay_cmp, max_pw_dev, max_cycs,cmp_st_cyc);
931 st_drclk_time = this.clk_stretch_chkr_1clk("dr_clk", st_phase_hi, st_delay_dr, max_pw_dev, max_cycs, dr_st_cyc);
932 join
933 //--- check cmp/io/io2x clks stretched in the same cycle ---
934//if ((abs(st_cmpclk_time - st_ioclk_time) > cmp2iox_clk_skew_max) || (abs(st_cmpclk_time - st_io2xclk_time) > cmp2iox_clk_skew_max))
935// dbg.dispmon(this.dispScope, MON_ERR, psprintf("clk stretch at time: cmp_clk=%0d, io_out=%0d, io2x_out=%0d <= not at same cycle",
936// st_cmpclk_time, st_ioclk_time, st_io2xclk_time));
937}
938
939//=============================================================
940// WHAT: clock strech checker: supporting task checking one clock
941// ARGS: clkname: "cmp_clk", "io_out", "io2x_out" or "dr_clk"
942//=============================================================
943function integer CCU_checker::clk_stretch_chkr_1clk(string clkname, bit st_phase_hi, integer st_delay, integer max_pw_dev, integer max_cycs, integer st_start_cyc) {
944 integer i, st_cnt=0, st_time=0; // counting clk stretch, stretch time
945 integer pw_nom, pw_hi, pw_lo, pw_hi_min, pw_hi_max, pw_lo_min, pw_lo_max; // pw: pulse width
946 integer pw_hi_st_min, pw_hi_st_max, pw_lo_st_min, pw_lo_st_max; // pulse width limits for clk stretch
947 integer p_edge, n_edge, prev_p_edge; // clk posedge and negedge
948 CCU_clk_packet temp_clk_pkt;
949 integer st_cyc; // stretch cycle
950 integer st_pipe_del; // stretch pipeline delay
951
952 //--- monitor clk stretch pipeline latency ----
953 fork {
954 wait_var(st_cnt);
955 if ((clkname == "cmp_clk") || (clkname == "dr_clk")) {
956 case (clkname) {
957 "cmp_clk": st_cyc = get_cycle(clk_port.$cmp_pll_clk);
958 "dr_clk": st_cyc = get_cycle(clk_port.$dr_pll_clk);
959 }
960 st_pipe_del = st_cyc - st_start_cyc;
961 if ((st_pipe_del < 6) || (st_pipe_del > 8))
962 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: clk stretch pipeline delay %0d (min: 6: max: 8)", clkname, st_pipe_del));
963 else
964 dbg.dispmon(this.dispScope, MON_INFO, psprintf("%s: clk stretch pipeline delay %0d (min: 6: max: 8)", clkname, st_pipe_del));
965 }
966 } join none
967 //---compute expected pw ----
968 temp_clk_pkt = ccu_states.get_exp_clk_pkt(); // warn: need this since diag may not call this.start_checker(), so this.clk_pkt is NULL
969 case (clkname) {
970 "cmp_clk": pw_nom = temp_clk_pkt.cmp_clk_per_nom / 2;
971 "io_out": pw_nom = temp_clk_pkt.io_out_per_nom / 2;
972 "io2x_out": pw_nom = temp_clk_pkt.io2x_out_per_nom / 2;
973 "dr_clk": pw_nom = temp_clk_pkt.dr_clk_per_nom / 2;
974 }
975 pw_hi_min = pw_nom - max_pw_dev;
976 pw_hi_max = pw_nom + max_pw_dev;
977 pw_lo_min = pw_nom - max_pw_dev;
978 pw_lo_max = pw_nom + max_pw_dev;
979 pw_hi_st_min = pw_nom + st_delay - max_pw_dev;
980 pw_hi_st_max = pw_nom + st_delay + max_pw_dev;
981 pw_lo_st_min = pw_nom + st_delay - max_pw_dev;
982 pw_lo_st_max = pw_nom + st_delay + max_pw_dev;
983 //---check ---
984 case (clkname) {
985 "cmp_clk": @(posedge clk_port.$cmp_pll_clk);
986 "io_out": @(posedge clk_port.$ccu_io_out);
987 "io2x_out": @(posedge clk_port.$ccu_io2x_out);
988 "dr_clk": @(posedge clk_port.$dr_pll_clk);
989 }
990 p_edge = get_time(LO);
991 for (i = 0; i < max_cycs; i++) {
992 prev_p_edge = p_edge; // save previous posedge
993 case (clkname) {
994 "cmp_clk": @(negedge clk_port.$cmp_pll_clk);
995 "io_out": @(negedge clk_port.$ccu_io_out);
996 "io2x_out": @(negedge clk_port.$ccu_io2x_out);
997 "dr_clk": @(negedge clk_port.$dr_pll_clk);
998 }
999 n_edge = get_time(LO);
1000 pw_hi = n_edge - p_edge;
1001 case (clkname) {
1002 "cmp_clk": @(posedge clk_port.$cmp_pll_clk);
1003 "io_out": @(posedge clk_port.$ccu_io_out);
1004 "io2x_out": @(posedge clk_port.$ccu_io2x_out);
1005 "dr_clk": @(posedge clk_port.$dr_pll_clk);
1006 }
1007 p_edge = get_time(LO);
1008 pw_lo = p_edge - n_edge;
1009 if ((clkname == "io_out") || (clkname == "io2x_out")) { // can happen either hi or low phase
1010 if (pw_hi > pw_hi_max) { // assume this is clk stretch cycle
1011 st_cnt++;
1012 if (st_cnt == 1) {
1013 st_time = prev_p_edge;
1014 if (is_outside_min_max(pw_hi_st_min, pw_hi, pw_hi_st_max))
1015 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_hi=%0d (stretched), expect: min=%0d, max=%0d", clkname, pw_hi, pw_hi_st_min, pw_hi_st_max));
1016 else
1017 dbg.dispmon(this.dispScope, MON_INFO, psprintf("%s: pw_hi=%0d (stretched), expect: min=%0d, max=%0d", clkname, pw_hi, pw_hi_st_min, pw_hi_st_max));
1018 }
1019 else
1020 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_hi=%0d exceeds pw_hi_max=%0d, but clk stretch already happened at %0d", clkname, pw_hi, pw_hi_max, st_time));
1021 }
1022 else { // assume this is not clk stretch cycle
1023 if (is_outside_min_max(pw_hi_min, pw_hi, pw_hi_max))
1024 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_hi=%0d, expect: min=%0d, max=%0d", clkname, pw_hi, pw_hi_min, pw_hi_max));
1025 }
1026 if (pw_lo > pw_lo_max) { // assume this is clk stretch cycle
1027 st_cnt++;
1028 if (st_cnt == 1) {
1029 st_time = prev_p_edge;
1030 if (is_outside_min_max(pw_lo_st_min, pw_lo, pw_lo_st_max))
1031 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_lo=%0d (stretched), expect: min=%0d, max=%0d", clkname, pw_lo, pw_lo_st_min, pw_lo_st_max));
1032 else
1033 dbg.dispmon(this.dispScope, MON_INFO, psprintf("%s: pw_lo=%0d (stretched), expect: min=%0d, max=%0d", clkname, pw_lo, pw_lo_st_min, pw_lo_st_max));
1034 }
1035 else
1036 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_lo=%0d exceeds pw_lo_max=%0d, but clk stretch already happened at %0d", clkname, pw_lo, pw_lo_max, st_time));
1037 }
1038 else { // assume this is not clk stretch cycle
1039 if (is_outside_min_max(pw_lo_min, pw_lo, pw_lo_max))
1040 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_lo=%0d, expect: min=%0d, max=%0d", clkname, pw_lo, pw_lo_min, pw_lo_max));
1041 }
1042 }
1043 else { // cmp or dr clk
1044 if (st_phase_hi) {
1045 if (is_outside_min_max(pw_lo_min, pw_lo, pw_lo_max)) // stretch high phase, so pw_lo must not change
1046 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_lo=%0d, expect: min=%0d, max=%0d", clkname, pw_lo, pw_lo_min, pw_lo_max));
1047 if (pw_hi > pw_hi_max) { // assume this is clk stretch cycle
1048 st_cnt++;
1049 if (st_cnt == 1) {
1050 st_time = prev_p_edge;
1051 if (is_outside_min_max(pw_hi_st_min, pw_hi, pw_hi_st_max))
1052 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_hi=%0d (stretched), expect: min=%0d, max=%0d", clkname, pw_hi, pw_hi_st_min, pw_hi_st_max));
1053 else
1054 dbg.dispmon(this.dispScope, MON_INFO, psprintf("%s: pw_hi=%0d (stretched), expect: min=%0d, max=%0d", clkname, pw_hi, pw_hi_st_min, pw_hi_st_max));
1055 }
1056 else
1057 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_hi=%0d exceeds pw_hi_max=%0d, but clk stretch already happened at %0d", clkname, pw_hi, pw_hi_max, st_time));
1058 }
1059 else // assume this is not clk stretch cycle
1060 if (is_outside_min_max(pw_hi_min, pw_hi, pw_hi_max))
1061 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_hi=%0d, expect: min=%0d, max=%0d", clkname, pw_hi, pw_hi_min, pw_hi_max));
1062 }
1063 else {
1064 if (is_outside_min_max(pw_hi_min, pw_hi, pw_hi_max)) // stretch low phase, so pw_hi must not change
1065 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_hi=%0d, expect: min=%0d, max=%0d", clkname, pw_hi, pw_hi_min, pw_hi_max));
1066 if (pw_lo > pw_lo_max) { // assume this is clk stretch cycle
1067 st_cnt++;
1068 if (st_cnt == 1) {
1069 st_time = prev_p_edge;
1070 if (is_outside_min_max(pw_lo_st_min, pw_lo, pw_lo_st_max))
1071 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_lo=%0d (stretched), expect: min=%0d, max=%0d", clkname, pw_lo, pw_lo_st_min, pw_lo_st_max));
1072 else
1073 dbg.dispmon(this.dispScope, MON_INFO, psprintf("%s: pw_lo=%0d (stretched), expect: min=%0d, max=%0d", clkname, pw_lo, pw_lo_st_min, pw_lo_st_max));
1074 }
1075 else
1076 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_lo=%0d exceeds pw_lo_max=%0d, but clk stretch already happened at %0d", clkname, pw_lo, pw_lo_max, st_time));
1077 }
1078 else // assume this is not clk stretch cycle
1079 if (is_outside_min_max(pw_lo_min, pw_lo, pw_lo_max))
1080 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: pw_lo=%0d, expect: min=%0d, max=%0d", clkname, pw_lo, pw_lo_min, pw_lo_max));
1081 } // end of else
1082 } // end of else
1083 } // end of for()
1084 if (st_cnt <= 0)
1085 dbg.dispmon(this.dispScope, MON_ERR, psprintf("%s: clk stretch not happens in %0d cycles after tcu asserts clk_stretch", clkname, max_cycs));
1086 clk_stretch_chkr_1clk = st_time; // return value
1087}