Compare commits
5 Commits
961d182bf7
...
cef035329a
Author | SHA1 | Date | |
---|---|---|---|
cef035329a | |||
93e67c0380 | |||
8caafe862e | |||
cfa62a5624 | |||
263c3fac5e |
7
.github/workflows/build-deb.yml
vendored
7
.github/workflows/build-deb.yml
vendored
@@ -8,14 +8,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: 🛒 Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: ⚙️ Prepare Environment Variables
|
||||
run: |
|
||||
echo "AGENT_TOOLSDIRECTORY=/home/runner/toolcache" >> $GITHUB_ENV
|
||||
mkdir -p /home/runner/toolcache
|
||||
|
||||
- name: 🛒 Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🐍 Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
@@ -25,6 +25,7 @@ jobs:
|
||||
run: |
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
poetry self add poetry-plugin-export
|
||||
|
||||
- name: 🔧 Install Project Dependencies
|
||||
run: |
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -184,4 +184,5 @@ vhdl/
|
||||
poetry.lock
|
||||
project.yml
|
||||
.project/
|
||||
.devcontainer/
|
||||
.devcontainer/
|
||||
vhdltests/
|
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "hdlbuild"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
description = "Flexible FPGA Build System"
|
||||
authors = ["0xMax42 <Mail@0xMax42.io>"]
|
||||
license = "MIT"
|
||||
@@ -18,10 +18,6 @@ pydantic = "^2.11.3"
|
||||
rich = "^14.0.0"
|
||||
gitpython = "^3.1.44"
|
||||
|
||||
[tool.poetry.requires-plugins]
|
||||
poetry-plugin-export = ">=1.8"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
@@ -2,6 +2,8 @@ import argparse
|
||||
import sys
|
||||
|
||||
from hdlbuild.dependencies.resolver import DependencyResolver
|
||||
from hdlbuild.models.config import DIRECTORIES
|
||||
from hdlbuild.tools.xilinx_ise.isim import build_testbench, generate_simulation_project_file, run_testbench
|
||||
from hdlbuild.tools.xilinx_ise.main import xilinx_ise_all, xilinx_ise_synth
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
from hdlbuild.utils.directory_manager import clear_build_directories, clear_directories, ensure_directories_exist
|
||||
@@ -38,6 +40,12 @@ def dep(args):
|
||||
console_utils.print("Starting dependencies process...")
|
||||
DependencyResolver(project).resolve_all()
|
||||
|
||||
def test(args):
|
||||
"""Starts the test process."""
|
||||
console_utils.print("Starting test process...")
|
||||
build_testbench(project, args.target)
|
||||
run_testbench(args.target)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
@@ -67,12 +75,21 @@ def main():
|
||||
parser_build.set_defaults(func=build)
|
||||
|
||||
# Synth command
|
||||
parser_build = subparsers.add_parser("synth", help="Start the synth process")
|
||||
parser_build.set_defaults(func=synth)
|
||||
parser_synth = subparsers.add_parser("synth", help="Start the synth process")
|
||||
parser_synth.set_defaults(func=synth)
|
||||
|
||||
# Dependencies command
|
||||
parser_build = subparsers.add_parser("dep", help="Start the dependencies process")
|
||||
parser_build.set_defaults(func=dep)
|
||||
parser_dep = subparsers.add_parser("dep", help="Start the dependencies process")
|
||||
parser_dep.set_defaults(func=dep)
|
||||
|
||||
# Tests command
|
||||
parser_test = subparsers.add_parser("test", help="Start the Tests process")
|
||||
parser_test.set_defaults(func=test)
|
||||
parser_test.add_argument(
|
||||
"target",
|
||||
nargs="?",
|
||||
help="Select the target to test"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
@@ -23,7 +23,8 @@ def copy_bitstream_file(project: ProjectConfig):
|
||||
copy_file(
|
||||
project=project,
|
||||
source_filename=f"{project.name}.bit",
|
||||
destination_filename=f"{project.name}.Bitstream",
|
||||
destination_filename=f"{project.name}.bit",
|
||||
destination_dir=DIRECTORIES.copy_target,
|
||||
description="Bitstream File",
|
||||
step_number=10, total_steps=12
|
||||
)
|
@@ -46,6 +46,7 @@ def copy_file(
|
||||
source_filename: str,
|
||||
destination_filename: str,
|
||||
description: str = "Report",
|
||||
destination_dir: str = DIRECTORIES.report,
|
||||
step_number: Optional[int] = None,
|
||||
total_steps: Optional[int] = None
|
||||
):
|
||||
@@ -59,12 +60,12 @@ def copy_file(
|
||||
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)
|
||||
dst_path = os.path.join(destination_dir, destination_filename)
|
||||
|
||||
if not os.path.exists(src_path):
|
||||
raise FileNotFoundError(f"{description} nicht gefunden: {src_path}")
|
||||
|
||||
os.makedirs(DIRECTORIES.report, exist_ok=True)
|
||||
os.makedirs(destination_dir, exist_ok=True)
|
||||
|
||||
shutil.copyfile(src_path, dst_path)
|
||||
|
||||
|
140
src/hdlbuild/tools/xilinx_ise/isim.py
Normal file
140
src/hdlbuild/tools/xilinx_ise/isim.py
Normal file
@@ -0,0 +1,140 @@
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
from hdlbuild.models.project import ProjectConfig
|
||||
from hdlbuild.models.config import DIRECTORIES
|
||||
from hdlbuild.dependencies.resolver import DependencyResolver
|
||||
from hdlbuild.utils.console_utils import ConsoleTask
|
||||
from hdlbuild.utils.source_resolver import expand_all_sources, expand_testbenches
|
||||
|
||||
def generate_simulation_project_file(project: ProjectConfig, output_path: str, testbench_name: str):
|
||||
"""
|
||||
Generiert die ISim Simulationsprojektdatei (.prj).
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Das Hauptprojekt.
|
||||
output_path (str): Zielpfad für die .prj Datei.
|
||||
testbench_name (str): Name der Testbench-Datei (z.B. "VGATimingGenerator_test_tb").
|
||||
"""
|
||||
resolver = DependencyResolver(project, offline_mode=True)
|
||||
resolver.resolve_all()
|
||||
|
||||
vhdl_sources, verilog_sources = expand_all_sources(project, resolver.resolved)
|
||||
|
||||
with open(output_path, "w") as f:
|
||||
# Normale VHDL-Sources
|
||||
for lib, file in vhdl_sources:
|
||||
f.write(f"vhdl {lib} \"{DIRECTORIES.get_relative_prefix()}{file}\"\n")
|
||||
|
||||
# Normale Verilog-Sources
|
||||
for lib, file in verilog_sources:
|
||||
f.write(f"verilog {lib} \"{DIRECTORIES.get_relative_prefix()}{file}\"\n")
|
||||
|
||||
# Testbench-Datei suchen und einfügen
|
||||
testbench_file = find_testbench_file(project, testbench_name)
|
||||
normalized_tb = os.path.normpath(testbench_file)
|
||||
f.write(f"vhdl work \"{DIRECTORIES.get_relative_prefix()}{normalized_tb}\"\n")
|
||||
|
||||
# glbl.v immer zuletzt
|
||||
f.write(f"verilog work /opt/Xilinx/14.7/ISE_DS/ISE/verilog/src/glbl.v\n")
|
||||
|
||||
|
||||
def find_testbench_file(project: ProjectConfig, testbench_name: str) -> str:
|
||||
"""
|
||||
Findet eine Testbench-Datei im Projekt anhand ihres Namens (ohne Endung, Case-Insensitive).
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Projektdefinition.
|
||||
testbench_name (str): Gesuchter Dateiname (z.B. "VGATimingGenerator_test_tb").
|
||||
|
||||
Returns:
|
||||
str: Vollständiger Pfad zur Testbench-Datei.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: Wenn die Datei nicht gefunden wurde.
|
||||
"""
|
||||
candidates = expand_testbenches(project)
|
||||
|
||||
# Vergleichswerte vorbereiten (Name ohne Endung, in Kleinbuchstaben)
|
||||
search_name = os.path.splitext(testbench_name)[0].lower()
|
||||
|
||||
for _, filepath in candidates:
|
||||
filename = os.path.basename(filepath)
|
||||
filename_no_ext = os.path.splitext(filename)[0].lower()
|
||||
|
||||
if filename_no_ext == search_name:
|
||||
return filepath
|
||||
|
||||
raise FileNotFoundError(f"Testbench '{testbench_name}' wurde nicht gefunden.")
|
||||
|
||||
def build_testbench(project: ProjectConfig, testbench_name: str):
|
||||
"""
|
||||
Baut eine einzelne Testbench mit FUSE.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Hauptprojekt-Konfiguration
|
||||
testbench_name (str): Name der Testbench-Datei, z.B. "VGATimingGenerator_test_tb.vhd"
|
||||
"""
|
||||
# Pfade
|
||||
isim_exe_name = f"isim_{testbench_name.replace('.vhd', '').replace('.v', '')}"
|
||||
isim_exe_path = os.path.join(DIRECTORIES.build, isim_exe_name)
|
||||
|
||||
# 1. Simulation-Projektdatei generieren
|
||||
generate_simulation_project_file(
|
||||
project=project,
|
||||
output_path=os.path.join(DIRECTORIES.build, f"{project.name}_sim.prj"),
|
||||
testbench_name=testbench_name
|
||||
)
|
||||
|
||||
# 2. Befehl bauen
|
||||
xilinx_path = project.xilinx_path
|
||||
xilinx_bin_dir = os.path.join(xilinx_path, "bin", "lin64") # oder nt64 bei Windows
|
||||
fuse_executable = os.path.join(xilinx_bin_dir, "fuse")
|
||||
|
||||
cmd = [
|
||||
fuse_executable,
|
||||
"-intstyle", "xflow",
|
||||
"-prj", f"{project.name}_sim.prj",
|
||||
"-o", isim_exe_name,
|
||||
f"work.{testbench_name.replace('.vhd', '').replace('.v', '')}",
|
||||
"work.glbl"
|
||||
]
|
||||
|
||||
# 3. Ausführen mit Konsole
|
||||
task = ConsoleTask(prefix="hdlbuild", title=f"FUSE {testbench_name}")
|
||||
result = task.run_command(cmd, cwd=DIRECTORIES.build)
|
||||
|
||||
if result != 0:
|
||||
raise RuntimeError(f"FUSE fehlgeschlagen für Testbench {testbench_name}")
|
||||
|
||||
def run_testbench(testbench_name: str):
|
||||
"""
|
||||
Führt eine gebaute Testbench-Executable aus (ISim Simulation).
|
||||
|
||||
Args:
|
||||
testbench_name (str): Name der Testbench-Datei (z.B. "VGATimingGenerator_test_tb.vhd")
|
||||
"""
|
||||
# Pfade
|
||||
isim_exe_name = f"isim_{testbench_name.replace('.vhd', '').replace('.v', '')}"
|
||||
isim_exe_path = os.path.join(DIRECTORIES.build, isim_exe_name)
|
||||
|
||||
isim_cmd_file = os.path.join(DIRECTORIES.build, f"{isim_exe_name}.cmd")
|
||||
|
||||
# 1. TCL-Skript für ISim erzeugen (einfache Simulation)
|
||||
with open(isim_cmd_file, "w") as f:
|
||||
f.write("")
|
||||
|
||||
# 2. Kommando bauen
|
||||
cmd = [
|
||||
f"./{isim_exe_name}",
|
||||
"-gui",
|
||||
"-tclbatch",
|
||||
f"{isim_exe_name}.cmd"
|
||||
]
|
||||
|
||||
# 3. Ausführen
|
||||
task = ConsoleTask(prefix="hdlbuild", title=f"RUN {testbench_name}")
|
||||
result = task.run_command(cmd, cwd=DIRECTORIES.build)
|
||||
|
||||
if result != 0:
|
||||
raise RuntimeError(f"Testbench {testbench_name} ist während der Simulation fehlgeschlagen!")
|
@@ -66,3 +66,35 @@ def expand_all_sources(root_project: ProjectConfig, resolved_dependencies: List[
|
||||
all_verilog_sources.extend(verilog_dep)
|
||||
|
||||
return all_vhdl_sources, all_verilog_sources
|
||||
|
||||
|
||||
def expand_testbenches(project: ProjectConfig) -> List[Tuple[str, str]]:
|
||||
"""
|
||||
Expandiert nur die Testbenches (vhdl und verilog) aus dem Hauptprojekt.
|
||||
|
||||
Args:
|
||||
project (ProjectConfig): Das Hauptprojekt.
|
||||
|
||||
Returns:
|
||||
List of (library, filepath) Tupel
|
||||
"""
|
||||
expanded = []
|
||||
|
||||
if project.testbenches:
|
||||
# VHDL Testbenches
|
||||
for source in project.testbenches.vhdl:
|
||||
full_pattern = os.path.join(".", source.path)
|
||||
matched_files = glob.glob(full_pattern, recursive=True)
|
||||
for file in matched_files:
|
||||
normalized = os.path.normpath(file)
|
||||
expanded.append((source.library, normalized))
|
||||
|
||||
# Verilog Testbenches (optional)
|
||||
for source in project.testbenches.verilog:
|
||||
full_pattern = os.path.join(".", source.path)
|
||||
matched_files = glob.glob(full_pattern, recursive=True)
|
||||
for file in matched_files:
|
||||
normalized = os.path.normpath(file)
|
||||
expanded.append((source.library, normalized))
|
||||
|
||||
return expanded
|
Reference in New Issue
Block a user