Skip to content

Commit 8efbf6f

Browse files
authored
Add compatibility with new Vitis command-line compilation flow (#1327)
* Support new Vitis compilation flow * Ditch build via vitis_hls
1 parent 009bcf5 commit 8efbf6f

File tree

4 files changed

+281
-17
lines changed

4 files changed

+281
-17
lines changed

hls4ml/backends/vitis/vitis_backend.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import shutil
55
import subprocess
66
import sys
7+
from pathlib import Path
78

89
from hls4ml.backends import VivadoBackend
910
from hls4ml.model.flow import get_flow, register_flow
@@ -111,24 +112,29 @@ def build(
111112
log_to_stdout=True,
112113
):
113114
if 'linux' in sys.platform:
114-
found = os.system('command -v vitis_hls > /dev/null')
115-
if found != 0:
116-
raise Exception('Vitis HLS installation not found. Make sure "vitis_hls" is on PATH.')
117-
118-
build_command = (
119-
'vitis_hls -f build_prj.tcl "reset={reset} csim={csim} synth={synth} cosim={cosim} '
120-
'validation={validation} export={export} vsynth={vsynth} fifo_opt={fifo_opt}"'
121-
).format(
122-
reset=reset,
123-
csim=csim,
124-
synth=synth,
125-
cosim=cosim,
126-
validation=validation,
127-
export=export,
128-
vsynth=vsynth,
129-
fifo_opt=fifo_opt,
115+
found_vrun = os.system('command -v vitis-run > /dev/null') == 0
116+
if not found_vrun:
117+
raise Exception('Vitis installation not found. Make sure "vitis-run" is on PATH.')
118+
119+
build_opts = (
120+
'array set opt {\n'
121+
f' reset {int(reset)}\n'
122+
f' csim {int(csim)}\n'
123+
f' synth {int(synth)}\n'
124+
f' cosim {int(cosim)}\n'
125+
f' validation {int(validation)}\n'
126+
f' export {int(export)}\n'
127+
f' vsynth {int(vsynth)}\n'
128+
f' fifo_opt {int(fifo_opt)}\n'
129+
'}\n'
130130
)
131131

132+
tcl_path = Path(model.config.get_output_dir()) / 'build_opt.tcl'
133+
with open(tcl_path, 'w') as file:
134+
file.write(build_opts)
135+
136+
build_command = 'vitis-run --tcl build_prj.tcl'
137+
132138
output_dir = model.config.get_output_dir()
133139
stdout_log = os.path.join(output_dir, 'build_stdout.log')
134140
stderr_log = os.path.join(output_dir, 'build_stderr.log')

hls4ml/templates/vitis/build_opt.tcl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
array set opt {
2+
reset 0
3+
csim 1
4+
synth 1
5+
cosim 1
6+
validation 1
7+
export 0
8+
vsynth 0
9+
fifo_opt 0
10+
}

hls4ml/templates/vitis/build_prj.tcl

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
#################
2+
# HLS4ML
3+
#################
4+
5+
set tcldir [file dirname [info script]]
6+
source [file join $tcldir project.tcl]
7+
source [file join $tcldir build_opt.tcl]
8+
9+
proc remove_recursive_log_wave {} {
10+
set tcldir [file dirname [info script]]
11+
source [file join $tcldir project.tcl]
12+
13+
set filename ${project_name}_prj/solution1/sim/verilog/${project_name}.tcl
14+
set timestamp [clock format [clock seconds] -format {%Y%m%d%H%M%S}]
15+
set temp $filename.new.$timestamp
16+
# set backup $filename.bak.$timestamp
17+
18+
set in [open $filename r]
19+
set out [open $temp w]
20+
21+
# line-by-line, read the original file
22+
while {[gets $in line] != -1} {
23+
if {[string equal "$line" "log_wave -r /"]} {
24+
set line { }
25+
}
26+
puts $out $line
27+
}
28+
29+
close $in
30+
close $out
31+
32+
# move the new data to the proper filename
33+
file delete -force $filename
34+
file rename -force $temp $filename
35+
}
36+
37+
proc add_vcd_instructions_tcl {} {
38+
set tcldir [file dirname [info script]]
39+
source [file join $tcldir project.tcl]
40+
41+
set filename ${project_name}_prj/solution1/sim/verilog/${project_name}.tcl
42+
set timestamp [clock format [clock seconds] -format {%Y%m%d%H%M%S}]
43+
set temp $filename.new.$timestamp
44+
# set backup $filename.bak.$timestamp
45+
46+
set in [open $filename r]
47+
set out [open $temp w]
48+
49+
# line-by-line, read the original file
50+
while {[gets $in line] != -1} {
51+
if {[string equal "$line" "log_wave -r /"]} {
52+
set line {source "../../../../project.tcl"
53+
if {[string equal "$backend" "vivadoaccelerator"]} {
54+
current_scope [get_scopes -regex "/apatb_${project_name}_axi_top/AESL_inst_${project_name}_axi/${project_name}_U0.*"]
55+
set scopes [get_scopes -regexp {layer(\d*)_.*data_0_V_U.*}]
56+
append scopes { }
57+
current_scope "/apatb_${project_name}_axi_top/AESL_inst_${project_name}_axi"
58+
append scopes [get_scopes -regexp {(in_local_V_data.*_0_.*)}]
59+
append scopes { }
60+
append scopes [get_scopes -regexp {(out_local_V_data.*_0_.*)}]
61+
} else {
62+
current_scope [get_scopes -regex "/apatb_${project_name}_top/AESL_inst_${project_name}"]
63+
set scopes [get_scopes -regexp {layer(\d*)_.*data_0_V_U.*}]
64+
}
65+
open_vcd fifo_opt.vcd
66+
foreach scope $scopes {
67+
current_scope $scope
68+
if {[catch [get_objects usedw]] == 0} {
69+
puts "$scope skipped"
70+
continue
71+
}
72+
set usedw [get_objects usedw]
73+
set depth [get_objects DEPTH]
74+
add_wave $usedw
75+
log_vcd $usedw
76+
log_wave $usedw
77+
add_wave $depth
78+
log_vcd $depth
79+
log_wave $depth
80+
}
81+
}
82+
}
83+
84+
if {[string equal "$line" "quit"]} {
85+
set line {flush_vcd
86+
close_vcd
87+
quit
88+
}
89+
}
90+
# then write the transformed line
91+
puts $out $line
92+
}
93+
94+
close $in
95+
close $out
96+
97+
# move the new data to the proper filename
98+
file delete -force $filename
99+
file rename -force $temp $filename
100+
}
101+
102+
proc report_time { op_name time_start time_end } {
103+
set time_taken [expr $time_end - $time_start]
104+
set time_s [expr ($time_taken / 1000) % 60]
105+
set time_m [expr ($time_taken / (1000*60)) % 60]
106+
set time_h [expr ($time_taken / (1000*60*60)) % 24]
107+
puts "***** ${op_name} COMPLETED IN ${time_h}h${time_m}m${time_s}s *****"
108+
}
109+
110+
# Compare file content: 1 = same, 0 = different
111+
proc compare_files {file_1 file_2} {
112+
# Check if files exist, error otherwise
113+
if {! ([file exists $file_1] && [file exists $file_2])} {
114+
return 0
115+
}
116+
# Files with different sizes are obviously different
117+
if {[file size $file_1] != [file size $file_2]} {
118+
return 0
119+
}
120+
121+
# String compare the content of the files
122+
set fh_1 [open $file_1 r]
123+
set fh_2 [open $file_2 r]
124+
set equal [string equal [read $fh_1] [read $fh_2]]
125+
close $fh_1
126+
close $fh_2
127+
return $equal
128+
}
129+
130+
file mkdir tb_data
131+
set CSIM_RESULTS "./tb_data/csim_results.log"
132+
set RTL_COSIM_RESULTS "./tb_data/rtl_cosim_results.log"
133+
134+
if {$opt(reset)} {
135+
open_project -reset ${project_name}_prj
136+
} else {
137+
open_project ${project_name}_prj
138+
}
139+
set_top ${project_name}
140+
add_files firmware/${project_name}.cpp -cflags "-std=c++0x"
141+
add_files -tb ${project_name}_test.cpp -cflags "-std=c++0x"
142+
add_files -tb firmware/weights
143+
add_files -tb tb_data
144+
if {$opt(reset)} {
145+
open_solution -reset "solution1"
146+
} else {
147+
open_solution "solution1"
148+
}
149+
catch {config_array_partition -maximum_size $maximum_size}
150+
config_compile -name_max_length 80
151+
set_part $part
152+
config_schedule -enable_dsp_full_reg=false
153+
create_clock -period $clock_period -name default
154+
set_clock_uncertainty $clock_uncertainty default
155+
156+
157+
if {$opt(csim)} {
158+
puts "***** C SIMULATION *****"
159+
set time_start [clock clicks -milliseconds]
160+
csim_design
161+
set time_end [clock clicks -milliseconds]
162+
report_time "C SIMULATION" $time_start $time_end
163+
}
164+
165+
if {$opt(synth)} {
166+
puts "***** C/RTL SYNTHESIS *****"
167+
168+
set time_start [clock clicks -milliseconds]
169+
csynth_design
170+
set time_end [clock clicks -milliseconds]
171+
report_time "C/RTL SYNTHESIS" $time_start $time_end
172+
}
173+
174+
if {$opt(cosim)} {
175+
puts "***** C/RTL SIMULATION *****"
176+
# TODO: This is a workaround (Xilinx defines __RTL_SIMULATION__ only for SystemC testbenches).
177+
add_files -tb ${project_name}_test.cpp -cflags "-std=c++0x -DRTL_SIM"
178+
set time_start [clock clicks -milliseconds]
179+
180+
cosim_design -trace_level all -setup
181+
182+
if {$opt(fifo_opt)} {
183+
puts "\[hls4ml\] - FIFO optimization started"
184+
185+
if {[string equal "$backend" "vivado"] || [string equal $backend "vivadoaccelerator"]} {
186+
add_vcd_instructions_tcl
187+
}
188+
}
189+
190+
remove_recursive_log_wave
191+
set old_pwd [pwd]
192+
cd ${project_name}_prj/solution1/sim/verilog/
193+
source run_sim.tcl
194+
cd $old_pwd
195+
196+
set time_end [clock clicks -milliseconds]
197+
puts "INFO:"
198+
if {[string equal "$backend" "vivadoaccelerator"]} {
199+
puts [read [open ${project_name}_prj/solution1/sim/report/${project_name}_axi_cosim.rpt r]]
200+
} else {
201+
puts [read [open ${project_name}_prj/solution1/sim/report/${project_name}_cosim.rpt r]]
202+
}
203+
report_time "C/RTL SIMULATION" $time_start $time_end
204+
}
205+
206+
if {$opt(validation)} {
207+
puts "***** C/RTL VALIDATION *****"
208+
if {[compare_files $CSIM_RESULTS $RTL_COSIM_RESULTS]} {
209+
puts "INFO: Test PASSED"
210+
} else {
211+
puts "ERROR: Test failed"
212+
puts "ERROR: - csim log: $CSIM_RESULTS"
213+
puts "ERROR: - RTL-cosim log: $RTL_COSIM_RESULTS"
214+
exit 1
215+
}
216+
}
217+
218+
if {$opt(export)} {
219+
puts "***** EXPORT IP *****"
220+
set time_start [clock clicks -milliseconds]
221+
export_design -format ip_catalog -version $version
222+
set time_end [clock clicks -milliseconds]
223+
report_time "EXPORT IP" $time_start $time_end
224+
}
225+
226+
if {$opt(vsynth)} {
227+
puts "***** VIVADO SYNTHESIS *****"
228+
if {[file exist ${project_name}_prj/solution1/syn/verilog]} {
229+
set time_start [clock clicks -milliseconds]
230+
exec vivado -mode batch -source vivado_synth.tcl >@ stdout
231+
set time_end [clock clicks -milliseconds]
232+
report_time "VIVADO SYNTHESIS" $time_start $time_end
233+
} else {
234+
puts "ERROR: Cannot find generated Verilog files. Did you run C synthesis?"
235+
exit 1
236+
}
237+
}
238+
239+
exit

hls4ml/writer/vitis_writer.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import glob
22
import os
33
from pathlib import Path
4-
from shutil import copy
4+
from shutil import copy, copyfile
55

66
from hls4ml.writer.vivado_writer import VivadoWriter
77

@@ -48,11 +48,20 @@ def write_board_script_override(self, model):
4848
with open(prj_tcl_file, 'w') as f:
4949
f.writelines(prj_tcl_contents)
5050

51+
def write_build_opts(self, model):
52+
filedir = Path(__file__).parent
53+
54+
# build_opt.tcl
55+
srcpath = (filedir / '../templates/vitis/build_opt.tcl').resolve()
56+
dstpath = f'{model.config.get_output_dir()}/build_opt.tcl'
57+
copyfile(srcpath, dstpath)
58+
5159
def write_hls(self, model):
5260
"""
5361
Write the HLS project. Calls the steps from VivadoWriter, adapted for Vitis
5462
"""
5563
super().write_hls(model)
5664
self.write_nnet_utils_overrides(model)
5765
self.write_board_script_override(model)
66+
self.write_build_opts(model)
5867
self.write_tar(model)

0 commit comments

Comments
 (0)