Files
ResBench/resource_usage.py
2025-12-17 18:16:44 +00:00

224 lines
8.2 KiB
Python

import json
import subprocess
import os
import re
# --- CONFIGURATION ---
# If your environment variable isn't set, this default path will be used.
DEFAULT_VIVADO_PATH = "/tools/Xilinx/Vivado/2023.1/bin"
def extract_module_name(verilog_code):
"""
Extract the module name from the Verilog code.
Assumes the module declaration is of the form:
module <module_name> (
Returns the module name as a string, or None if not found.
"""
match = re.search(r'\bmodule\s+(\w+)', verilog_code)
if match:
return match.group(1)
return None
def parse_optimized(lines):
"""
Extract resource usage numbers from the main (optimized) report sections.
Returns a dictionary with keys: LUT, FF, DSP, BRAM, IO.
"""
optimized = {"LUT": None, "FF": None, "DSP": None, "BRAM": None, "IO": None}
for line in lines:
m = re.search(r'\|\s*Slice LUTs\*?\s*\|\s*(\d+)', line)
if m:
optimized["LUT"] = int(m.group(1))
m = re.search(r'\|\s*Slice Registers\s*\|\s*(\d+)', line)
if m:
optimized["FF"] = int(m.group(1))
m = re.search(r'\|\s*DSPs\s*\|\s*(\d+)', line)
if m:
optimized["DSP"] = int(m.group(1))
m = re.search(r'\|\s*Block RAM Tile\s*\|\s*(\d+)', line)
if m:
optimized["BRAM"] = int(m.group(1))
m = re.search(r'\|\s*Bonded IOB\s*\|\s*(\d+)', line)
if m:
optimized["IO"] = int(m.group(1))
return optimized
def extract_primitives_section(lines):
"""
Extracts all lines between the "7. Primitives" header and the "8. Black Boxes" header.
"""
start_marker = "7. Primitives"
end_marker = "8. Black Boxes"
start_idx = None
end_idx = None
for idx, line in enumerate(lines):
if start_idx is None and start_marker in line and (idx + 1 < len(lines) and "------" in lines[idx + 1]):
start_idx = idx
elif start_idx is not None and end_marker in line and (idx + 1 < len(lines) and "------" in lines[idx + 1]):
end_idx = idx
break
if start_idx is None or end_idx is None:
return []
return lines[start_idx:end_idx]
def parse_primitives_section(lines):
"""
Parses the primitives section lines to accumulate resource usage.
"""
resources = {"LUT": 0, "FF": 0, "DSP": 0, "BRAM": 0, "IO": 0}
for line in lines:
stripped_line = line.strip()
if not stripped_line.startswith("|"):
continue
parts = stripped_line.split("|")
if len(parts) < 4:
continue
ref_name = parts[1].strip()
used_str = parts[2].strip()
try:
used = int(used_str)
except ValueError:
continue
if ref_name.startswith("LUT"):
resources["LUT"] += used
if ref_name in ("IBUF", "OBUF"):
resources["IO"] += used
return resources
def run_synthesis(solution_code):
"""
Runs Vivado synthesis on the provided code.
"""
# 1. Write the Verilog code to a temporary file.
verilog_file = "temp.v"
with open(verilog_file, "w") as f:
f.write(solution_code)
# 2. Extract the module name
top_module = extract_module_name(solution_code)
if top_module is None:
print("Could not extract module name; using 'temp_top' as a default.")
top_module = "temp_top"
# 3. Resolve Vivado Path (Linux Fix)
vivado_path_env = os.environ.get("vivado")
# If env var is missing, try the default path, or search system path
if vivado_path_env:
vivado_bin = os.path.join(vivado_path_env, "vivado") # NO .bat
elif os.path.exists(os.path.join(DEFAULT_VIVADO_PATH, "vivado")):
vivado_bin = os.path.join(DEFAULT_VIVADO_PATH, "vivado")
else:
# Last resort: assume it's in the system PATH
vivado_bin = "vivado"
tcl_script = "synthesis_script.tcl"
vivado_project = "temp_project"
# 4. Create the Vivado Tcl script.
tcl_commands = f"""
create_project {vivado_project} -force -part xc7z020clg400-1
add_files {verilog_file}
set_property top {top_module} [current_fileset]
# Run synthesis only (no simulation)
synth_design -top {top_module} -part xc7z020clg400-1
# Generate resource utilization report
report_utilization -file resource_usage.rpt
exit
"""
with open(tcl_script, "w") as file:
file.write(tcl_commands)
# 5. Run Vivado
print(f"Starting Synthesis for {top_module} using {vivado_bin}...")
try:
# Added stdout=PIPE so we can see the output if it crashes
result = subprocess.run(
[vivado_bin, "-mode", "batch", "-source", tcl_script],
capture_output=True, text=True, check=True
)
except subprocess.CalledProcessError as e:
print(f"❌ Synthesis CRASHED for {top_module}")
print("--- Error Log ---")
print(e.stderr) # Print the actual error from Vivado
print("-----------------")
return None
except FileNotFoundError:
print(f"❌ CRITICAL ERROR: Could not find Vivado executable at: {vivado_bin}")
return None
# 6. Check results
if "Finished Writing Synthesis Report" in result.stdout:
print(f"✅ Synthesis Success: {top_module}")
if os.path.exists("resource_usage.rpt"):
with open("resource_usage.rpt", "r") as f:
report_lines = f.readlines()
optimized = parse_optimized(report_lines)
primitives_sec = extract_primitives_section(report_lines)
primitives = parse_primitives_section(primitives_sec) if primitives_sec else {}
return {"optimized": optimized, "primitives": primitives}
else:
print("⚠️ Report file missing despite success message.")
return None
else:
print(f"❌ Synthesis Failed (Logic Error) for {top_module}")
# Print the last 10 lines of the log to help debug
print("\n".join(result.stdout.splitlines()[-10:]))
return None
def run_resource_usage():
input_json_file = "solutions.json"
if not os.path.exists(input_json_file):
print(f"Error: {input_json_file} not found.")
return
with open(input_json_file, "r") as f:
data = json.load(f)
print(f"Loaded {input_json_file}. Scanning for passing solutions...")
found_any = False
for top_key, top_value in data.items(): # e.g. "gpt-4"
for category, module_list in top_value.items(): # e.g. "Combinational Logic"
for module in module_list:
for idx, sol in enumerate(module["solutions"]):
# DEBUG PRINT: Show us the status
status = sol.get("pass", "MISSING").strip().lower()
if status == "true":
found_any = True
print(f"🚀 MATCH: Synthesizing {module['module']} (Sol #{idx})...")
solution_code = sol["solution"]
resource_usage = run_synthesis(solution_code)
if resource_usage:
sol["resource usage"] = resource_usage
print(f" ✅ Result: {resource_usage['optimized']['LUT']} LUTs")
else:
sol["resource usage"] = {"optimized": {}, "primitives": {}}
print(f" ❌ Synthesis Failed internally.")
# Save immediately
with open("solutions.json", "w") as f:
json.dump(data, f, indent=4)
else:
# Tell the user we are skipping
print(f"⏭️ Skipping {module['module']} (Status: '{status}')")
if not found_any:
print("\n⚠️ WARNING: No passing solutions found! Synthesis only runs on code that passed simulation.")
print("To force a test, edit 'solutions.json' and change one 'pass' value to 'true'.")
else:
print("\n✅ All Resource Usage checks completed.")
if __name__ == "__main__":
run_resource_usage()