Files
ResBench/resource_usage.py
jultrishyyy ae1cc41f21 first commit
2025-02-20 20:38:50 +00:00

195 lines
7.3 KiB
Python

import json
import subprocess
import os
import re
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.
Returns a dictionary with keys: LUT, FF, DSP, BRAM, IO.
In this example:
- For LUT: sums up any primitive whose name starts with "LUT" (e.g., LUT2, LUT3, ...)
- For IO: sums the usage of IBUF and OBUF.
"""
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
# (Add additional processing for FF, DSP, BRAM if necessary.)
return resources
def run_synthesis(solution_code):
"""
Writes the given Verilog solution to a temporary file,
creates a Tcl script for Vivado to run synthesis and generate a utilization report,
runs Vivado in batch mode, and parses the resource usage report.
Returns a dictionary with keys "optimized" and "primitives" containing resource usage.
"""
# Write the Verilog code to a temporary file.
verilog_file = "temp.v"
with open(verilog_file, "w") as f:
f.write(solution_code)
# Extract the module name from the solution code.
top_module = extract_module_name(solution_code)
print(top_module)
if top_module is None:
print("Could not extract module name; using 'temp_top' as a default.")
top_module = "temp_top"
vivado_project = "temp_project"
tcl_script = "synthesis_script.tcl"
# Get the Vivado installation path from the environment variable.
vivado_path_env = os.environ.get("vivado")
if vivado_path_env is None:
print("Error: 'vivado' environment variable is not set.")
return None
vivado_path = os.path.join(vivado_path_env, "vivado.bat")
# 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}
# Generate resource utilization report
report_utilization -file resource_usage.rpt
quit
"""
with open(tcl_script, "w") as file:
file.write(tcl_commands)
# Run Vivado in batch mode using the generated Tcl script.
try:
result = subprocess.run(
[vivado_path, "-mode", "batch", "-source", tcl_script],
capture_output=True, text=True, check=True
)
except subprocess.CalledProcessError as e:
print("Synthesis failed:", e)
return None
print(result.stdout)
# Check for the success message in the output.
if "Finished Writing Synthesis Report" in result.stdout:
# Read the resource utilization report.
with open("resource_usage.rpt", "r") as f:
report_lines = f.readlines()
optimized_resources = parse_optimized(report_lines)
primitives_section = extract_primitives_section(report_lines)
primitives_resources = (parse_primitives_section(primitives_section)
if primitives_section else {})
return {"optimized": optimized_resources, "primitives": primitives_resources}
else:
print("Synthesis did not complete successfully.")
return None
def run_resource_usage():
# Load the original JSON.
input_json_file = "solutions.json" # Update this file name if needed.
with open(input_json_file, "r") as f:
data = json.load(f)
# Traverse all top-level keys (e.g., "4o") and all subcategories.
for top_key, top_value in data.items():
# print(top_value.keys())
# exit()
# top_value should be a dict with categories (e.g., "Combinational Logic", "Finite State Machines", etc.)
for category, module_list in top_value.items():
# if category == "Combinational Logic":
# continue
for module in module_list:
for sol in module["solutions"]:
if sol.get("pass", "").strip().lower() == "true":
solution_code = sol["solution"]
print(f"Running synthesis for module '{module['module']}' in category '{category}'")
resource_usage = run_synthesis(solution_code)
if resource_usage:
sol["resource usage"] = resource_usage
else:
sol["resource usage"] = {"optimized": {}, "primitives": {}}
else:
sol["resource usage"] = {"optimized": {}, "primitives": {}}
# Write the updated JSON (with resource usage added) to a new file.
output_json_file = "solutions.json"
with open(output_json_file, "w") as f:
json.dump(data, f, indent=4)
print(f"Updated JSON written to {output_json_file}")