diff --git a/src/hdlbuild.py b/src/hdlbuild.py index 9a0d0a4..141d80b 100644 --- a/src/hdlbuild.py +++ b/src/hdlbuild.py @@ -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) diff --git a/src/tools/xilinx_ise/bitgen.py b/src/tools/xilinx_ise/bitgen.py index 2bfb66a..12c1a26 100644 --- a/src/tools/xilinx_ise/bitgen.py +++ b/src/tools/xilinx_ise/bitgen.py @@ -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): """ diff --git a/src/tools/xilinx_ise/common.py b/src/tools/xilinx_ise/common.py index 953b707..5cccba5 100644 --- a/src/tools/xilinx_ise/common.py +++ b/src/tools/xilinx_ise/common.py @@ -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}") \ No newline at end of file diff --git a/src/tools/xilinx_ise/map.py b/src/tools/xilinx_ise/map.py index 52ac0be..a77e6f7 100644 --- a/src/tools/xilinx_ise/map.py +++ b/src/tools/xilinx_ise/map.py @@ -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}") \ No newline at end of file + copy_report_file( + project=project, + source_filename=f"{project.name}.map.mrp", + destination_filename=f"{project.name}.MapReport", + description="Map Report" + ) diff --git a/src/tools/xilinx_ise/ngdbuild.py b/src/tools/xilinx_ise/ngdbuild.py index 677096f..4ad179b 100644 --- a/src/tools/xilinx_ise/ngdbuild.py +++ b/src/tools/xilinx_ise/ngdbuild.py @@ -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" + ] + ) diff --git a/src/tools/xilinx_ise/par.py b/src/tools/xilinx_ise/par.py index fba6cf8..4e832ab 100644 --- a/src/tools/xilinx_ise/par.py +++ b/src/tools/xilinx_ise/par.py @@ -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}") \ No newline at end of file + copy_report_file( + project=project, + source_filename=f"{project.name}_pad.txt", + destination_filename=f"{project.name}.PinoutReport", + description="Pinout Report" + ) \ No newline at end of file diff --git a/src/tools/xilinx_ise/xst.py b/src/tools/xilinx_ise/xst.py index 61c21ab..b22fd72 100644 --- a/src/tools/xilinx_ise/xst.py +++ b/src/tools/xilinx_ise/xst.py @@ -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}") \ No newline at end of file + copy_report_file( + project=project, + source_filename=f"{project.name}.srp", + destination_filename=f"{project.name}.SynthesisReport", + description="Synthesebericht" + ) \ No newline at end of file