diff --git a/hls4ml/backends/symbolic/symbolic_backend.py b/hls4ml/backends/symbolic/symbolic_backend.py index bad75c2417..20fb60bfda 100644 --- a/hls4ml/backends/symbolic/symbolic_backend.py +++ b/hls4ml/backends/symbolic/symbolic_backend.py @@ -90,23 +90,49 @@ def create_initial_config( return config def build(self, model, reset=False, csim=True, synth=True, cosim=False, validation=False, export=False, vsynth=False): - if 'linux' in sys.platform: - found = os.system('command -v vivado_hls > /dev/null') - if found != 0: - raise Exception('Vivado HLS installation not found. Make sure "vivado_hls" is on PATH.') + compiler = model.config.get_config_value('Compiler') curr_dir = os.getcwd() os.chdir(model.config.get_output_dir()) - vivado_cmd = ( - f'vivado_hls -f build_prj.tcl "reset={reset} ' - f'csim={csim} ' - f'synth={synth} ' - f'cosim={cosim} ' - f'validation={validation} ' - f'export={export} ' - f'vsynth={vsynth}"' - ) - os.system(vivado_cmd) + + if compiler == 'vitis_hls': + if 'linux' in sys.platform: + found = os.system('command -v vitis-run > /dev/null') == 0 + if not found: + raise Exception('Vitis HLS installation not found. Make sure "vitis-run" is on PATH.') + + build_opts = ( + 'array set opt {\n' + f' reset {int(reset)}\n' + f' csim {int(csim)}\n' + f' synth {int(synth)}\n' + f' cosim {int(cosim)}\n' + f' validation {int(validation)}\n' + f' export {int(export)}\n' + f' vsynth {int(vsynth)}\n' + f' fifo_opt 0\n' + '}\n' + ) + with open('build_opt.tcl', 'w') as f: + f.write(build_opts) + os.system('vitis-run --tcl build_prj.tcl --mode hls') + else: + if 'linux' in sys.platform: + found = os.system('command -v vivado_hls > /dev/null') + if found != 0: + raise Exception('Vivado HLS installation not found. Make sure "vivado_hls" is on PATH.') + + vivado_cmd = ( + f'vivado_hls -f build_prj.tcl "reset={reset} ' + f'csim={csim} ' + f'synth={synth} ' + f'cosim={cosim} ' + f'validation={validation} ' + f'export={export} ' + f'vsynth={vsynth}"' + ) + os.system(vivado_cmd) + os.chdir(curr_dir) return parse_vivado_report(model.config.get_output_dir()) diff --git a/hls4ml/converters/__init__.py b/hls4ml/converters/__init__.py index 203e31a668..ae4ca0175e 100644 --- a/hls4ml/converters/__init__.py +++ b/hls4ml/converters/__init__.py @@ -399,6 +399,7 @@ def convert_from_symbolic_expression( input_data_tb=None, output_data_tb=None, precision='ap_fixed<16,6>', + hls_compiler='vivado_hls', **kwargs, ): """Converts a given (SymPy or string) expression to hls4ml model. @@ -438,17 +439,24 @@ def convert_from_symbolic_expression( part (str, optional): The FPGA part. If set to `None` a default part of a backend will be used. clock_period (int, optional): Clock period of the design. Defaults to 5. - compiler (str, optional): Compiler to use, ``vivado_hls`` or ``vitis_hls``. Defaults to ``vivado_hls``. - hls_include_path (str, optional): Path to HLS inlcude files. If `None` the location will be inferred from the - location of the `compiler` used. If an empty string is passed the HLS math libraries won't be used during + hls_compiler (str, optional): HLS compiler to use. Must be ``'vivado_hls'`` or ``'vitis_hls'`. Defaults to ``'vivado_hls'``. + hls_include_path (str, optional): Path to HLS include files. If `None` the location will be inferred from the + location of the compiler. If an empty string is passed the HLS math libraries won't be used during compilation, meaning Python integration won't work unless all functions are LUT-based. Doesn't affect synthesis. Defaults to None. hls_libs_path (str, optional): Path to HLS libs files. If `None` the location will be inferred from the - location of the `compiler` used. Defaults to None. + location of the compiler. Defaults to None. Returns: ModelGraph: hls4ml model. """ + _valid_compilers = ('vivado_hls', 'vitis_hls') + if hls_compiler not in _valid_compilers: + raise ValueError(f"hls_compiler must be one of {_valid_compilers}, got '{hls_compiler}'") + + # Remove legacy 'compiler' kwarg if passed to avoid duplicate keyword argument + kwargs.pop('compiler', None) + import sympy if not isinstance(expr, (list, set)): @@ -490,7 +498,9 @@ def convert_from_symbolic_expression( expr_layer['use_built_in_luts'] = use_built_in_lut_functions layer_list.append(expr_layer) - config = create_config(output_dir=output_dir, project_name=project_name, backend='SymbolicExpression', **kwargs) + config = create_config( + output_dir=output_dir, project_name=project_name, backend='SymbolicExpression', compiler=hls_compiler, **kwargs + ) # config['Expression'] = str(expr) config['NSymbols'] = n_symbols diff --git a/hls4ml/writer/symbolic_writer.py b/hls4ml/writer/symbolic_writer.py index 0ade2d46ca..cd7e0eb0de 100644 --- a/hls4ml/writer/symbolic_writer.py +++ b/hls4ml/writer/symbolic_writer.py @@ -59,6 +59,7 @@ def write_build_script(self, model): """ filedir = Path(__file__).parent + compiler = model.config.get_config_value('Compiler') # project.tcl prj_tcl_dst = Path(f'{model.config.get_output_dir()}/project.tcl') @@ -77,16 +78,26 @@ def write_build_script(self, model): f.write('set version "{}"\n'.format(model.config.get_config_value('Version', '1.0.0'))) # build_prj.tcl - srcpath = (filedir / '../templates/vivado/build_prj.tcl').resolve() + if compiler == 'vitis_hls': + srcpath = (filedir / '../templates/vitis/build_prj.tcl').resolve() + else: + srcpath = (filedir / '../templates/vivado/build_prj.tcl').resolve() dstpath = f'{model.config.get_output_dir()}/build_prj.tcl' copyfile(srcpath, dstpath) + # build_opt.tcl — required by the vitis build_prj.tcl, since vitis-run doesn't accept TCL arguments + if compiler == 'vitis_hls': + srcpath = (filedir / '../templates/vitis/build_opt.tcl').resolve() + dstpath = f'{model.config.get_output_dir()}/build_opt.tcl' + copyfile(str(srcpath), dstpath) + # vivado_synth.tcl srcpath = (filedir / '../templates/vivado/vivado_synth.tcl').resolve() dstpath = f'{model.config.get_output_dir()}/vivado_synth.tcl' copyfile(srcpath, dstpath) # build_lib.sh + # Vitis HLS 2022.2+ ships fpo_v7_1; Vivado HLS ships fpo_v7_0. build_lib_src = (filedir / '../templates/symbolic/build_lib.sh').resolve() build_lib_dst = Path(f'{model.config.get_output_dir()}/build_lib.sh').resolve() with open(build_lib_src) as src, open(build_lib_dst, 'w') as dst: @@ -95,6 +106,10 @@ def write_build_script(self, model): line = line.replace('mystamp', model.config.get_config_value('Stamp')) line = line.replace('mylibspath', model.config.get_config_value('HLSLibsPath')) + if compiler == 'vitis_hls': + line = line.replace('fpo_v7_0', 'fpo_v7_1') + line = line.replace('Ip_floating_point_v7_0', 'Ip_floating_point_v7_1') + if 'LDFLAGS=' in line and not os.path.exists(model.config.get_config_value('HLSLibsPath')): line = 'LDFLAGS=\n'