Introduces support for Xilinx ISE build flow
Adds scripts and utilities for synthesis, mapping, placement, and bitstream generation using Xilinx ISE tools. Refactors configuration management into a dedicated module. Updates project model to support tool-specific options. Adjusts `.gitignore` and Python version compatibility. Simplifies directory handling and ensures modularity by reorganizing configuration and tool logic.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -184,3 +184,4 @@ vhdl/
|
|||||||
poetry.lock
|
poetry.lock
|
||||||
project.yml
|
project.yml
|
||||||
.project/
|
.project/
|
||||||
|
.devcontainer/
|
@@ -7,7 +7,7 @@ license = "MIT"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.12"
|
python = "^3.10"
|
||||||
pyyaml = "^6.0.2"
|
pyyaml = "^6.0.2"
|
||||||
doit = "^0.36.0"
|
doit = "^0.36.0"
|
||||||
pydantic = "^2.11.3"
|
pydantic = "^2.11.3"
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
class DirectoryConfig(BaseModel):
|
|
||||||
dependency: str = ".hdlbuild_deps"
|
|
||||||
build: str = ".working"
|
|
||||||
report: str = "reports"
|
|
||||||
copy_target: str = "output"
|
|
||||||
|
|
||||||
DIRECTORIES = DirectoryConfig()
|
|
||||||
|
|
||||||
class GitConfig(BaseModel):
|
|
||||||
timeout: int = 10
|
|
||||||
|
|
||||||
GIT = GitConfig()
|
|
@@ -1,3 +1,9 @@
|
|||||||
|
from models.config import DIRECTORIES
|
||||||
|
from tools.xilinx_ise.bitgen import copy_bitstream_file, run_bitgen
|
||||||
|
from tools.xilinx_ise.map import copy_map_report, run_map
|
||||||
|
from tools.xilinx_ise.ngdbuild import run_ngdbuild
|
||||||
|
from tools.xilinx_ise.par import copy_par_report, copy_pinout_report, run_par
|
||||||
|
from tools.xilinx_ise.xst import copy_synthesis_report, generate_xst_project_file, generate_xst_script_file, run_xst
|
||||||
from utils.directory_manager import clear_directories, ensure_directories_exist
|
from utils.directory_manager import clear_directories, ensure_directories_exist
|
||||||
from utils.project_loader import load_project_config
|
from utils.project_loader import load_project_config
|
||||||
from utils.source_resolver import expand_sources
|
from utils.source_resolver import expand_sources
|
||||||
@@ -7,11 +13,40 @@ project = load_project_config()
|
|||||||
print(project.name)
|
print(project.name)
|
||||||
print(project.sources.vhdl)
|
print(project.sources.vhdl)
|
||||||
|
|
||||||
|
clear_directories()
|
||||||
|
|
||||||
ensure_directories_exist()
|
ensure_directories_exist()
|
||||||
|
|
||||||
clear_directories()
|
|
||||||
|
|
||||||
expanded_vhdl = expand_sources(project.sources.vhdl)
|
expanded_vhdl = expand_sources(project.sources.vhdl)
|
||||||
|
|
||||||
for library, filepath in expanded_vhdl:
|
for library, filepath in expanded_vhdl:
|
||||||
print(f"vhdl {library} \"{filepath}\"")
|
print(f"vhdl {library} \"{filepath}\"")
|
||||||
|
|
||||||
|
generate_xst_project_file(project, f"{DIRECTORIES.build}/{project.name}.prj")
|
||||||
|
generate_xst_script_file(project, f"{DIRECTORIES.build}/{project.name}.scr")
|
||||||
|
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)
|
||||||
|
|
||||||
|
run_bitgen(project)
|
||||||
|
copy_bitstream_file(project)
|
26
src/models/config.py
Normal file
26
src/models/config.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import os
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
class DirectoryConfig(BaseModel):
|
||||||
|
dependency: str = ".hdlbuild_deps"
|
||||||
|
build: str = ".working"
|
||||||
|
report: str = "reports"
|
||||||
|
copy_target: str = "output"
|
||||||
|
|
||||||
|
def get_relative_prefix(self) -> str:
|
||||||
|
"""
|
||||||
|
Gibt den relativen Pfad von build-Verzeichnis zurück zum Hauptverzeichnis.
|
||||||
|
|
||||||
|
Beispiel:
|
||||||
|
".working" -> "../"
|
||||||
|
".build/deep" -> "../../"
|
||||||
|
"""
|
||||||
|
depth = len(os.path.normpath(self.build).split(os.sep))
|
||||||
|
return "../" * depth
|
||||||
|
|
||||||
|
DIRECTORIES = DirectoryConfig()
|
||||||
|
|
||||||
|
class GitConfig(BaseModel):
|
||||||
|
timeout: int = 10
|
||||||
|
|
||||||
|
GIT = GitConfig()
|
@@ -5,6 +5,16 @@ class SourceFile(BaseModel):
|
|||||||
path: str
|
path: str
|
||||||
library: str = "work" # Default auf 'work'
|
library: str = "work" # Default auf 'work'
|
||||||
|
|
||||||
|
class ToolOptions(BaseModel):
|
||||||
|
common: List[str] = Field(default_factory=list)
|
||||||
|
xst: List[str] = Field(default_factory=list)
|
||||||
|
ngdbuild: List[str] = Field(default_factory=list)
|
||||||
|
map: List[str] = Field(default_factory=list)
|
||||||
|
par: List[str] = Field(default_factory=list)
|
||||||
|
bitgen: List[str] = Field(default_factory=list)
|
||||||
|
trace: List[str] = Field(default_factory=list)
|
||||||
|
fuse: List[str] = Field(default_factory=list)
|
||||||
|
|
||||||
class Dependency(BaseModel):
|
class Dependency(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
git: str
|
git: str
|
||||||
@@ -34,3 +44,4 @@ class ProjectConfig(BaseModel):
|
|||||||
constraints: Optional[str] = None
|
constraints: Optional[str] = None
|
||||||
build: Optional[BuildOptions] = None
|
build: Optional[BuildOptions] = None
|
||||||
dependencies: Optional[List[Dependency]] = Field(default_factory=list)
|
dependencies: Optional[List[Dependency]] = Field(default_factory=list)
|
||||||
|
tool_options: Optional[ToolOptions] = ToolOptions()
|
||||||
|
63
src/tools/xilinx_ise/bitgen.py
Normal file
63
src/tools/xilinx_ise/bitgen.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from typing import Optional
|
||||||
|
from models.project import ProjectConfig
|
||||||
|
from models.config import DIRECTORIES
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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):
|
||||||
|
"""
|
||||||
|
Kopiert die Bitstream-Datei (.bit) vom Build-Verzeichnis ins Output-Verzeichnis.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project (ProjectConfig): Geladene Projektkonfiguration.
|
||||||
|
"""
|
||||||
|
src_path = os.path.join(DIRECTORIES.build, f"{project.name}.bit")
|
||||||
|
dst_path = os.path.join(DIRECTORIES.copy_target, f"{project.name}.bit")
|
||||||
|
|
||||||
|
if not os.path.exists(src_path):
|
||||||
|
raise FileNotFoundError(f"Bitstream-Datei nicht gefunden: {src_path}")
|
||||||
|
|
||||||
|
os.makedirs(DIRECTORIES.copy_target, exist_ok=True)
|
||||||
|
|
||||||
|
shutil.copyfile(src_path, dst_path)
|
||||||
|
print(f"[hdlbuild] Bitstream-Datei kopiert nach {dst_path}")
|
54
src/tools/xilinx_ise/common.py
Normal file
54
src/tools/xilinx_ise/common.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
from typing import Optional, List
|
||||||
|
from models.project import ProjectConfig
|
||||||
|
from models.config import DIRECTORIES
|
||||||
|
|
||||||
|
def run_tool(
|
||||||
|
project: ProjectConfig,
|
||||||
|
tool_executable_name: str,
|
||||||
|
tool_option_attr: str,
|
||||||
|
mandatory_arguments: List[str],
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
if working_dir is None:
|
||||||
|
working_dir = DIRECTORIES.build
|
||||||
|
|
||||||
|
xilinx_bin_dir = os.path.join(project.xilinx_path, "bin", "lin64") # oder "nt64"
|
||||||
|
tool_executable = os.path.join(xilinx_bin_dir, tool_executable_name)
|
||||||
|
|
||||||
|
if not os.path.exists(tool_executable):
|
||||||
|
raise FileNotFoundError(f"Executable nicht gefunden: {tool_executable}")
|
||||||
|
|
||||||
|
print(f"[hdlbuild] Starte {tool_executable_name.upper()} über {tool_executable}")
|
||||||
|
print(f"[hdlbuild] Arbeitsverzeichnis: {working_dir}")
|
||||||
|
|
||||||
|
cmd = [tool_executable]
|
||||||
|
|
||||||
|
# Füge zuerst "common" Optionen ein
|
||||||
|
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:
|
||||||
|
tool_opts = getattr(project.tool_options, tool_option_attr, [])
|
||||||
|
if tool_opts:
|
||||||
|
cmd.extend(tool_opts)
|
||||||
|
|
||||||
|
# Füge die Pflicht-Argumente an
|
||||||
|
cmd.extend(mandatory_arguments)
|
||||||
|
|
||||||
|
print(f"[hdlbuild] Befehl: {' '.join(cmd)}")
|
||||||
|
|
||||||
|
subprocess.run(cmd, cwd=working_dir, check=True)
|
67
src/tools/xilinx_ise/map.py
Normal file
67
src/tools/xilinx_ise/map.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from typing import Optional
|
||||||
|
from models.project import ProjectConfig
|
||||||
|
from models.config import DIRECTORIES
|
||||||
|
|
||||||
|
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 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}")
|
46
src/tools/xilinx_ise/ngdbuild.py
Normal file
46
src/tools/xilinx_ise/ngdbuild.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
from typing import Optional
|
||||||
|
from models.project import ProjectConfig
|
||||||
|
from models.config import DIRECTORIES
|
||||||
|
|
||||||
|
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)
|
85
src/tools/xilinx_ise/par.py
Normal file
85
src/tools/xilinx_ise/par.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
from typing import Optional
|
||||||
|
from models.project import ProjectConfig
|
||||||
|
from models.config import DIRECTORIES
|
||||||
|
|
||||||
|
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 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}")
|
||||||
|
|
||||||
|
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}")
|
101
src/tools/xilinx_ise/xst.py
Normal file
101
src/tools/xilinx_ise/xst.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
from typing import Optional
|
||||||
|
from models.config import DIRECTORIES
|
||||||
|
from utils.source_resolver import expand_sources
|
||||||
|
from models.project import ProjectConfig
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
def generate_xst_project_file(project: ProjectConfig, output_path: str):
|
||||||
|
"""
|
||||||
|
Generiert die XST .prj-Datei mit allen Quellcodes.
|
||||||
|
"""
|
||||||
|
with open(output_path, "w") as f:
|
||||||
|
# VHDL-Sources
|
||||||
|
for lib, src in expand_sources(project.sources.vhdl):
|
||||||
|
f.write(f"vhdl {lib} \"{DIRECTORIES.get_relative_prefix()}/{src}\"\n")
|
||||||
|
# Verilog-Sources
|
||||||
|
for lib, src in expand_sources(project.sources.verilog):
|
||||||
|
f.write(f"verilog {lib} \"{DIRECTORIES.get_relative_prefix()}/{src}\"\n")
|
||||||
|
|
||||||
|
# Optionale Dependencies
|
||||||
|
if project.dependencies:
|
||||||
|
for dep in project.dependencies:
|
||||||
|
# Hier könnte man noch spezielle Sources aus dep.path expandieren
|
||||||
|
pass
|
||||||
|
|
||||||
|
def generate_xst_script_file(project: ProjectConfig, output_path: str):
|
||||||
|
"""
|
||||||
|
Generiert die XST .scr-Datei mit den Synthese-Optionen.
|
||||||
|
"""
|
||||||
|
with open(output_path, "w") as f:
|
||||||
|
f.write(f"run ")
|
||||||
|
f.write(f"-ifn {project.name}.prj ")
|
||||||
|
f.write(f"-ofn {project.name}.ngc ")
|
||||||
|
f.write(f"-ifmt mixed ")
|
||||||
|
|
||||||
|
if project.tool_options and project.tool_options.xst:
|
||||||
|
for opt in project.tool_options.xst:
|
||||||
|
f.write(f"{opt} ")
|
||||||
|
|
||||||
|
f.write(f"-top {project.topmodule} ")
|
||||||
|
f.write(f"-ofmt NGC ")
|
||||||
|
f.write(f"-p {project.target_device} ")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 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}")
|
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from config import DIRECTORIES
|
from models.config import DIRECTORIES
|
||||||
|
|
||||||
def ensure_directories_exist():
|
def ensure_directories_exist():
|
||||||
"""
|
"""
|
||||||
|
Reference in New Issue
Block a user