Skip to content

Commit 5669de9

Browse files
authored
Merge pull request #138 from merkelmarrow/fix/config-cfg-pre-synth-wiring
Wire config.cfg pre_synth entries into hardware builds
2 parents f200d56 + aeaaa5d commit 5669de9

3 files changed

Lines changed: 140 additions & 8 deletions

File tree

linker/slashkit/core/command_config.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# ##################################################################################################
2020
from enum import Enum
2121
from pathlib import Path
22-
from typing import Dict, List, Optional
22+
from typing import Dict, List, Optional, Set
2323
import re
2424
import os
2525
import shutil
@@ -229,13 +229,7 @@ def __init__(self, args: argparse.Namespace):
229229
self._build_dir.unlink()
230230
self._build_dir.mkdir(parents=True)
231231

232-
# Resolve and verify pre-synthesis TCLs (if any)
233-
self._pre_synth_tcls: List[Path] = []
234-
for path in args.pre_synth_tcls:
235-
path: Path = path.expanduser().resolve()
236-
if not path.is_file():
237-
raise FileNotFoundError(path)
238-
self._pre_synth_tcls.append(path)
232+
# pre-synth Tcls are resolved below, once the config is parsed.
239233

240234
# Misc. arguments
241235
self._platform = Platform(args.platform)
@@ -267,6 +261,20 @@ def __init__(self, args: argparse.Namespace):
267261
self._kernel_instances: List[KernelInstance] = apply_config_to_instances(
268262
self.configuration, self.kernels)
269263

264+
# merge config [user_region] pre_synth= entries with the --pre-synth-tcls
265+
# flag. config entries source first, then CLI ones, with duplicates
266+
# dropped at their first occurrence.
267+
self._pre_synth_tcls: List[Path] = []
268+
seen: Set[Path] = set()
269+
for raw in [*self._configuration.user_region.pre_synth_tcls,
270+
*args.pre_synth_tcls]:
271+
path = Path(raw).expanduser().resolve()
272+
if not path.is_file():
273+
raise FileNotFoundError(path)
274+
if path not in seen:
275+
seen.add(path)
276+
self._pre_synth_tcls.append(path)
277+
270278
@property
271279
def block_design_ports(self) -> BlockDesignPorts:
272280
return self._bd_ports

linker/test/core/__init__.py

Whitespace-only changes.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# ##################################################################################################
2+
# The MIT License (MIT)
3+
# Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
6+
# and associated documentation files (the "Software"), to deal in the Software without restriction,
7+
# including without limitation the rights to use, copy, modify, merge, publish, distribute,
8+
# sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
9+
# furnished to do so, subject to the following conditions:
10+
#
11+
# The above copyright notice and this permission notice shall be included in all copies or
12+
# substantial portions of the Software.
13+
#
14+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15+
# NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17+
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19+
# ##################################################################################################
20+
21+
"""Tests for core.command_config — pre-synth Tcl resolution and merging."""
22+
23+
import argparse
24+
import textwrap
25+
from pathlib import Path
26+
from unittest import mock
27+
28+
import pytest
29+
30+
from slashkit.core import command_config
31+
from slashkit.core.command_config import LinkerConfiguration
32+
33+
_FIXTURE_COMPONENT = (
34+
Path(__file__).resolve().parents[1]
35+
/ "fixtures" / "dma_in" / "hls" / "impl" / "ip" / "component.xml"
36+
)
37+
38+
39+
def _make_args(tmp_path: Path, *, config: Path, cli_pre_synth):
40+
vivado = tmp_path / "vivado"
41+
vivado.write_text("#!/bin/sh\n")
42+
vivado.chmod(0o755)
43+
return argparse.Namespace(
44+
vivado=vivado,
45+
jobs=8,
46+
config=config,
47+
kernels=[_FIXTURE_COMPONENT],
48+
out=tmp_path / "out.vbin",
49+
platform="hw",
50+
pre_synth_tcls=list(cli_pre_synth),
51+
clock_hz=None,
52+
)
53+
54+
55+
def _build_config(tmp_path: Path, *, config: Path, cli_pre_synth):
56+
"""Construct a LinkerConfiguration with the heavy, environment-dependent
57+
lookups stubbed out, so only the pre-synth resolution/merging is exercised."""
58+
args = _make_args(tmp_path, config=config, cli_pre_synth=cli_pre_synth)
59+
with mock.patch.object(command_config, "_find_vitis_include",
60+
return_value=tmp_path), \
61+
mock.patch.object(command_config, "apply_config_to_instances",
62+
return_value=[]):
63+
return LinkerConfiguration(args)
64+
65+
66+
def _write_cfg(tmp_path: Path, body: str) -> Path:
67+
cfg = tmp_path / "config.cfg"
68+
cfg.write_text(textwrap.dedent(body))
69+
return cfg
70+
71+
72+
def test_config_cfg_pre_synth_reaches_pre_synth_tcls(tmp_path):
73+
"""[user_region] pre_synth= entries from the config file must be honoured."""
74+
a = tmp_path / "a.tcl"
75+
a.write_text("# a\n")
76+
cfg = _write_cfg(tmp_path, """
77+
[connectivity]
78+
[user_region]
79+
pre_synth=a.tcl
80+
""")
81+
config = _build_config(tmp_path, config=cfg, cli_pre_synth=[])
82+
assert config.pre_synth_tcls == [a.resolve()]
83+
84+
85+
def test_config_cfg_and_cli_pre_synth_are_merged_in_order(tmp_path):
86+
"""Config-declared scripts run first, then CLI ones."""
87+
a = tmp_path / "a.tcl"
88+
a.write_text("# a\n")
89+
b = tmp_path / "b.tcl"
90+
b.write_text("# b\n")
91+
cli = tmp_path / "cli.tcl"
92+
cli.write_text("# cli\n")
93+
cfg = _write_cfg(tmp_path, """
94+
[connectivity]
95+
[user_region]
96+
pre_synth=a.tcl
97+
pre_synth=b.tcl
98+
""")
99+
config = _build_config(tmp_path, config=cfg, cli_pre_synth=[cli])
100+
assert config.pre_synth_tcls == [a.resolve(), b.resolve(), cli.resolve()]
101+
102+
103+
def test_pre_synth_duplicates_are_de_duplicated(tmp_path):
104+
"""A script named in both the config and on the CLI is sourced once."""
105+
a = tmp_path / "a.tcl"
106+
a.write_text("# a\n")
107+
cfg = _write_cfg(tmp_path, """
108+
[connectivity]
109+
[user_region]
110+
pre_synth=a.tcl
111+
""")
112+
config = _build_config(tmp_path, config=cfg, cli_pre_synth=[a])
113+
assert config.pre_synth_tcls == [a.resolve()]
114+
115+
116+
def test_missing_config_cfg_pre_synth_raises(tmp_path):
117+
"""A non-existent [user_region] pre_synth path fails fast."""
118+
cfg = _write_cfg(tmp_path, """
119+
[connectivity]
120+
[user_region]
121+
pre_synth=does_not_exist.tcl
122+
""")
123+
with pytest.raises(FileNotFoundError):
124+
_build_config(tmp_path, config=cfg, cli_pre_synth=[])

0 commit comments

Comments
 (0)