Skip to content

Commit 1ef2c85

Browse files
committed
[evaluation] Add script to extract key metrics from evaluation data into CSV
1 parent 50d7993 commit 1ef2c85

1 file changed

Lines changed: 124 additions & 0 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Dynamatic evaluation data extraction script
4+
5+
Extracts key metrics from evaluation data and writes it to a CSV file.
6+
"""
7+
8+
import argparse
9+
import csv
10+
import re
11+
import sys
12+
from pathlib import Path
13+
14+
15+
def parse_sim_report(path: Path):
16+
"""Return (cycle_count, passed) from a simulation report.txt."""
17+
text = path.read_text()
18+
passed = "C and VHDL outputs match" in text
19+
m = re.search(r"Simulation done!\s+Latency\s*=\s*(\d+)\s+cycles", text)
20+
cycle_count = int(m.group(1)) if m else None
21+
return cycle_count, passed
22+
23+
24+
def parse_utilization(path: Path):
25+
"""Return (slices, luts, ffs) from a utilization report."""
26+
slices = luts = ffs = None
27+
for line in path.read_text().splitlines():
28+
# Match table rows like "| Slice LUTs | 4029 | ..."
29+
# Use the most-specific patterns first.
30+
if m := re.match(r"\|\s*Slice LUTs\s*\|\s*(\d+)", line):
31+
luts = int(m.group(1))
32+
elif m := re.match(r"\|\s*Slice Registers\s*\|\s*(\d+)", line):
33+
ffs = int(m.group(1))
34+
elif m := re.match(r"\|\s*Slice\s*\|\s*(\d+)", line):
35+
slices = int(m.group(1))
36+
return slices, luts, ffs
37+
38+
39+
def parse_timing(path: Path):
40+
"""Return (cp_ns, slack_ns, cp_src, cp_dst) from a timing report."""
41+
cp_ns = slack_ns = cp_src = cp_dst = None
42+
for line in path.read_text().splitlines():
43+
if m := re.match(r"\s*Slack\s*\([^)]*\)\s*:\s*(-?[\d.]+)ns", line):
44+
slack_ns = float(m.group(1))
45+
elif m := re.match(r"\s*Data Path Delay:\s*([\d.]+)ns", line):
46+
cp_ns = float(m.group(1))
47+
elif m := re.match(r"\s*Source:\s*(\S+)", line):
48+
cp_src = m.group(1)
49+
elif m := re.match(r"\s*Destination:\s*(\S+)", line):
50+
cp_dst = m.group(1)
51+
return cp_ns, slack_ns, cp_src, cp_dst
52+
53+
54+
def main():
55+
parser = argparse.ArgumentParser(
56+
description="Extract evaluation metrics from run_evaluation.py output directory."
57+
)
58+
parser.add_argument("eval_dir", help="Path to the evaluation output directory")
59+
args = parser.parse_args()
60+
61+
eval_dir = Path(args.eval_dir)
62+
if not eval_dir.is_dir():
63+
print(f"Error: {eval_dir} is not a directory", file=sys.stderr)
64+
sys.exit(1)
65+
66+
columns = [
67+
"kernel",
68+
"cycle_count",
69+
"utilization_slice",
70+
"utilization_lut",
71+
"utilization_ff",
72+
"timing_cp_ns",
73+
"timing_slack_ns",
74+
"timing_cp_src",
75+
"timing_cp_dst",
76+
]
77+
78+
writer = csv.DictWriter(sys.stdout, fieldnames=columns)
79+
writer.writeheader()
80+
81+
for kernel_dir in sorted(eval_dir.iterdir()):
82+
if not kernel_dir.is_dir():
83+
continue
84+
kernel = kernel_dir.name
85+
86+
row: dict = {"kernel": kernel}
87+
88+
# Simulation report
89+
sim_report = kernel_dir / "out" / "sim" / "report.txt"
90+
if not sim_report.exists():
91+
print(f"Error: {sim_report} not found", file=sys.stderr)
92+
sys.exit(1)
93+
cycle_count, passed = parse_sim_report(sim_report)
94+
if not passed:
95+
print(f"Error: {kernel}: C and VHDL outputs do not match", file=sys.stderr)
96+
sys.exit(1)
97+
row["cycle_count"] = cycle_count
98+
99+
# Utilization report
100+
util_rpt = kernel_dir / "out" / "synth" / "utilization_post_pr.rpt"
101+
if not util_rpt.exists():
102+
print(f"Error: {util_rpt} not found", file=sys.stderr)
103+
sys.exit(1)
104+
slices, luts, ffs = parse_utilization(util_rpt)
105+
row["utilization_slice"] = slices
106+
row["utilization_lut"] = luts
107+
row["utilization_ff"] = ffs
108+
109+
# Timing report
110+
timing_rpt = kernel_dir / "out" / "synth" / "timing_post_pr.rpt"
111+
if not timing_rpt.exists():
112+
print(f"Error: {timing_rpt} not found", file=sys.stderr)
113+
sys.exit(1)
114+
cp_ns, slack_ns, cp_src, cp_dst = parse_timing(timing_rpt)
115+
row["timing_cp_ns"] = cp_ns
116+
row["timing_slack_ns"] = slack_ns
117+
row["timing_cp_src"] = cp_src
118+
row["timing_cp_dst"] = cp_dst
119+
120+
writer.writerow(row)
121+
122+
123+
if __name__ == "__main__":
124+
main()

0 commit comments

Comments
 (0)