195 lines
7.3 KiB
Python
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}")
|
|
|
|
|