diff --git a/.gitignore b/.gitignore
index 22c8ff685b..8151cd3af9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@ sdist/
*.egg-info/
vivado_prj
.vscode
+.idea
my-hls-test
*.tar.gz
docs/_build
@@ -14,3 +15,6 @@ docs/autodoc/*
hls4mlprj_*
*~
*.ipynb_checkpoints/
+
+test/pytest/test_backend/input_file/*
+test/pytest/test_backend/output_file/*
diff --git a/hls4ml/backends/__init__.py b/hls4ml/backends/__init__.py
index 4a48f072cd..3bb8aa7c6d 100644
--- a/hls4ml/backends/__init__.py
+++ b/hls4ml/backends/__init__.py
@@ -11,9 +11,13 @@
from hls4ml.backends.vitis.vitis_backend import VitisBackend # isort: skip
+from hls4ml.backends.vitis_unified.vitis_unified_backend import VitisUnifiedBackend # isort: skip
+
+
register_backend('Vivado', VivadoBackend)
register_backend('VivadoAccelerator', VivadoAcceleratorBackend)
register_backend('Vitis', VitisBackend)
+register_backend('VitisUnified', VitisUnifiedBackend)
register_backend('Quartus', QuartusBackend)
register_backend('Catapult', CatapultBackend)
register_backend('SymbolicExpression', SymbolicExpressionBackend)
diff --git a/hls4ml/backends/vitis_unified/passes/fifo_depth_optimization.py b/hls4ml/backends/vitis_unified/passes/fifo_depth_optimization.py
new file mode 100644
index 0000000000..0451270ca1
--- /dev/null
+++ b/hls4ml/backends/vitis_unified/passes/fifo_depth_optimization.py
@@ -0,0 +1,113 @@
+# we inherit it from vitis
+import zipfile
+
+from hls4ml.backends.vitis.passes.fifo_depth_optimization import (
+ generate_depths_file,
+ initialize_large_fifos,
+ set_optimized_fifo_depths,
+)
+from hls4ml.model.optimizer.optimizer import ConfigurableOptimizerPass, ModelOptimizerPass
+
+
+def get_vitis_optimized_fifo_depths(model, cus_hls_prj_path=None):
+ """Parse the files generated by the co-simulation to retrieve the optimized depths for the FIFOs.
+ Attention, only the FIFOs between the layers are profiled!
+
+ Args:
+ model (ModelGraph): The model to which FIFO depth optimization is applied.
+
+ Returns:
+ Dict[str, int]: A dictionary that contains the FIFO names as keys and the optimized depths as values.
+ """
+ # channel.zip is generated after the co-simulation and contains the chan_status*.csv files
+ # in the chan_status*.csv files the max depth achieved during co-simulation can be found at the last (4th) line
+
+ if cus_hls_prj_path is None:
+ cus_hls_prj_path = model.config.get_output_dir() + '/' + model.config.get_project_name() + '/_prj/solution1'
+
+ path_to_zip_file = cus_hls_prj_path + '/.autopilot/db/channel_depth_info/'
+
+ with zipfile.ZipFile(f'{path_to_zip_file}channel.zip', 'r') as zip_ref:
+ zip_ref.extractall(path_to_zip_file)
+
+ # the channel_info.csv file contains the mapping of each fifo name (i.e layer4_out_U) to the respective
+ # chan_status*.csv file
+ names_file_path = cus_hls_prj_path + '/.autopilot/db/channel_info.csv'
+
+ csv_fifo_depth_files = {}
+ with open(names_file_path) as names_file:
+ for _ in range(4):
+ next(names_file)
+ for line in names_file:
+ layer_name = line.split(',')[1]
+ csv_file_name = line.split(',')[3][:-1]
+ csv_fifo_depth_files[layer_name] = csv_file_name
+
+ optmized_fifo_depths = {}
+ for layer_name, file_name in csv_fifo_depth_files.items():
+ with open(path_to_zip_file + file_name) as chan_status_file:
+ lines = chan_status_file.readlines()
+ optmized_fifo_depths[layer_name[:-4]] = int(
+ lines[-1]
+ ) # remove "_i_U" from the layer name string and keep the last line of the file that contains the max depth
+
+ return optmized_fifo_depths
+
+
+def execute_cosim_to_profile_fifos(model):
+ model.write()
+ model.build(
+ reset=False,
+ csim=False,
+ synth=True,
+ cosim=False,
+ validation=False,
+ export=False,
+ vsynth=False,
+ fifo_opt=True,
+ bitfile=False,
+ log_to_stdout=False,
+ )
+
+
+class FifoDepthOptimization(ConfigurableOptimizerPass, ModelOptimizerPass):
+
+ def __init__(self):
+ self.profiling_fifo_depth = 100_000
+
+ def transform(self, model):
+ """Perform FIFO depth optimization between the FIFOs of all layers to reduce resource utilization as the
+ initial FIFOs set by hls4ml might be larger than required. At the end of the optimization the FIFOs will
+ have the largest depths achieved during co-simulation without causing any deadlocks between the layers
+ (producer-consumer), thus no additional delays between the layers. In some cases, this optimization
+ might lead to bigger FIFOs than initially set by the hls4ml tool in order to prevent deadlocks.
+
+ Args:
+ model (ModelGraph): The model to which FIFO depth optimization is applied.
+
+ Raises:
+ ValueError: If the FIFO depth for profiling provided by the user is not a non-negative integer.
+ RuntimeError: If the IO type is not set to "io_stream".
+
+ Returns:
+ bool: The execution state of the Optimizer Pass
+ """
+
+ if not isinstance(self.profiling_fifo_depth, int) or self.profiling_fifo_depth <= 0:
+ raise ValueError('The FIFO depth for profiling (profiling_fifo_depth variable) must be a non-negative integer.')
+
+ # check axi-stream or io-stream
+ if not (model.config.get_config_value('IOType') == 'io_stream'):
+ raise RuntimeError('To use this optimization you have to set `IOType` field to `io_stream` in the HLS config.')
+
+ hlsPrjPath = model.config.backend.writer.mg.get_vitis_hls_exec_dir(model)
+
+ initial_fifo_depths = initialize_large_fifos(model, self.profiling_fifo_depth)
+ execute_cosim_to_profile_fifos(model)
+ optimized_fifo_depths = get_vitis_optimized_fifo_depths(model, cus_hls_prj_path=hlsPrjPath + "/hls")
+ generate_depths_file(model, initial_fifo_depths, optimized_fifo_depths)
+ set_optimized_fifo_depths(model, optimized_fifo_depths)
+
+ print('FIFO optimization completed')
+
+ return False
diff --git a/hls4ml/backends/vitis_unified/vitis_unified_backend.py b/hls4ml/backends/vitis_unified/vitis_unified_backend.py
new file mode 100644
index 0000000000..8bfed0f88a
--- /dev/null
+++ b/hls4ml/backends/vitis_unified/vitis_unified_backend.py
@@ -0,0 +1,181 @@
+import os
+import subprocess
+import sys
+from shutil import copy2
+
+from hls4ml.backends import VitisBackend, VivadoBackend
+from hls4ml.model.flow import register_flow
+from hls4ml.writer.vitis_unified_writer.meta_gen import VitisUnified_MetaGen as mg
+
+
+class VitisUnifiedBackend(VitisBackend):
+ def __init__(self):
+ super(VivadoBackend, self).__init__(name='VitisUnified')
+ self._register_layer_attributes()
+ self._register_flows()
+
+ def run_term_command(self, model, taskName: str, command: str, logStdOut: bool, cwd):
+
+ print("-------------------------------------------------------")
+ print(f"start running task : {taskName}")
+ print(f" with command: {command}")
+ print("-------------------------------------------------------")
+
+ output_dir = model.config.get_output_dir()
+
+ out_log_path = os.path.join(output_dir, f'{taskName}_out.log')
+ err_log_path = os.path.join(output_dir, f'{taskName}_err.log')
+ out_target = None if logStdOut else open(out_log_path, 'w')
+ err_target = None if logStdOut else open(err_log_path, 'w')
+
+ try:
+ runningProcess = subprocess.Popen(command, shell=True, cwd=cwd, stdout=out_target, stderr=err_target, text=True)
+ runningProcess.communicate()
+ if runningProcess.returncode != 0:
+ raise Exception(
+ f'Package failed for {taskName} for project {model.config.get_project_name()}. See logs for details.'
+ )
+
+ stdout, stderr = runningProcess.communicate()
+ print(f"stdout: {stdout}")
+ print(f"stderr: {stderr}")
+
+ print(f"task {taskName} finished")
+
+ except Exception as e:
+ print(f"task {taskName} failed")
+ print(e)
+ raise e
+ finally:
+ if out_target:
+ out_target.close()
+ if err_target:
+ err_target.close()
+
+ def build(
+ self,
+ model,
+ reset=False,
+ csim=False,
+ synth=False,
+ cosim=False,
+ validation=False,
+ export=False,
+ vsynth=False,
+ fifo_opt=False,
+ bitfile=False,
+ log_to_stdout=True,
+ ):
+ # it builds and return vivado reports
+ if 'linux' in sys.platform:
+ found = os.system('command -v vitis > /dev/null')
+ if found != 0:
+ raise Exception('Vitis installation not found. Make sure "vitis" is on PATH.')
+
+ if csim:
+ raise Exception("Current Vitis Unified not support csim. Please set csim=False to run Vitis Unified.")
+ if validation:
+ raise Exception(
+ "Current Vitis Unified not support validation. Please set validation=False to run Vitis Unified."
+ )
+ if export:
+ raise Exception("Current Vitis Unified not support export. Please set export=False to run Vitis Unified.")
+
+ output_dir = model.config.get_output_dir()
+
+ hls_config_file = os.path.join(output_dir, "hls_kernel_config.cfg")
+ # build command
+ csynth_cmd = ("v++ -c --mode hls --config {configPath} --work_dir unifiedPrj").format(configPath=hls_config_file)
+ csynth_cwd = mg.get_vitis_hls_dir(model)
+
+ # util template (used in csim/cosim/package)
+ util_command = "vitis-run --mode hls --{op} --config {configPath} --work_dir unifiedPrj"
+
+ # command for each configuration
+
+ package_cmd = util_command.format(op="package", configPath=hls_config_file)
+ package_cwd = mg.get_vitis_hls_dir(model)
+ cosim_cmd = util_command.format(op="cosim", configPath=hls_config_file)
+ cosim_cwd = mg.get_vitis_hls_dir(model)
+ csim_cmd = util_command.format(op="csim", configPath=hls_config_file)
+ csim_cwd = mg.get_vitis_hls_dir(model)
+
+ kerlink_cmd = "./buildAcc.sh"
+ kerlink_cwd = mg.get_vitis_linker_dir(model)
+
+ if synth:
+ self.prepare_sim_config_file(model, True)
+ self.run_term_command(model, "csynth", csynth_cmd, log_to_stdout, csynth_cwd)
+ self.run_term_command(model, "package", package_cmd, log_to_stdout, package_cwd)
+
+ if csim:
+ self.prepare_sim_config_file(model, True)
+ self.run_term_command(model, "csim", csim_cmd, log_to_stdout, csim_cwd)
+
+ if cosim or fifo_opt:
+ self.prepare_sim_config_file(model, False)
+ self.run_term_command(model, "cosim", cosim_cmd, log_to_stdout, cosim_cwd)
+
+ # if bitfile
+ if bitfile:
+ self.run_term_command(model, "kerlink", kerlink_cmd, log_to_stdout, kerlink_cwd)
+
+ def prepare_sim_config_file(self, model, is_csim):
+ suffix = "csim" if is_csim else "cosim"
+ src = f"{model.config.get_output_dir()}/hls_kernel_config_{suffix}.cfg"
+ des = f"{model.config.get_output_dir()}/hls_kernel_config.cfg"
+ copy2(src, des)
+ return des
+
+ def create_initial_config(
+ self,
+ board='zcu102',
+ part=None,
+ clock_period=5,
+ clock_uncertainty='12.5%',
+ io_type='io_stream',
+ driver='python',
+ input_type='float',
+ output_type='float',
+ in_stream_buf_size=128,
+ out_stream_buf_size=128,
+ xpfmPath='/opt/Xilinx/Vitis/2023.2/base_platforms/' 'xilinx_zcu102_base_202320_1/xilinx_zcu102_base_202320_1.xpfm',
+ **_,
+ ):
+
+ config = super().create_initial_config(part, clock_period, clock_uncertainty, io_type)
+
+ config['UnifiedConfig'] = {}
+ config['UnifiedConfig']["in_stream_buf_Size"] = in_stream_buf_size
+ config['UnifiedConfig']["out_stream_buf_Size"] = out_stream_buf_size
+ config['UnifiedConfig']['XPFMPath'] = xpfmPath
+ config['UnifiedConfig']['Board'] = board
+ config['UnifiedConfig']['Driver'] = driver
+ config['UnifiedConfig']['InputDtype'] = input_type # float, double or ap_fixed
+ config['UnifiedConfig']['OutputDtype'] = output_type # float, double or ap_fixed
+
+ if io_type != "io_stream":
+ raise Exception("io_type must be io_stream")
+ if input_type not in ["double", "float"]:
+ raise Exception("input_type must be float or double")
+ if output_type not in ["double", "float"]:
+ raise Exception("output_type must be float or double")
+
+ return config
+
+ def get_default_flow(self):
+ return self._default_flow
+
+ def get_writer_flow(self):
+ return self._writer_flow
+
+ def _register_flows(self):
+ vitis_ip = 'vitis:ip'
+ writer_passes = ['make_stamp', 'vitisunified:write_hls']
+ self._writer_flow = register_flow('write', writer_passes, requires=['vitis:ip'], backend=self.name)
+ self._default_flow = vitis_ip
+
+ # register fifo depth optimization
+ fifo_depth_opt_passes = ['vitisunified:fifo_depth_optimization'] + writer_passes
+
+ register_flow('fifo_depth_optimization', fifo_depth_opt_passes, requires=['vitis:ip'], backend=self.name)
diff --git a/hls4ml/backends/vitis_unified/vitis_unified_config.py b/hls4ml/backends/vitis_unified/vitis_unified_config.py
new file mode 100644
index 0000000000..8f3289931e
--- /dev/null
+++ b/hls4ml/backends/vitis_unified/vitis_unified_config.py
@@ -0,0 +1,49 @@
+class VitisUnifiedConfig:
+
+ def __init__(self, config, model_inputs, model_outputs):
+ self.config = config.config
+ self.board = self.config.get('UnifiedConfig', {}).get('Board', 'pynq-z2')
+
+ # before first and afterlast layer we have the configuratble buffer
+ # [platform]<-->[in_steram_bufferSz]<-->[hls]<-->[out_stream_bufferSz]<-->[platform]
+ self.in_steram_bufferSz = self.config["UnifiedConfig"]["in_stream_buf_Size"]
+ self.out_stream_bufferSz = self.config["UnifiedConfig"]["out_stream_buf_Size"]
+
+ # the path to the generated platform
+ self.XPFMPath = self.config["UnifiedConfig"]["XPFMPath"]
+
+ self.driver = self.config['UnifiedConfig']['Driver']
+
+ # c++ type for input and output of the hls kernel it must be str (float/double)
+ self.input_type = self.config['UnifiedConfig']['InputDtype']
+ self.output_type = self.config['UnifiedConfig']['OutputDtype']
+
+ assert self.input_type == self.output_type, "Input and Output data types must be the same type different"
+ assert len(model_inputs) >= 1, "Only models with at least one input tensor are currently supported by VitisUnified"
+ assert len(model_outputs) >= 1, "Only models with one output tensor are currently supported by VitisUnified"
+ self.inps = model_inputs.copy()
+ self.outs = model_outputs.copy()
+
+ def get_corrected_types(self):
+ return self.input_type, self.output_type, self.inps, self.outs
+
+ def get_driver(self):
+ return self.driver
+
+ def get_board(self):
+ return self.board
+
+ def get_input_type(self):
+ return self.input_type
+
+ def get_output_type(self):
+ return self.output_type
+
+ def get_in_stream_bufferSz(self):
+ return self.in_steram_bufferSz
+
+ def get_out_stream_bufferSz(self):
+ return self.out_stream_bufferSz
+
+ def get_XPFMPath(self):
+ return self.XPFMPath
diff --git a/hls4ml/templates/vitis_unified/build_lib.sh b/hls4ml/templates/vitis_unified/build_lib.sh
new file mode 100644
index 0000000000..2645804f90
--- /dev/null
+++ b/hls4ml/templates/vitis_unified/build_lib.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+CC=g++
+if [[ "$OSTYPE" == "linux-gnu" ]]; then
+ CFLAGS="-O3 -fPIC -std=c++11 -fno-gnu-unique"
+elif [[ "$OSTYPE" == "darwin"* ]]; then
+ CFLAGS="-O3 -fPIC -std=c++11"
+fi
+VITIS_UNIFIED_FLAGS="VITIS_UNIFIED"
+CFLAGS="$CFLAGS -D$VITIS_UNIFIED_FLAGS"
+
+INCFLAGS="-Ifirmware/ap_types/"
+
+PROJECT=myprojectBaseName
+WRAPPER_NAME=myprojectWrapName
+LIB_STAMP=mystamp
+BASEDIR="$(cd "$(dirname "$0")" && pwd)"
+WEIGHTS_DIR="\"${BASEDIR}/firmware/weights\""
+
+echo "------------- This is build_lib.sh debug message ----------------"
+echo "Compiling for OSTYPE: $OSTYPE"
+echo "CFLAGS: $CFLAGS"
+echo "Include Flags: $INCFLAGS"
+echo "Weights directory: $WEIGHTS_DIR"
+echo "-----------------------------------------------------------------"
+
+${CC} ${CFLAGS} ${INCFLAGS} -D WEIGHTS_DIR="${WEIGHTS_DIR}" -c firmware/${PROJECT}.cpp -o ${PROJECT}.o
+${CC} ${CFLAGS} ${INCFLAGS} -D WEIGHTS_DIR="${WEIGHTS_DIR}" -c firmware/${WRAPPER_NAME}.cpp -o ${WRAPPER_NAME}.o
+${CC} ${CFLAGS} ${INCFLAGS} -D WEIGHTS_DIR="${WEIGHTS_DIR}" -c ${PROJECT}_bridge.cpp -o ${PROJECT}_bridge.o
+${CC} ${CFLAGS} ${INCFLAGS} -shared ${PROJECT}.o ${WRAPPER_NAME}.o ${PROJECT}_bridge.o -o firmware/${PROJECT}-${LIB_STAMP}.so
+rm -f *.o
diff --git a/hls4ml/templates/vitis_unified/build_lib_multigraph.sh b/hls4ml/templates/vitis_unified/build_lib_multigraph.sh
new file mode 100644
index 0000000000..9dcd85f7d1
--- /dev/null
+++ b/hls4ml/templates/vitis_unified/build_lib_multigraph.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+set -e
+
+CC=g++
+if [[ "$OSTYPE" == "linux-gnu" ]]; then
+ CFLAGS="-O3 -fPIC -std=c++11 -fno-gnu-unique"
+elif [[ "$OSTYPE" == "darwin"* ]]; then
+ CFLAGS="-O3 -fPIC -std=c++11"
+fi
+
+graph_project_names=(mygraph_name_list)
+
+LDFLAGS=
+VITIS_UNIFIED_FLAGS="VITIS_UNIFIED"
+CFLAGS="$CFLAGS -D$VITIS_UNIFIED_FLAGS"
+
+ORIGINAL_PROJECT=myproject
+PROJECT=myproject_stitched
+LIB_STAMP=mystamp
+BASEDIR="$(cd "$(dirname "$0")" && cd .. && pwd)"
+INCFLAGS=""
+OUTPUT_DIR="${BASEDIR}/stitched/firmware"
+WEIGHTS_DIR="\"${BASEDIR}/stitched/firmware/weights\""
+
+mkdir -p "${OUTPUT_DIR}"
+
+# Compile all graphs in parallel
+OBJECT_FILES=()
+PIDS=()
+
+for g in "${graph_project_names[@]}"; do
+ SRC_FILE="${g}/firmware/${ORIGINAL_PROJECT}_${g}.cpp"
+ OBJ_FILE="${ORIGINAL_PROJECT}_${g}.o"
+ AP_TYPES_PATH="-I${BASEDIR}/${g}/firmware/ap_types/"
+ (
+ ${CC} ${CFLAGS} ${AP_TYPES_PATH} -D WEIGHTS_DIR="${WEIGHTS_DIR}" -c "${BASEDIR}/${SRC_FILE}" -o "${OBJ_FILE}"
+ ) &
+ PIDS+=($!)
+ OBJECT_FILES+=("${OBJ_FILE}")
+ INCFLAGS+="-I${BASEDIR}/${g}/ "
+done
+
+# compile axi_stream as well
+
+for g in "${graph_project_names[@]}"; do
+ SRC_FILE="${g}/firmware/${ORIGINAL_PROJECT}_${g}_axi.cpp"
+ OBJ_FILE="${ORIGINAL_PROJECT}_${g}_axi.o"
+ AP_TYPES_PATH="-I${BASEDIR}/${g}/firmware/ap_types/"
+ (
+ ${CC} ${CFLAGS} ${AP_TYPES_PATH} -D WEIGHTS_DIR="${WEIGHTS_DIR}" -c "${BASEDIR}/${SRC_FILE}" -o "${OBJ_FILE}"
+ ) &
+ PIDS+=($!)
+ OBJECT_FILES+=("${OBJ_FILE}")
+ #INCFLAGS+="-I${BASEDIR}/${g}/ "
+done
+
+
+
+for pid in "${PIDS[@]}"; do
+ wait $pid
+done
+
+AP_TYPES_PATH="-I${BASEDIR}/${graph_project_names[@]: -1}/firmware/ap_types/"
+
+${CC} ${CFLAGS} ${INCFLAGS} ${AP_TYPES_PATH} -c "${PROJECT}_bridge.cpp" -o ${PROJECT}_bridge.o
+${CC} ${CFLAGS} ${INCFLAGS} ${AP_TYPES_PATH} -shared "${OBJECT_FILES[@]}" ${PROJECT}_bridge.o -o "${OUTPUT_DIR}/${PROJECT}-${LIB_STAMP}.so"
+
+rm -f "${OBJECT_FILES[@]}"
+rm -f ${PROJECT}_bridge.o
diff --git a/hls4ml/templates/vitis_unified/driver/pynq/pynq_driver.py.hls4ml b/hls4ml/templates/vitis_unified/driver/pynq/pynq_driver.py.hls4ml
new file mode 100644
index 0000000000..e8c1d0de5c
--- /dev/null
+++ b/hls4ml/templates/vitis_unified/driver/pynq/pynq_driver.py.hls4ml
@@ -0,0 +1,98 @@
+# import the library
+import os
+import re
+import subprocess
+import time
+
+import numpy as np
+from pynq import DefaultIP # import the ip connector library for extension
+from pynq import Overlay # import the overlay
+from pynq import allocate # import for CMA (contingeous memory allocation)
+
+
+class MyDfxCtrl(DefaultIP):
+ def __init__(self, description):
+ super().__init__(description=description)
+
+ self.REG_ADDR_AP_CTRL = 0x00
+ self.REG_ADDR_AMT_QUERY = VAL
+
+ self.REG_ADDR_GIE = 0x04
+ self.REG_ADDR_IER = 0x08
+ self.REG_ADDR_ISR = 0x0C
+
+ self.INP_PORT_NAMEs = [
+ # hls-driver-input-dbg-name
+ ]
+
+ self.REG_ADDR_INP_PTRs = [
+ # hls-driver-input-ptr
+ ]
+
+ self.OUT_PORT_NAMEs = [
+ # hls-driver-output-dbg-name
+ ]
+
+ self.REG_ADDR_OUT_PTRs = [
+ # hls-driver-output-ptr
+ ]
+
+ bindto = ['xilinx.com:hls::1.0']
+
+ def enable_gie(self):
+ print("global interrupt enable register")
+ self.write(self.REG_ADDR_GIE, 0x01)
+ print("enable gie successful")
+
+ def disable_gie(self):
+ print("global interrupt enable register")
+ self.write(self.REG_ADDR_GIE, 0x01)
+ print("disable gie successful")
+
+ def enable_done_intr(self):
+ print("ap_done interrupt enable register")
+ self.write(self.REG_ADDR_IER, 0x01)
+ print("enable ap_done interrupt successful")
+
+ def clear_done_status(self):
+ print("ap_done register clear")
+ self.write(self.REG_ADDR_ISR, 0x01)
+ print("clear ap_done interrupt successful")
+
+ def prepare_intr(self):
+ print("prepare your interrupt")
+ self.enable_gie()
+ self.enable_done_intr()
+ self.clear_done_status()
+ print("----------------------")
+
+ def set_single_bit(self, addr, idx):
+ self.write(addr, 1 << idx)
+
+ def ctrl_start(self):
+ self.write(0x00, 0x01) # ap_start = 1
+
+ def wait_until_done(self):
+ while (self.read(0x00) & 0x2) == 0: # Wait for ap_done
+ time.sleep(0.001)
+
+ def set_input(self, idx, buffer):
+
+ print(
+ f"input {self.INP_PORT_NAMEs[idx]} will be set to addr: {hex(buffer.physical_address)} with elements: {buffer.size}"
+ )
+ self.write(self.REG_ADDR_INP_PTRs[idx], buffer.physical_address)
+ self.write(self.REG_ADDR_INP_PTRs[idx] + 4, 0)
+ buffer.flush()
+
+ def set_output(self, idx, buffer):
+
+ print(
+ f"output {self.OUT_PORT_NAMEs[idx]} will be set to addr: {hex(buffer.physical_address)} with elements: {buffer.size}"
+ )
+ self.write(self.REG_ADDR_OUT_PTRs[idx], buffer.physical_address)
+ self.write(self.REG_ADDR_OUT_PTRs[idx] + 4, 0)
+
+ def set_amt_query(self, val):
+ print(f"amount of queries will be set to: {val} at address: {hex(self.REG_ADDR_AMT_QUERY)}")
+ self.write(self.REG_ADDR_AMT_QUERY, val)
diff --git a/hls4ml/templates/vitis_unified/hls_kernel_config.cfg b/hls4ml/templates/vitis_unified/hls_kernel_config.cfg
new file mode 100644
index 0000000000..c1d12a0c18
--- /dev/null
+++ b/hls4ml/templates/vitis_unified/hls_kernel_config.cfg
@@ -0,0 +1,24 @@
+part={PART}
+
+[hls]
+clock={CLK}
+clock_uncertainty={CLK_UC}
+flow_target=vivado
+syn.file={OUTDIR}/firmware/{FILE_NAME_WRAP}.cpp
+syn.file={OUTDIR}/firmware/{FILE_NAME_BASE}.cpp
+syn.file_cflags={OUTDIR}/firmware/{FILE_NAME_WRAP}.cpp,-std=c++0x
+syn.file_cflags={OUTDIR}/firmware/{FILE_NAME_BASE}.cpp,-std=c++0x
+
+syn.top={TOP_NAME}
+
+tb.file={OUTDIR}/{SIM_FILE_NAME}.cpp
+tb.file={OUTDIR}/firmware/weights
+tb.file={OUTDIR}/tb_data
+tb.file_cflags={OUTDIR}/{SIM_FILE_NAME}.cpp,-std=c++0x
+tb.file_cflags={OUTDIR}/{SIM_FILE_NAME}.cpp,-DRTL_SIM
+package.ip.version=1.0.0
+package.output.format={OUTPUT_KERNEL_TYPE}
+syn.compile.name_max_length=80
+syn.schedule.enable_dsp_full_reg=0
+package.output.syn=1
+cosim.enable_fifo_sizing=true
diff --git a/hls4ml/templates/vitis_unified/myproject_bridge.cpp b/hls4ml/templates/vitis_unified/myproject_bridge.cpp
new file mode 100644
index 0000000000..9a56f10d99
--- /dev/null
+++ b/hls4ml/templates/vitis_unified/myproject_bridge.cpp
@@ -0,0 +1,71 @@
+#ifndef MYPROJECT_BRIDGE_H_
+#define MYPROJECT_BRIDGE_H_
+
+#include "firmware/PROJECT_FILE_NAME.h"
+#include "firmware/nnet_utils/nnet_helpers.h"
+#include
+#include