first commit
This commit is contained in:
194
resource_usage.py
Normal file
194
resource_usage.py
Normal file
@@ -0,0 +1,194 @@
|
||||
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}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user