Refactors Xilinx ISE tool execution and report handling
Replaces repetitive tool execution logic with a reusable `run_tool` function to streamline code and reduce duplication. Introduces a shared `copy_report_file` utility for consistent report copying and error handling. Simplifies individual tool scripts (XST, NGDBuild, MAP, PAR, BitGen) by delegating core logic to common utilities. Improves maintainability and consistency across the codebase.
This commit is contained in:
@@ -30,21 +30,15 @@ print(f"XST project file generated at {DIRECTORIES.build}/{project.name}.prj")
|
||||
print(f"XST script file generated at {DIRECTORIES.build}/{project.name}.scr")
|
||||
|
||||
run_xst(project)
|
||||
print("Run XST")
|
||||
|
||||
copy_synthesis_report(project)
|
||||
print("Copy synthesis report")
|
||||
|
||||
run_ngdbuild(project)
|
||||
print("Run ngdbuild")
|
||||
|
||||
run_map(project)
|
||||
print("Run map")
|
||||
copy_map_report(project)
|
||||
print("Copy map report")
|
||||
|
||||
run_par(project)
|
||||
print("Run par")
|
||||
copy_par_report(project)
|
||||
copy_pinout_report(project)
|
||||
|
||||
|
@@ -4,45 +4,20 @@ import shutil
|
||||
from typing import Optional
|
||||
from models.project import ProjectConfig
|
||||
from models.config import DIRECTORIES
|
||||
from tools.xilinx_ise.common import run_tool
|
||||
|
||||
def run_bitgen(project: ProjectConfig, working_dir: Optional[str] = None):
|
||||
"""
|
||||
Führt Xilinx BitGen aus, um den finalen Bitstream zu erzeugen.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Geladene Projektkonfiguration.
|
||||
working_dir (str, optional): Arbeitsverzeichnis; Standard: build-Verzeichnis.
|
||||
"""
|
||||
if working_dir is None:
|
||||
working_dir = DIRECTORIES.build
|
||||
def run_bitgen(project: ProjectConfig):
|
||||
run_tool(
|
||||
project=project,
|
||||
tool_executable_name="bitgen",
|
||||
tool_option_attr="bitgen",
|
||||
mandatory_arguments=[
|
||||
"-w",
|
||||
f"{project.name}.ncd",
|
||||
f"{project.name}.bit"
|
||||
]
|
||||
)
|
||||
|
||||
xilinx_bin_dir = os.path.join(project.xilinx_path, "bin", "lin64") # oder "nt64" für Windows
|
||||
bitgen_executable = os.path.join(xilinx_bin_dir, "bitgen")
|
||||
|
||||
if not os.path.exists(bitgen_executable):
|
||||
raise FileNotFoundError(f"BitGen-Executable nicht gefunden unter: {bitgen_executable}")
|
||||
|
||||
print(f"[hdlbuild] Starte BitGen über {bitgen_executable}")
|
||||
print(f"[hdlbuild] Arbeitsverzeichnis: {working_dir}")
|
||||
|
||||
cmd = [bitgen_executable]
|
||||
|
||||
# Füge zuerst die "common" Optionen ein (falls vorhanden)
|
||||
if project.tool_options and project.tool_options.common:
|
||||
cmd.extend(project.tool_options.common)
|
||||
|
||||
# Dann die BitGen-spezifischen Optionen
|
||||
if project.tool_options and project.tool_options.bitgen:
|
||||
cmd.extend(project.tool_options.bitgen)
|
||||
|
||||
# Dann die Pflicht-Argumente
|
||||
cmd.extend([
|
||||
"-w",
|
||||
f"{project.name}.ncd",
|
||||
f"{project.name}.bit"
|
||||
])
|
||||
|
||||
subprocess.run(cmd, cwd=working_dir, check=True)
|
||||
|
||||
def copy_bitstream_file(project: ProjectConfig):
|
||||
"""
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import shutil
|
||||
import subprocess
|
||||
import os
|
||||
from typing import Optional, List
|
||||
@@ -7,20 +8,20 @@ from models.config import DIRECTORIES
|
||||
def run_tool(
|
||||
project: ProjectConfig,
|
||||
tool_executable_name: str,
|
||||
tool_option_attr: str,
|
||||
mandatory_arguments: List[str],
|
||||
tool_option_attr: Optional[str] = None,
|
||||
working_dir: Optional[str] = None
|
||||
):
|
||||
"""
|
||||
Führt ein beliebiges Xilinx ISE Tool aus (XST, NGDBuild, MAP, PAR, BitGen),
|
||||
mit Common- und Toolspezifischen Optionen + festen Pflichtargumenten.
|
||||
mit Common- und ggf. Toolspezifischen Optionen + festen Pflichtargumenten.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Das Projekt-Objekt
|
||||
tool_executable_name (str): z.B. "xst", "map", "par", "bitgen"
|
||||
tool_option_attr (str): Attribut-Name in tool_options, z.B. "xst", "map"
|
||||
mandatory_arguments (List[str]): Liste mit Pflicht-Argumenten
|
||||
working_dir (str, optional): Arbeitsverzeichnis
|
||||
tool_option_attr (Optional[str]): Attribut-Name in tool_options, z.B. "xst", "map"
|
||||
working_dir (Optional[str]): Arbeitsverzeichnis
|
||||
"""
|
||||
if working_dir is None:
|
||||
working_dir = DIRECTORIES.build
|
||||
@@ -40,8 +41,8 @@ def run_tool(
|
||||
if project.tool_options and project.tool_options.common:
|
||||
cmd.extend(project.tool_options.common)
|
||||
|
||||
# Füge dann Toolspezifische Optionen ein
|
||||
if project.tool_options:
|
||||
# Füge dann Toolspezifische Optionen ein (nur wenn angegeben)
|
||||
if tool_option_attr and project.tool_options:
|
||||
tool_opts = getattr(project.tool_options, tool_option_attr, [])
|
||||
if tool_opts:
|
||||
cmd.extend(tool_opts)
|
||||
@@ -52,3 +53,29 @@ def run_tool(
|
||||
print(f"[hdlbuild] Befehl: {' '.join(cmd)}")
|
||||
|
||||
subprocess.run(cmd, cwd=working_dir, check=True)
|
||||
|
||||
def copy_report_file(
|
||||
project: ProjectConfig,
|
||||
source_filename: str,
|
||||
destination_filename: str,
|
||||
description: str = "Report"
|
||||
):
|
||||
"""
|
||||
Kopiert eine beliebige Report-Datei vom Build- in das Report-Verzeichnis.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Geladene Projektkonfiguration
|
||||
source_filename (str): Name der Quelldatei im Build-Ordner
|
||||
destination_filename (str): Neuer Name der Zieldatei im Report-Ordner
|
||||
description (str): Optionale Beschreibung für die Ausgabe (z.B. "Synthesis Report")
|
||||
"""
|
||||
src_path = os.path.join(DIRECTORIES.build, source_filename)
|
||||
dst_path = os.path.join(DIRECTORIES.report, destination_filename)
|
||||
|
||||
if not os.path.exists(src_path):
|
||||
raise FileNotFoundError(f"{description} nicht gefunden: {src_path}")
|
||||
|
||||
os.makedirs(DIRECTORIES.report, exist_ok=True)
|
||||
|
||||
shutil.copyfile(src_path, dst_path)
|
||||
print(f"[hdlbuild] {description} kopiert nach {dst_path}")
|
@@ -4,64 +4,26 @@ import shutil
|
||||
from typing import Optional
|
||||
from models.project import ProjectConfig
|
||||
from models.config import DIRECTORIES
|
||||
from tools.xilinx_ise.common import copy_report_file, run_tool
|
||||
|
||||
def run_map(project: ProjectConfig, working_dir: Optional[str] = None):
|
||||
"""
|
||||
Führt Xilinx MAP aus, basierend auf dem gegebenen Projekt.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Geladene Projektkonfiguration.
|
||||
working_dir (str, optional): Arbeitsverzeichnis; Standard: build-Verzeichnis.
|
||||
"""
|
||||
if working_dir is None:
|
||||
working_dir = DIRECTORIES.build
|
||||
|
||||
xilinx_bin_dir = os.path.join(project.xilinx_path, "bin", "lin64") # oder "nt64" für Windows
|
||||
map_executable = os.path.join(xilinx_bin_dir, "map")
|
||||
|
||||
if not os.path.exists(map_executable):
|
||||
raise FileNotFoundError(f"MAP-Executable nicht gefunden unter: {map_executable}")
|
||||
|
||||
print(f"[hdlbuild] Starte MAP über {map_executable}")
|
||||
print(f"[hdlbuild] Arbeitsverzeichnis: {working_dir}")
|
||||
|
||||
cmd = [map_executable]
|
||||
|
||||
# Füge zuerst die "common" Optionen ein (falls vorhanden)
|
||||
if project.tool_options and project.tool_options.common:
|
||||
cmd.extend(project.tool_options.common)
|
||||
|
||||
# Dann die MAP-spezifischen Optionen
|
||||
if project.tool_options and project.tool_options.map:
|
||||
cmd.extend(project.tool_options.map)
|
||||
|
||||
# Dann die Pflicht-Argumente
|
||||
cmd.extend([
|
||||
"-p", project.target_device,
|
||||
"-w",
|
||||
f"{project.name}.ngd",
|
||||
"-o", f"{project.name}.map.ncd",
|
||||
f"{project.name}.pcf"
|
||||
])
|
||||
|
||||
|
||||
subprocess.run(cmd, cwd=working_dir, check=True)
|
||||
def run_map(project: ProjectConfig):
|
||||
run_tool(
|
||||
project=project,
|
||||
tool_executable_name="map",
|
||||
tool_option_attr="map",
|
||||
mandatory_arguments=[
|
||||
"-p", project.target_device,
|
||||
"-w",
|
||||
f"{project.name}.ngd",
|
||||
"-o", f"{project.name}.map.ncd",
|
||||
f"{project.name}.pcf"
|
||||
]
|
||||
)
|
||||
|
||||
def copy_map_report(project: ProjectConfig):
|
||||
"""
|
||||
Kopiert den Map-Report (.map.mrp) vom Build-Verzeichnis ins Report-Verzeichnis
|
||||
und benennt ihn sinnvoll um.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Geladene Projektkonfiguration.
|
||||
"""
|
||||
src_path = os.path.join(DIRECTORIES.build, f"{project.name}.map.mrp")
|
||||
dst_path = os.path.join(DIRECTORIES.report, f"{project.name}.MapReport")
|
||||
|
||||
if not os.path.exists(src_path):
|
||||
raise FileNotFoundError(f"Map-Report nicht gefunden: {src_path}")
|
||||
|
||||
os.makedirs(DIRECTORIES.report, exist_ok=True)
|
||||
|
||||
shutil.copyfile(src_path, dst_path)
|
||||
print(f"[hdlbuild] Map-Report kopiert nach {dst_path}")
|
||||
copy_report_file(
|
||||
project=project,
|
||||
source_filename=f"{project.name}.map.mrp",
|
||||
destination_filename=f"{project.name}.MapReport",
|
||||
description="Map Report"
|
||||
)
|
||||
|
@@ -3,44 +3,17 @@ import os
|
||||
from typing import Optional
|
||||
from models.project import ProjectConfig
|
||||
from models.config import DIRECTORIES
|
||||
from tools.xilinx_ise.common import run_tool
|
||||
|
||||
def run_ngdbuild(project: ProjectConfig, working_dir: Optional[str] = None):
|
||||
"""
|
||||
Führt Xilinx NGDBuild aus, basierend auf dem gegebenen Projekt.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Geladene Projektkonfiguration.
|
||||
working_dir (str, optional): Arbeitsverzeichnis; Standard: build-Verzeichnis.
|
||||
"""
|
||||
if working_dir is None:
|
||||
working_dir = DIRECTORIES.build
|
||||
|
||||
xilinx_bin_dir = os.path.join(project.xilinx_path, "bin", "lin64") # oder "nt64" für Windows
|
||||
ngdbuild_executable = os.path.join(xilinx_bin_dir, "ngdbuild")
|
||||
|
||||
if not os.path.exists(ngdbuild_executable):
|
||||
raise FileNotFoundError(f"NGDBuild-Executable nicht gefunden unter: {ngdbuild_executable}")
|
||||
|
||||
print(f"[hdlbuild] Starte NGDBuild über {ngdbuild_executable}")
|
||||
print(f"[hdlbuild] Arbeitsverzeichnis: {working_dir}")
|
||||
|
||||
cmd = [ngdbuild_executable]
|
||||
|
||||
# Füge zuerst die "common" Optionen ein (falls vorhanden)
|
||||
if project.tool_options and project.tool_options.common:
|
||||
cmd.extend(project.tool_options.common)
|
||||
|
||||
# Dann die NGDBuild-spezifischen Optionen
|
||||
if project.tool_options and project.tool_options.ngdbuild:
|
||||
cmd.extend(project.tool_options.ngdbuild)
|
||||
|
||||
# Dann die Pflicht-Argumente
|
||||
cmd.extend([
|
||||
"-p", project.target_device,
|
||||
"-uc", f"{DIRECTORIES.get_relative_prefix()}{project.constraints}",
|
||||
f"{project.name}.ngc",
|
||||
f"{project.name}.ngd"
|
||||
])
|
||||
|
||||
|
||||
subprocess.run(cmd, cwd=working_dir, check=True)
|
||||
def run_ngdbuild(project: ProjectConfig):
|
||||
run_tool(
|
||||
project=project,
|
||||
tool_executable_name="ngdbuild",
|
||||
tool_option_attr="ngdbuild",
|
||||
mandatory_arguments=[
|
||||
"-p", project.target_device,
|
||||
"-uc", f"{DIRECTORIES.get_relative_prefix()}{project.constraints}",
|
||||
f"{project.name}.ngc",
|
||||
f"{project.name}.ngd"
|
||||
]
|
||||
)
|
||||
|
@@ -4,82 +4,33 @@ import os
|
||||
from typing import Optional
|
||||
from models.project import ProjectConfig
|
||||
from models.config import DIRECTORIES
|
||||
from tools.xilinx_ise.common import copy_report_file, run_tool
|
||||
|
||||
def run_par(project: ProjectConfig, working_dir: Optional[str] = None):
|
||||
"""
|
||||
Führt Xilinx PAR (Place & Route) aus, basierend auf dem gegebenen Projekt.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Geladene Projektkonfiguration.
|
||||
working_dir (str, optional): Arbeitsverzeichnis; Standard: build-Verzeichnis.
|
||||
"""
|
||||
if working_dir is None:
|
||||
working_dir = DIRECTORIES.build
|
||||
|
||||
xilinx_bin_dir = os.path.join(project.xilinx_path, "bin", "lin64") # oder "nt64" für Windows
|
||||
par_executable = os.path.join(xilinx_bin_dir, "par")
|
||||
|
||||
if not os.path.exists(par_executable):
|
||||
raise FileNotFoundError(f"PAR-Executable nicht gefunden unter: {par_executable}")
|
||||
|
||||
print(f"[hdlbuild] Starte PAR über {par_executable}")
|
||||
print(f"[hdlbuild] Arbeitsverzeichnis: {working_dir}")
|
||||
|
||||
cmd = [par_executable]
|
||||
|
||||
# Füge zuerst die "common" Optionen ein (falls vorhanden)
|
||||
if project.tool_options and project.tool_options.common:
|
||||
cmd.extend(project.tool_options.common)
|
||||
|
||||
# Dann die PAR-spezifischen Optionen
|
||||
if project.tool_options and project.tool_options.par:
|
||||
cmd.extend(project.tool_options.par)
|
||||
|
||||
# Dann die Pflicht-Argumente
|
||||
cmd.extend([
|
||||
"-w",
|
||||
f"{project.name}.map.ncd",
|
||||
f"{project.name}.ncd",
|
||||
f"{project.name}.pcf"
|
||||
])
|
||||
|
||||
|
||||
subprocess.run(cmd, cwd=working_dir, check=True)
|
||||
def run_par(project: ProjectConfig):
|
||||
run_tool(
|
||||
project=project,
|
||||
tool_executable_name="par",
|
||||
tool_option_attr="par",
|
||||
mandatory_arguments=[
|
||||
"-w",
|
||||
f"{project.name}.map.ncd",
|
||||
f"{project.name}.ncd",
|
||||
f"{project.name}.pcf"
|
||||
]
|
||||
)
|
||||
|
||||
def copy_par_report(project: ProjectConfig):
|
||||
"""
|
||||
Kopiert den Place & Route Report (.par) vom Build-Verzeichnis ins Report-Verzeichnis
|
||||
und benennt ihn sinnvoll um.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Geladene Projektkonfiguration.
|
||||
"""
|
||||
src_path = os.path.join(DIRECTORIES.build, f"{project.name}.par")
|
||||
dst_path = os.path.join(DIRECTORIES.report, f"{project.name}.PlaceRouteReport")
|
||||
|
||||
if not os.path.exists(src_path):
|
||||
raise FileNotFoundError(f"PAR-Report nicht gefunden: {src_path}")
|
||||
|
||||
os.makedirs(DIRECTORIES.report, exist_ok=True)
|
||||
|
||||
shutil.copyfile(src_path, dst_path)
|
||||
print(f"[hdlbuild] PAR-Report kopiert nach {dst_path}")
|
||||
copy_report_file(
|
||||
project=project,
|
||||
source_filename=f"{project.name}.par",
|
||||
destination_filename=f"{project.name}.PlaceRouteReport",
|
||||
description="Place & Route Report"
|
||||
)
|
||||
|
||||
def copy_pinout_report(project: ProjectConfig):
|
||||
"""
|
||||
Kopiert den Pinout Summary Report (_pad.txt) vom Build-Verzeichnis ins Report-Verzeichnis
|
||||
und benennt ihn sinnvoll um.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Geladene Projektkonfiguration.
|
||||
"""
|
||||
src_path = os.path.join(DIRECTORIES.build, f"{project.name}_pad.txt")
|
||||
dst_path = os.path.join(DIRECTORIES.report, f"{project.name}.PinoutReport")
|
||||
|
||||
if not os.path.exists(src_path):
|
||||
raise FileNotFoundError(f"Pinout-Report nicht gefunden: {src_path}")
|
||||
|
||||
os.makedirs(DIRECTORIES.report, exist_ok=True)
|
||||
|
||||
shutil.copyfile(src_path, dst_path)
|
||||
print(f"[hdlbuild] Pinout-Report kopiert nach {dst_path}")
|
||||
copy_report_file(
|
||||
project=project,
|
||||
source_filename=f"{project.name}_pad.txt",
|
||||
destination_filename=f"{project.name}.PinoutReport",
|
||||
description="Pinout Report"
|
||||
)
|
@@ -1,5 +1,6 @@
|
||||
from typing import Optional
|
||||
from models.config import DIRECTORIES
|
||||
from tools.xilinx_ise.common import copy_report_file, run_tool
|
||||
from utils.source_resolver import expand_sources
|
||||
from models.project import ProjectConfig
|
||||
import subprocess
|
||||
@@ -44,58 +45,17 @@ def generate_xst_script_file(project: ProjectConfig, output_path: str):
|
||||
|
||||
|
||||
|
||||
def run_xst(project: ProjectConfig, working_dir: Optional[str] = None):
|
||||
"""
|
||||
Führt Xilinx XST Synthese aus, basierend auf dem gegebenen Projekt.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Geladene Projektkonfiguration.
|
||||
working_dir (str, optional): Pfad, wo .prj/.scr liegen und gebaut werden soll.
|
||||
Wenn None, wird das aktuelle Arbeitsverzeichnis verwendet.
|
||||
"""
|
||||
if working_dir is None:
|
||||
working_dir = DIRECTORIES.build
|
||||
|
||||
xilinx_bin_dir = os.path.join(project.xilinx_path, "bin", "lin64") # oder "nt64" für Windows
|
||||
xst_executable = os.path.join(xilinx_bin_dir, "xst")
|
||||
|
||||
if not os.path.exists(xst_executable):
|
||||
raise FileNotFoundError(f"XST-Executable nicht gefunden unter: {xst_executable}")
|
||||
|
||||
print(f"[hdlbuild] Starte XST Synthese über {xst_executable}")
|
||||
print(f"[hdlbuild] Arbeitsverzeichnis: {working_dir}")
|
||||
|
||||
cmd = [xst_executable]
|
||||
|
||||
# Füge die "common" Optionen ein, wenn sie existieren
|
||||
if project.tool_options and project.tool_options.common:
|
||||
cmd.extend(project.tool_options.common)
|
||||
|
||||
# Jetzt die XST-spezifischen Aufrufe
|
||||
cmd.extend([
|
||||
"-ifn", f"{project.name}.scr"
|
||||
])
|
||||
|
||||
print(f"[hdlbuild] XST-Befehl: {' '.join(cmd)}")
|
||||
|
||||
subprocess.run(cmd, cwd=working_dir, check=True)
|
||||
def run_xst(project: ProjectConfig):
|
||||
run_tool(
|
||||
project=project,
|
||||
tool_executable_name="xst",
|
||||
mandatory_arguments=["-ifn", f"{project.name}.scr"]
|
||||
)
|
||||
|
||||
def copy_synthesis_report(project: ProjectConfig):
|
||||
"""
|
||||
Kopiert den Synthesebericht (.srp) vom Build-Verzeichnis ins Report-Verzeichnis
|
||||
und benennt ihn sinnvoll um.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Geladene Projektkonfiguration.
|
||||
"""
|
||||
src_path = os.path.join(DIRECTORIES.build, f"{project.name}.srp")
|
||||
dst_path = os.path.join(DIRECTORIES.report, f"{project.name}.SynthesisReport")
|
||||
|
||||
if not os.path.exists(src_path):
|
||||
raise FileNotFoundError(f"Synthesebericht nicht gefunden: {src_path}")
|
||||
|
||||
# Stelle sicher, dass das Zielverzeichnis existiert
|
||||
os.makedirs(DIRECTORIES.report, exist_ok=True)
|
||||
|
||||
shutil.copyfile(src_path, dst_path)
|
||||
print(f"[hdlbuild] Synthesebericht kopiert nach {dst_path}")
|
||||
copy_report_file(
|
||||
project=project,
|
||||
source_filename=f"{project.name}.srp",
|
||||
destination_filename=f"{project.name}.SynthesisReport",
|
||||
description="Synthesebericht"
|
||||
)
|
Reference in New Issue
Block a user