feat: fixes and configuration to path

This commit is contained in:
Saleh Bubshait
2025-12-17 18:16:44 +00:00
parent a4a9644f9e
commit abfd851c59
4 changed files with 130 additions and 93 deletions

View File

@@ -3,6 +3,10 @@ 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.
@@ -62,10 +66,6 @@ def extract_primitives_section(lines):
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:
@@ -85,110 +85,140 @@ def parse_primitives_section(lines):
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.
Runs Vivado synthesis on the provided code.
"""
# Write the Verilog code to a temporary file.
# 1. 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.
# 2. Extract the module name
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.
# 3. Resolve Vivado Path (Linux Fix)
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")
# 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"
# Create the Vivado Tcl script.
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}
synth_design -top {top_module} -part xc7z020clg400-1
# Generate resource utilization report
report_utilization -file resource_usage.rpt
quit
exit
"""
with open(tcl_script, "w") as file:
file.write(tcl_commands)
# Run Vivado in batch mode using the generated Tcl script.
# 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_path, "-mode", "batch", "-source", tcl_script],
[vivado_bin, "-mode", "batch", "-source", tcl_script],
capture_output=True, text=True, check=True
)
except subprocess.CalledProcessError as e:
print("Synthesis failed:", e)
print(f"Synthesis CRASHED for {top_module}")
print("--- Error Log ---")
print(e.stderr) # Print the actual error from Vivado
print("-----------------")
return None
print(result.stdout)
# Check for the success message in the output.
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:
# 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}
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("Synthesis did not complete successfully.")
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():
# Load the original JSON.
input_json_file = "solutions.json" # Update this file name if needed.
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)
# 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
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 sol in module["solutions"]:
if sol.get("pass", "").strip().lower() == "true":
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"]
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
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:
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}")
# 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()