|
5 | 5 | from os.path import isfile |
6 | 6 | from re import sub |
7 | 7 | import argparse |
| 8 | +import fnmatch |
8 | 9 | import json |
9 | 10 | import operator |
10 | 11 | import os |
@@ -60,6 +61,13 @@ def gen_rule_file( |
60 | 61 | # } |
61 | 62 |
|
62 | 63 | rules_dict = { |
| 64 | + # all stages |
| 65 | + "*__flow__warnings__count:*": { |
| 66 | + "mode": "direct", |
| 67 | + "round_value": True, |
| 68 | + "compare": "<=", |
| 69 | + "level": "warning", |
| 70 | + }, |
63 | 71 | # synth |
64 | 72 | "synth__design__instance__area__stdcell": { |
65 | 73 | "mode": "padding", |
@@ -308,130 +316,145 @@ def gen_rule_file( |
308 | 316 |
|
309 | 317 | format_str = "| {:45} | {:8} | {:8} | {:8} |\n" |
310 | 318 | change_str = "" |
311 | | - for field, option in rules_dict.items(): |
312 | | - if field not in metrics.keys(): |
313 | | - print(f"[WARNING] Metric {field} not found") |
314 | | - continue |
315 | | - |
316 | | - if isinstance(metrics[field], str): |
317 | | - print(f"[WARNING] Skipping string field {field} = {metrics[field]}") |
318 | | - continue |
319 | | - |
320 | | - if len(period_list) != 1 and field == "globalroute__timing__clock__slack": |
321 | | - print("[WARNING] Skipping clock slack until multiple clocks support.") |
322 | | - continue |
323 | | - |
324 | | - rule_value = None |
325 | | - if option["mode"] == "direct": |
326 | | - rule_value = metrics[field] |
327 | | - |
328 | | - elif option["mode"] == "sum_fixed": |
329 | | - rule_value = metrics[field] + option["padding"] |
330 | | - |
331 | | - elif option["mode"] == "period": |
332 | | - rule_value = metrics[field] - period * option["padding"] / 100 |
333 | | - rule_value = min(rule_value, 0) |
334 | | - |
335 | | - elif option["mode"] == "padding": |
336 | | - rule_value = metrics[field] * (1 + option["padding"] / 100) |
337 | | - |
338 | | - elif option["mode"] == "period_padding": |
339 | | - negative_slack = min(metrics[field], 0) |
340 | | - rule_value = negative_slack - max( |
341 | | - negative_slack * option["padding"] / 100, |
342 | | - period * option["padding"] / 100, |
343 | | - ) |
344 | | - |
345 | | - elif option["mode"] == "abs_padding": |
346 | | - rule_value = abs(metrics[field]) * (1 + option["padding"] / 100) |
347 | 319 |
|
348 | | - elif option["mode"] == "metric": |
349 | | - rule_value = metrics[option["metric"]] * option["padding"] / 100 |
350 | | - |
351 | | - if ( |
352 | | - field == "cts__design__instance__count__setup_buffer" |
353 | | - or field == "cts__design__instance__count__hold_buffer" |
354 | | - ): |
355 | | - rule_value = max(rule_value, metrics[field] * 1.1) |
| 320 | + processed_fields = set() |
| 321 | + for pattern, option in rules_dict.items(): |
| 322 | + matching_fields = [] |
| 323 | + # Find all metric fields that match this pattern. |
| 324 | + if "*" in pattern: |
| 325 | + for metric_field in sorted(metrics.keys()): |
| 326 | + if ( |
| 327 | + fnmatch.fnmatch(metric_field, pattern) |
| 328 | + and metric_field not in processed_fields |
| 329 | + ): |
| 330 | + matching_fields.append(metric_field) |
| 331 | + elif pattern in metrics and pattern not in processed_fields: |
| 332 | + matching_fields.append(pattern) |
| 333 | + |
| 334 | + for field in matching_fields: |
| 335 | + processed_fields.add(field) |
| 336 | + if isinstance(metrics[field], str): |
| 337 | + print(f"[WARNING] Skipping string field {field} = {metrics[field]}") |
| 338 | + continue |
| 339 | + |
| 340 | + if len(period_list) != 1 and field == "globalroute__timing__clock__slack": |
| 341 | + print("[WARNING] Skipping clock slack until multiple clocks support.") |
| 342 | + continue |
| 343 | + |
| 344 | + rule_value = None |
| 345 | + if option["mode"] == "direct": |
| 346 | + rule_value = metrics[field] |
| 347 | + |
| 348 | + elif option["mode"] == "sum_fixed": |
| 349 | + rule_value = metrics[field] + option["padding"] |
| 350 | + |
| 351 | + elif option["mode"] == "period": |
| 352 | + rule_value = metrics[field] - period * option["padding"] / 100 |
| 353 | + rule_value = min(rule_value, 0) |
| 354 | + |
| 355 | + elif option["mode"] == "padding": |
| 356 | + rule_value = metrics[field] * (1 + option["padding"] / 100) |
356 | 357 |
|
357 | | - if "min_max" in option.keys(): |
358 | | - if "min_max_direct" in option.keys(): |
359 | | - rule_value = option["min_max"](rule_value, option["min_max_direct"]) |
360 | | - elif "min_max_sum" in option.keys(): |
361 | | - rule_value = option["min_max"]( |
362 | | - rule_value + option["min_max_sum"], option["min_max_sum"] |
| 358 | + elif option["mode"] == "period_padding": |
| 359 | + negative_slack = min(metrics[field], 0) |
| 360 | + rule_value = negative_slack - max( |
| 361 | + negative_slack * option["padding"] / 100, |
| 362 | + period * option["padding"] / 100, |
363 | 363 | ) |
364 | | - elif "min_max_period" in option.keys(): |
365 | | - rule_value = option["min_max"]( |
366 | | - rule_value, -period * option["min_max_period"] / 100.0 |
367 | | - ) |
368 | | - else: |
369 | | - print( |
370 | | - f"[ERROR] Metric {field} has 'min_max' field but no " |
371 | | - "'min_max_direct', 'min_max_sum', or 'min_max_period' field." |
372 | | - ) |
373 | | - sys.exit(1) |
374 | 364 |
|
375 | | - if rule_value is None: |
376 | | - print(f"[ERROR] Metric {field} has invalid mode {option['mode']}.") |
377 | | - sys.exit(1) |
378 | | - |
379 | | - if option["round_value"] and not isinf(rule_value): |
380 | | - rule_value = int(round(rule_value)) |
381 | | - else: |
382 | | - rule_value = float(f"{rule_value:.3g}") |
383 | | - |
384 | | - preserve_old_rule = ( |
385 | | - True |
386 | | - if len(metrics_to_consider) > 0 and field not in metrics_to_consider |
387 | | - else False |
388 | | - ) |
389 | | - has_old_rule = OLD_RULES is not None and field in OLD_RULES.keys() |
| 365 | + elif option["mode"] == "abs_padding": |
| 366 | + rule_value = abs(metrics[field]) * (1 + option["padding"] / 100) |
390 | 367 |
|
391 | | - if has_old_rule and preserve_old_rule: |
392 | | - rule_value = OLD_RULES[field]["value"] |
| 368 | + elif option["mode"] == "metric": |
| 369 | + rule_value = metrics[option["metric"]] * option["padding"] / 100 |
393 | 370 |
|
394 | | - if has_old_rule and not preserve_old_rule: |
395 | | - old_rule = OLD_RULES[field] |
396 | | - if old_rule["compare"] != option["compare"]: |
397 | | - print("[WARNING] Compare operator changed since last update.") |
398 | | - |
399 | | - compare = ops[option["compare"]] |
400 | | - |
401 | | - if compare(rule_value, metrics[field]) and "padding" in option.keys(): |
402 | | - rule_value = metrics[field] * (1 + option["padding"] / 100) |
403 | | - if option["round_value"] and not isinf(rule_value): |
404 | | - rule_value = int(round(rule_value)) |
405 | | - else: |
406 | | - rule_value = float(f"{rule_value:.3g}") |
407 | | - |
408 | | - need_to_update = False |
409 | 371 | if ( |
410 | | - tighten |
411 | | - and rule_value != old_rule["value"] |
412 | | - and compare(rule_value, old_rule["value"]) |
| 372 | + field == "cts__design__instance__count__setup_buffer" |
| 373 | + or field == "cts__design__instance__count__hold_buffer" |
413 | 374 | ): |
414 | | - need_to_update = True |
415 | | - change_str += format_str.format( |
416 | | - field, old_rule["value"], rule_value, "Tighten" |
417 | | - ) |
418 | | - |
419 | | - if failing and not compare(metrics[field], old_rule["value"]): |
420 | | - need_to_update = True |
421 | | - change_str += format_str.format( |
422 | | - field, old_rule["value"], rule_value, "Failing" |
423 | | - ) |
424 | | - |
425 | | - if update and old_rule["value"] != rule_value: |
426 | | - need_to_update = True |
427 | | - change_str += format_str.format( |
428 | | - field, old_rule["value"], rule_value, "Updating" |
429 | | - ) |
| 375 | + rule_value = max(rule_value, metrics[field] * 1.1) |
| 376 | + |
| 377 | + if "min_max" in option.keys(): |
| 378 | + if "min_max_direct" in option.keys(): |
| 379 | + rule_value = option["min_max"](rule_value, option["min_max_direct"]) |
| 380 | + elif "min_max_sum" in option.keys(): |
| 381 | + rule_value = option["min_max"]( |
| 382 | + rule_value + option["min_max_sum"], option["min_max_sum"] |
| 383 | + ) |
| 384 | + elif "min_max_period" in option.keys(): |
| 385 | + rule_value = option["min_max"]( |
| 386 | + rule_value, -period * option["min_max_period"] / 100.0 |
| 387 | + ) |
| 388 | + else: |
| 389 | + print( |
| 390 | + f"[ERROR] Metric {field} has 'min_max' field but no " |
| 391 | + "'min_max_direct', 'min_max_sum', or 'min_max_period' field." |
| 392 | + ) |
| 393 | + sys.exit(1) |
| 394 | + |
| 395 | + if rule_value is None: |
| 396 | + print(f"[ERROR] Metric {field} has invalid mode {option['mode']}.") |
| 397 | + sys.exit(1) |
430 | 398 |
|
431 | | - if not need_to_update: |
432 | | - rule_value = old_rule["value"] |
| 399 | + if option["round_value"] and not isinf(rule_value): |
| 400 | + rule_value = int(round(rule_value)) |
| 401 | + else: |
| 402 | + rule_value = float(f"{rule_value:.3g}") |
433 | 403 |
|
434 | | - rules[field] = dict(value=rule_value, compare=option["compare"]) |
| 404 | + preserve_old_rule = ( |
| 405 | + True |
| 406 | + if len(metrics_to_consider) > 0 and field not in metrics_to_consider |
| 407 | + else False |
| 408 | + ) |
| 409 | + has_old_rule = OLD_RULES is not None and field in OLD_RULES.keys() |
| 410 | + |
| 411 | + if has_old_rule and preserve_old_rule: |
| 412 | + rule_value = OLD_RULES[field]["value"] |
| 413 | + |
| 414 | + if has_old_rule and not preserve_old_rule: |
| 415 | + old_rule = OLD_RULES[field] |
| 416 | + if old_rule["compare"] != option["compare"]: |
| 417 | + print("[WARNING] Compare operator changed since last update.") |
| 418 | + |
| 419 | + compare = ops[option["compare"]] |
| 420 | + |
| 421 | + if compare(rule_value, metrics[field]) and "padding" in option.keys(): |
| 422 | + rule_value = metrics[field] * (1 + option["padding"] / 100) |
| 423 | + if option["round_value"] and not isinf(rule_value): |
| 424 | + rule_value = int(round(rule_value)) |
| 425 | + else: |
| 426 | + rule_value = float(f"{rule_value:.3g}") |
| 427 | + |
| 428 | + need_to_update = False |
| 429 | + if ( |
| 430 | + tighten |
| 431 | + and rule_value != old_rule["value"] |
| 432 | + and compare(rule_value, old_rule["value"]) |
| 433 | + ): |
| 434 | + need_to_update = True |
| 435 | + change_str += format_str.format( |
| 436 | + field, old_rule["value"], rule_value, "Tighten" |
| 437 | + ) |
| 438 | + |
| 439 | + if failing and not compare(metrics[field], old_rule["value"]): |
| 440 | + need_to_update = True |
| 441 | + change_str += format_str.format( |
| 442 | + field, old_rule["value"], rule_value, "Failing" |
| 443 | + ) |
| 444 | + |
| 445 | + if update and old_rule["value"] != rule_value: |
| 446 | + need_to_update = True |
| 447 | + change_str += format_str.format( |
| 448 | + field, old_rule["value"], rule_value, "Updating" |
| 449 | + ) |
| 450 | + |
| 451 | + if not need_to_update: |
| 452 | + rule_value = old_rule["value"] |
| 453 | + |
| 454 | + rule_entry = {"value": rule_value, "compare": option["compare"]} |
| 455 | + if "level" in option: |
| 456 | + rule_entry["level"] = option["level"] |
| 457 | + rules[field] = rule_entry |
435 | 458 |
|
436 | 459 | if len(change_str) > 0: |
437 | 460 | print(f"{os.path.normpath(rules_file)} updates:") |
|
0 commit comments