-
Notifications
You must be signed in to change notification settings - Fork 467
pytest based synthesis tests #1257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
marco66colombo
wants to merge
44
commits into
fastmachinelearning:main
Choose a base branch
from
marco66colombo:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 22 commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
f1c952a
add conftest.py, utils.py, update test_keras_api.py
marco66colombo 4fb8bfe
Use asserts in check_synthesis
marco66colombo 38f9199
update utils.py
marco66colombo d3038bd
Merge branch 'main' into add-conftest
marco66colombo 109195d
fix baselines dir path
marco66colombo d25ea90
refactor synthesis_helpers.py
marco66colombo f2abbeb
fix comment
marco66colombo 4b05026
Merge branch 'main' into add-conftest
marco66colombo bddf2c2
Merge pull request #1 from marco66colombo/add-conftest
marco66colombo a7bbc04
pytest synthesis tests refactor
marco66colombo 343a4e1
update synthesis_helpers, confetst.py
marco66colombo 08abec3
update default vitis version
marco66colombo 3dae27b
fix typo test_keras_api.py
marco66colombo 76b16fc
Merge branch 'main' into add-conftest
marco66colombo e6b09fd
Merge pull request #2 from marco66colombo/add-conftest
marco66colombo 9528c8f
Merge branch 'fastmachinelearning:main' into main
marco66colombo 3195bcb
clean imports test_keras_api
marco66colombo 5e8990b
clean new lines test_keras_api
marco66colombo c1436a9
Trigger pre-commit hook
marco66colombo 943dc74
update after precommit
marco66colombo 19f653d
Merge branch 'fastmachinelearning:main' into main
marco66colombo 34b28ea
enable vivado and vitis
marco66colombo 8a2771f
Merge branch 'fastmachinelearning:main' into main
marco66colombo 2908052
add oneAPI report support, bug fix
marco66colombo c51c180
add temp test file
marco66colombo e423d16
update generate_ci_template.py
marco66colombo d9c613a
update temp test file
marco66colombo 97f3fe6
install libidn
marco66colombo 1ed33b4
test cmake version
marco66colombo d39d7dc
fix cmake error
marco66colombo 1280aa7
fix cmake error
marco66colombo aaf865f
update baselines, clean ci-template
marco66colombo db21ed4
remove test_keras_api_temp.py
marco66colombo 4b9ceac
Merge remote-tracking branch 'upstream/main'
marco66colombo 45f862b
separate test_keras_api in a single job
marco66colombo 37d184f
update genereate_ci_yaml.py to batch syntehsis tests
marco66colombo 44a11a0
update gitlab-ci.yml to support pytest
marco66colombo df6a487
create jobs for single test functions
marco66colombo f7c0c79
update test policy from skip to fail
marco66colombo 0a90de3
test conv1d vivado
marco66colombo 8a3f0ae
use apptainer in cvmfs
marco66colombo 9581f27
fix bug ci-tempalte
marco66colombo c2f4297
use apptainer-suid
marco66colombo 2872ce7
check apptainer on host
marco66colombo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import os | ||
|
||
import pytest | ||
|
||
|
||
def str_to_bool(val): | ||
return str(val).lower() in ("1", "true") | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
def synthesis_config(): | ||
""" | ||
Fixture that provides synthesis configuration for tests. | ||
|
||
It gathers: | ||
- Whether synthesis should be run (from the RUN_SYNTHESIS env var) | ||
- Tool versions for each supported backend (from env vars) | ||
- Build arguments specific to each backend toolchain | ||
|
||
""" | ||
return { | ||
"run_synthesis": str_to_bool(os.getenv("RUN_SYNTHESIS", "false")), | ||
"tools_version": { | ||
"Vivado": os.getenv("VIVADO_VERSION", "2020.1"), | ||
"Vitis": os.getenv("VITIS_VERSION", "2020.1"), | ||
"Quartus": os.getenv("QUARTUS_VERSION", "latest"), | ||
"oneAPI": os.getenv("ONEAPI_VERSION", "latest"), | ||
}, | ||
"build_args": { | ||
"Vivado": {"csim": False, "synth": True, "export": False}, | ||
"Vitis": {"csim": False, "synth": True, "export": False}, | ||
"Quartus": {"synth": True, "fpgasynth": False}, | ||
"oneAPI": {"build_type": "fpga_emu", "run": False}, | ||
}, | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import json | ||
from pathlib import Path | ||
|
||
import pytest | ||
|
||
|
||
def get_baseline_path(baseline_file_name, backend, version): | ||
""" | ||
Construct the full path to a baseline synthesis report file. | ||
|
||
Args: | ||
baseline_file_name (str): The name of the baseline report file. | ||
backend (str): The backend used (e.g., 'Vivado', 'Vitis'). | ||
version (str): The tool version (e.g., '2020.1'). | ||
|
||
Returns: | ||
Path: A pathlib.Path object pointing to the baseline file location. | ||
""" | ||
return Path(__file__).parent / "baselines" / backend / version / baseline_file_name | ||
|
||
|
||
def save_report(data, filename): | ||
""" | ||
Save synthesis data to a JSON file in the same directory as this script. | ||
|
||
Args: | ||
data (dict): The synthesis output data to be saved. | ||
filename (str): The filename to write to (e.g., 'synthesis_report_test_x.json'). | ||
|
||
Raises: | ||
OSError: If the file cannot be written. | ||
""" | ||
out_path = Path(__file__).parent / filename | ||
with open(out_path, "w") as fp: | ||
json.dump(data, fp, indent=4) | ||
|
||
|
||
def get_tolerance(key): | ||
""" | ||
Get the relative tolerance for a specific synthesis report field. | ||
|
||
Args: | ||
key (str): The key in the synthesis report to compare. | ||
|
||
Returns: | ||
float: The relative tolerance allowed for that key. Defaults to 1% (0.01). | ||
""" | ||
tolerances = { | ||
"EstimatedClockPeriod": 0.01, | ||
"FF": 0.05, | ||
"LUT": 0.10, | ||
"BRAM_18K": 0.0, | ||
"DSP": 0.0, | ||
"URAM": 0.0, | ||
"AvailableBRAM_18K": 0.0, | ||
"AvailableDSP": 0.0, | ||
"AvailableFF": 0.0, | ||
"AvailableLUT": 0.0, | ||
"AvailableURAM": 0.0, | ||
} | ||
|
||
default_tolerance = 0.01 | ||
|
||
return tolerances.get(key, default_tolerance) | ||
|
||
|
||
def compare_reports_with_tolerance(data, baseline): | ||
""" | ||
Compare two synthesis reports using tolerances defined per key. | ||
|
||
Args: | ||
data (dict): The current synthesis report. | ||
baseline (dict): The baseline synthesis report to compare against. | ||
""" | ||
csrBaseline = baseline.get("CSynthesisReport") | ||
csrData = data.get("CSynthesisReport") | ||
|
||
for key, expected_value in csrBaseline.items(): | ||
actual_value = csrData.get(key) | ||
tolerance = get_tolerance(key) | ||
|
||
try: | ||
# Convert to float for numerical comparison | ||
expected_num = float(expected_value) | ||
actual_num = float(actual_value) | ||
assert actual_num == pytest.approx( | ||
expected_num, rel=tolerance | ||
), f"{key}: expected {expected_num}, got {actual_num} (tolerance={tolerance*100}%)" | ||
except ValueError: | ||
# Exact match for non-numeric values | ||
assert actual_value == expected_value, f"{key}: expected '{expected_value}', got '{actual_value}'" | ||
|
||
|
||
def test_synthesis(config, hls_model, baseline_file_name, backend): | ||
""" | ||
Run HLS synthesis and compare the output with a stored baseline report. | ||
|
||
If synthesis is disabled via the configuration (`run_synthesis=False`), | ||
no synthesis is executed and the test silently returns. | ||
|
||
Args: | ||
config (dict): Test-wide synthesis configuration fixture. | ||
hls_model (object): hls4ml model instance to build and synthesize. | ||
baseline_file_name (str): The name of the baseline file for comparison. | ||
backend (str): The synthesis backend used (e.g., 'Vivado', 'Vitis'). | ||
""" | ||
if not config.get("run_synthesis", False): | ||
# TODO: should this info be printed or logged? | ||
return | ||
|
||
if backend == 'oneAPI': | ||
pytest.skip('oneAPI backend not supported in synthesis tests.') | ||
|
||
build_args = config["build_args"] | ||
|
||
try: | ||
data = hls_model.build(**build_args.get(backend, {})) | ||
except Exception as e: | ||
pytest.skip(f"hls_model.build failed: {e}") | ||
|
||
save_report(data, f"synthesis_report_{baseline_file_name}") | ||
|
||
assert data and {'CSynthesisReport'}.issubset( | ||
data.keys() | ||
), "Synthesis failed: Missing expected keys in the synthesis report" | ||
|
||
version = config["tools_version"].get(backend) | ||
baseline_path = get_baseline_path(baseline_file_name, backend, version) | ||
|
||
try: | ||
with open(baseline_path) as fp: | ||
baseline = json.load(fp) | ||
except FileNotFoundError: | ||
pytest.skip(f"Baseline file '{baseline_path}' not found.") | ||
|
||
compare_reports_with_tolerance(data, baseline) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fpga_emu
is actually just C compilation, so this needs to be changed, though to what I am not sure. The descriptions are given in https://github.com/oneapi-src/oneAPI-samples/tree/release/2025.0/DirectProgramming/C%2B%2BSYCL_FPGA/Tutorials/GettingStarted/fpga_compile#four-compilation-options and they correspond tofpga_emu
,report
,fpga_sim
, andfpga
. I would guessreport
unless you want to run cosim, in which case it'sfpga_sim
. The first two don't require Quartus, while the last two do.