From cd45b22f02c44cfffc3274cd26a62e984e86dc4a Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Thu, 12 Mar 2026 13:34:31 -0500 Subject: [PATCH 1/8] test VivadoAccelerator synth --- test/pytest/ci-template.yml | 2 + test/pytest/conftest.py | 11 ++++ test/pytest/synthesis_helpers.py | 2 + test/pytest/test_keras_api_vivadoacc.py | 67 +++++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 test/pytest/test_keras_api_vivadoacc.py diff --git a/test/pytest/ci-template.yml b/test/pytest/ci-template.yml index ebbcd8a21e..47e52c1ce8 100644 --- a/test/pytest/ci-template.yml +++ b/test/pytest/ci-template.yml @@ -15,6 +15,8 @@ - pip install .${EXTRA_DEPS} # set up vivado_hls command + # TODO: For VivadoAccelerator full synthesis (vsynth/bitfile), export/provide the full Vivado toolchain (`vivado`), + # not only `vivado_hls`. - mkdir -p cmd_vivado_${VIVADO_VERSION} - echo '#!/bin/bash' > cmd_vivado_${VIVADO_VERSION}/vivado_hls - echo "apptainer exec --cleanenv --env LANG=C,LC_ALL=C /cvmfs/projects.cern.ch/hls4ml/vivado/${VIVADO_VERSION} vivado_hls \"\$@\"" >> cmd_vivado_${VIVADO_VERSION}/vivado_hls diff --git a/test/pytest/conftest.py b/test/pytest/conftest.py index 7841a1a989..0e1d9d1fae 100644 --- a/test/pytest/conftest.py +++ b/test/pytest/conftest.py @@ -56,12 +56,23 @@ def synthesis_config(): 'run_synthesis': str_to_bool(os.getenv('RUN_SYNTHESIS', 'false')), 'tools_version': { 'Vivado': os.getenv('VIVADO_VERSION', '2020.1'), + 'VivadoAccelerator': os.getenv('VIVADO_VERSION', '2020.1'), 'Vitis': os.getenv('VITIS_VERSION', '2024.1'), 'Quartus': os.getenv('QUARTUS_VERSION', 'latest'), 'oneAPI': os.getenv('ONEAPI_VERSION', '2025.0.1'), }, 'build_args': { 'Vivado': {'csim': False, 'synth': True, 'export': False}, + # Full accelerator flow: run C/RTL synthesis, downstream Vivado synthesis, and board project/bitfile. + 'VivadoAccelerator': { + 'csim': False, + 'synth': True, + 'cosim': False, + 'validation': False, + 'export': True, + 'vsynth': True, + 'bitfile': True, + }, 'Vitis': {'csim': False, 'synth': True, 'export': False}, 'Quartus': {'synth': True, 'fpgasynth': False}, 'oneAPI': {'build_type': 'report', 'run': False}, diff --git a/test/pytest/synthesis_helpers.py b/test/pytest/synthesis_helpers.py index 27d953b101..7a71700b2c 100644 --- a/test/pytest/synthesis_helpers.py +++ b/test/pytest/synthesis_helpers.py @@ -119,6 +119,7 @@ def compare_oneapi_backend(data, baseline): COMPARE_FUNCS = { 'Vivado': compare_vitis_backend, + 'VivadoAccelerator': compare_vitis_backend, 'Vitis': compare_vitis_backend, 'oneAPI': compare_oneapi_backend, } @@ -126,6 +127,7 @@ def compare_oneapi_backend(data, baseline): EXPECTED_REPORT_KEYS = { 'Vivado': {'CSynthesisReport'}, + 'VivadoAccelerator': {'CSynthesisReport'}, 'Vitis': {'CSynthesisReport'}, 'oneAPI': {'report'}, } diff --git a/test/pytest/test_keras_api_vivadoacc.py b/test/pytest/test_keras_api_vivadoacc.py new file mode 100644 index 0000000000..725e0e3621 --- /dev/null +++ b/test/pytest/test_keras_api_vivadoacc.py @@ -0,0 +1,67 @@ +from pathlib import Path + +import numpy as np +import pytest +import tensorflow as tf +from synthesis_helpers import run_synthesis_test +from tensorflow.keras.layers import ( + Activation, + Dense, +) + +import hls4ml + +test_root_path = Path(__file__).parent + + +@pytest.mark.parametrize('backend', ['Vivado', 'VivadoAccelerator']) +@pytest.mark.parametrize('io_type', ['io_parallel']) +def test_dense(test_case_id, backend, io_type, synthesis_config): + model = tf.keras.models.Sequential() + model.add( + Dense( + 2, + input_shape=(1,), + name='Dense', + use_bias=True, + kernel_initializer=tf.keras.initializers.RandomUniform(minval=1, maxval=10), + bias_initializer='zeros', + kernel_regularizer=None, + bias_regularizer=None, + activity_regularizer=None, + kernel_constraint=None, + bias_constraint=None, + ) + ) + model.add(Activation(activation='elu', name='Activation')) + model.compile(optimizer='adam', loss='mse') + + X_input = np.random.rand(100, 1) + + keras_prediction = model.predict(X_input) + + config = hls4ml.utils.config_from_keras_model(model) + output_dir = str(test_root_path / test_case_id) + baseline_file_name = f'{test_case_id}.json' + + hls_model = hls4ml.converters.convert_from_keras_model( + model, hls_config=config, output_dir=output_dir, backend=backend, io_type=io_type + ) + + hls_model.compile() + + hls_prediction = hls_model.predict(X_input) + + np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=1e-2, atol=0.01) + + assert len(model.layers) + 1 == len(hls_model.get_layers()) + assert list(hls_model.get_layers())[0].attributes['class_name'] == 'InputLayer' + assert list(hls_model.get_layers())[1].attributes['class_name'] == model.layers[0]._name + assert list(hls_model.get_layers())[2].attributes['class_name'] == 'ELU' + assert list(hls_model.get_layers())[0].attributes['input_shape'] == list(model.layers[0].input_shape[1:]) + assert list(hls_model.get_layers())[1].attributes['n_in'] == model.layers[0].input_shape[1:][0] + assert list(hls_model.get_layers())[1].attributes['n_out'] == model.layers[0].output_shape[1:][0] + assert list(hls_model.get_layers())[2].attributes['activation'] == str(model.layers[1].activation).split()[1] + assert list(hls_model.get_layers())[1].attributes['activation'] == str(model.layers[0].activation).split()[1] + + run_synthesis_test(config=synthesis_config, hls_model=hls_model, baseline_file_name=baseline_file_name, backend=backend) From e915de1bcebe535b56a46bdf8fce09ae963e2c1b Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Thu, 12 Mar 2026 14:36:36 -0500 Subject: [PATCH 2/8] add VivadoAccelerator test baseline --- ...t_dense_io_parallel-VivadoAccelerator.json | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 test/pytest/baselines/Vivado/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json diff --git a/test/pytest/baselines/Vivado/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json b/test/pytest/baselines/Vivado/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json new file mode 100644 index 0000000000..fb49d15a71 --- /dev/null +++ b/test/pytest/baselines/Vivado/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json @@ -0,0 +1,34 @@ +{ + "CSynthesisReport": { + "TargetClockPeriod": "5.00", + "EstimatedClockPeriod": "4.367", + "BestLatency": "24", + "WorstLatency": "24", + "IntervalMin": "25", + "IntervalMax": "25", + "BRAM_18K": "1", + "DSP": "2", + "FF": "2282", + "LUT": "2089", + "URAM": "0", + "AvailableBRAM_18K": "280", + "AvailableDSP": "220", + "AvailableFF": "106400", + "AvailableLUT": "53200", + "AvailableURAM": "0" + }, + "VivadoSynthReport": { + "LUT": "71", + "FF": "159", + "BRAM_18K": "0.5", + "DSP48E": "2" + }, + "TimingReport": { + "WNS": 1.16, + "TNS": 0.0, + "WHS": 0.012, + "THS": 0.0, + "WPWS": 3.75, + "TPWS": 0.0 + } +} From 72d34148471e59ba61f3c0f7a4a02a6c406d6365 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Thu, 12 Mar 2026 14:42:21 -0500 Subject: [PATCH 3/8] add: export vivado command in CI --- test/pytest/ci-template.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/pytest/ci-template.yml b/test/pytest/ci-template.yml index 47e52c1ce8..379860ef2b 100644 --- a/test/pytest/ci-template.yml +++ b/test/pytest/ci-template.yml @@ -14,13 +14,14 @@ - if [ $EXAMPLEMODEL == 1 ]; then git submodule update --init example-models; fi - pip install .${EXTRA_DEPS} - # set up vivado_hls command - # TODO: For VivadoAccelerator full synthesis (vsynth/bitfile), export/provide the full Vivado toolchain (`vivado`), - # not only `vivado_hls`. + # set up Vivado commands - mkdir -p cmd_vivado_${VIVADO_VERSION} - echo '#!/bin/bash' > cmd_vivado_${VIVADO_VERSION}/vivado_hls - echo "apptainer exec --cleanenv --env LANG=C,LC_ALL=C /cvmfs/projects.cern.ch/hls4ml/vivado/${VIVADO_VERSION} vivado_hls \"\$@\"" >> cmd_vivado_${VIVADO_VERSION}/vivado_hls + - echo '#!/bin/bash' > cmd_vivado_${VIVADO_VERSION}/vivado + - echo "apptainer exec --cleanenv --env LANG=C,LC_ALL=C /cvmfs/projects.cern.ch/hls4ml/vivado/${VIVADO_VERSION} vivado \"\$@\"" >> cmd_vivado_${VIVADO_VERSION}/vivado - chmod +x cmd_vivado_${VIVADO_VERSION}/vivado_hls + - chmod +x cmd_vivado_${VIVADO_VERSION}/vivado - export PATH=$PWD/cmd_vivado_${VIVADO_VERSION}:$PATH # set up vitis-run command From 1bf32364d80179f68996d1deab6a2bc304ec707f Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Thu, 12 Mar 2026 14:42:47 -0500 Subject: [PATCH 4/8] assign vivadoaccelerator test to a signle job --- test/pytest/generate_ci_yaml.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/pytest/generate_ci_yaml.py b/test/pytest/generate_ci_yaml.py index 684abc0511..80b86768c1 100644 --- a/test/pytest/generate_ci_yaml.py +++ b/test/pytest/generate_ci_yaml.py @@ -41,6 +41,7 @@ # Value = chunk size per CI job SPLIT_BY_TEST_CASE = { 'test_keras_api': 1, + 'test_keras_api_vivadoacc': 1, } From 9e35fff3a2bc6047f511d39d06c183c4b039cdcc Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Thu, 12 Mar 2026 15:16:28 -0500 Subject: [PATCH 5/8] add board and part in test_keras_api_vivadoacc --- test/pytest/test_keras_api_vivadoacc.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/test/pytest/test_keras_api_vivadoacc.py b/test/pytest/test_keras_api_vivadoacc.py index 725e0e3621..6509968c67 100644 --- a/test/pytest/test_keras_api_vivadoacc.py +++ b/test/pytest/test_keras_api_vivadoacc.py @@ -12,9 +12,11 @@ import hls4ml test_root_path = Path(__file__).parent +VIVADOACC_BOARD = 'pynq-z2' +VIVADOACC_PART = 'xc7z020clg400-1' -@pytest.mark.parametrize('backend', ['Vivado', 'VivadoAccelerator']) +@pytest.mark.parametrize('backend', ['VivadoAccelerator']) @pytest.mark.parametrize('io_type', ['io_parallel']) def test_dense(test_case_id, backend, io_type, synthesis_config): model = tf.keras.models.Sequential() @@ -45,7 +47,13 @@ def test_dense(test_case_id, backend, io_type, synthesis_config): baseline_file_name = f'{test_case_id}.json' hls_model = hls4ml.converters.convert_from_keras_model( - model, hls_config=config, output_dir=output_dir, backend=backend, io_type=io_type + model, + hls_config=config, + output_dir=output_dir, + backend=backend, + io_type=io_type, + board=VIVADOACC_BOARD, + part=VIVADOACC_PART, ) hls_model.compile() @@ -64,4 +72,9 @@ def test_dense(test_case_id, backend, io_type, synthesis_config): assert list(hls_model.get_layers())[2].attributes['activation'] == str(model.layers[1].activation).split()[1] assert list(hls_model.get_layers())[1].attributes['activation'] == str(model.layers[0].activation).split()[1] - run_synthesis_test(config=synthesis_config, hls_model=hls_model, baseline_file_name=baseline_file_name, backend=backend) + run_synthesis_test( + config=synthesis_config, + hls_model=hls_model, + baseline_file_name=baseline_file_name, + backend=backend, + ) From dee7f602117672236b022f07230b1cc910a96418 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Fri, 13 Mar 2026 16:49:41 -0500 Subject: [PATCH 6/8] fix: update vivado board and part --- test/pytest/test_keras_api_vivadoacc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pytest/test_keras_api_vivadoacc.py b/test/pytest/test_keras_api_vivadoacc.py index 6509968c67..b259c9fc7a 100644 --- a/test/pytest/test_keras_api_vivadoacc.py +++ b/test/pytest/test_keras_api_vivadoacc.py @@ -12,8 +12,8 @@ import hls4ml test_root_path = Path(__file__).parent -VIVADOACC_BOARD = 'pynq-z2' -VIVADOACC_PART = 'xc7z020clg400-1' +VIVADOACC_BOARD = 'zcu102' +VIVADOACC_PART = 'xczu9eg-ffvb1156-2-e' @pytest.mark.parametrize('backend', ['VivadoAccelerator']) From 791d062322a7eb86b32b701dde8327e7276435e8 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Fri, 13 Mar 2026 17:00:19 -0500 Subject: [PATCH 7/8] update baselines --- ...t_dense_io_parallel-VivadoAccelerator.json | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/pytest/baselines/Vivado/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json b/test/pytest/baselines/Vivado/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json index fb49d15a71..5b7cb5c46a 100644 --- a/test/pytest/baselines/Vivado/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json +++ b/test/pytest/baselines/Vivado/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json @@ -1,34 +1,34 @@ { "CSynthesisReport": { "TargetClockPeriod": "5.00", - "EstimatedClockPeriod": "4.367", - "BestLatency": "24", - "WorstLatency": "24", - "IntervalMin": "25", - "IntervalMax": "25", + "EstimatedClockPeriod": "4.349", + "BestLatency": "11", + "WorstLatency": "11", + "IntervalMin": "12", + "IntervalMax": "12", "BRAM_18K": "1", "DSP": "2", - "FF": "2282", - "LUT": "2089", + "FF": "622", + "LUT": "1947", "URAM": "0", - "AvailableBRAM_18K": "280", - "AvailableDSP": "220", - "AvailableFF": "106400", - "AvailableLUT": "53200", + "AvailableBRAM_18K": "1824", + "AvailableDSP": "2520", + "AvailableFF": "548160", + "AvailableLUT": "274080", "AvailableURAM": "0" }, "VivadoSynthReport": { - "LUT": "71", - "FF": "159", + "LUT": "47", + "FF": "35", "BRAM_18K": "0.5", "DSP48E": "2" }, "TimingReport": { - "WNS": 1.16, + "WNS": 3.988, "TNS": 0.0, - "WHS": 0.012, + "WHS": 0.01, "THS": 0.0, - "WPWS": 3.75, + "WPWS": 3.5, "TPWS": 0.0 } } From 00484980ba293aafbf4de79fdfec8a0eadf1d67b Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Mon, 16 Mar 2026 10:34:15 -0500 Subject: [PATCH 8/8] move VivadoAccelerator baselines files --- ...as_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/pytest/baselines/{Vivado => VivadoAccelerator}/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json (100%) diff --git a/test/pytest/baselines/Vivado/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json b/test/pytest/baselines/VivadoAccelerator/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json similarity index 100% rename from test/pytest/baselines/Vivado/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json rename to test/pytest/baselines/VivadoAccelerator/2020.1/test_keras_api_vivadoacc_test_dense_io_parallel-VivadoAccelerator.json