Skip to content
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

FIFO depth optimization #509

Merged
merged 16 commits into from
Jul 21, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
102 changes: 102 additions & 0 deletions hls4ml/backends/vivado/passes/fifo_depth_optimization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import json

from pyDigitalWaveTools.vcd.parser import VcdParser
thesps marked this conversation as resolved.
Show resolved Hide resolved

import hls4ml
from hls4ml.model.optimizer.optimizer import ConfigurableOptimizerPass, ModelOptimizerPass


class FifoDepthOptimization(ConfigurableOptimizerPass, ModelOptimizerPass):
def __init__(self):
self.values = []

def match(self, node):
return True

def _populate_values(self, name, data, depth):
self.values.append({'name': name, 'data': [], 'max': 0, 'depth': 0})
get_values = lambda x: int(x[1][1:], 2)
self.values[-1]['data'] = [get_values(x) for x in data]
self.values[-1]['max'] = max(self.values[-1]['data'])
self.values[-1]['depth'] = int(depth[0][1][1:], 2)

def transform(self, model):
model.fifo_opt = True
init_large_fifo = getattr(self, 'init_large_fifo', True)
# cfg = model.config.config.copy()
# hls_config = cfg['HLSConfig']
thesps marked this conversation as resolved.
Show resolved Hide resolved

# check axi-stream or io-stream, if not one the 2 exit
if not(model.config.get_config_value('IOType') == 'io_stream' or
model.config.get_config_value('AcceleratorConfig')['Interface'] == 'axi_stream' or
model.config.get_config_value('AcceleratorConfig')['Interface'] == 'axi_master'):
raise Exception('To use this optimization you have to set `IOType` field to `io_stream` in the HLS config '
'or `axi_stream` or `axi_master` in `AcceleratorConfig` interface field')

# initialize all the fifos to 10000 so that they will be automatically implemented in BRAMs and so they will be
# profiled

if init_large_fifo:

for k, v in model.output_vars.items():
if model.config.get_config_value('Backend') == 'Vivado' and (v == model.get_input_variables()[0] or v == model.get_output_variables()[0]):
continue
v.pragma = (v.pragma[0], 100000)
thesps marked this conversation as resolved.
Show resolved Hide resolved

model.write()
model.build(reset=False, csim=True, synth=True, cosim=True, validation=False, export=False, vsynth=False)

with open(
model.config.get_output_dir() + '/' + model.config.get_project_name() + '_prj' + '/solution1/sim/verilog/fifo_opt.vcd') as vcd_file:
vcd = VcdParser()
vcd.parse(vcd_file)
data = vcd.scope.toJson()

# wrapper fifos - useful only with VivadoAccelerator backend
if model.config.get_config_value('Backend') == 'VivadoAccelerator':
thesps marked this conversation as resolved.
Show resolved Hide resolved
for i in range(1, len(data['children'][0]['children'][0]['children'])):
# wrapper fifos
self._populate_values(data['children'][0]['children'][0]['children'][i]['name'],
data['children'][0]['children'][0]['children'][i]['children'][0]['data'],
data['children'][0]['children'][0]['children'][i]['children'][1]['data'])

n_elem = len(data['children'][0]['children'][0]['children'][0]['children'])
for i in range(n_elem):
name = data['children'][0]['children'][0]['children'][0]['children'][i]['name']
data_p = data['children'][0]['children'][0]['children'][0]['children'][i]['children'][0]['data']
depth = data['children'][0]['children'][0]['children'][0]['children'][i]['children'][1]['data']
self._populate_values(name, data_p, depth)
else:
n_elem = len(data['children'][0]['children'][0]['children'])
for i in range(n_elem):
name = data['children'][0]['children'][0]['children'][i]['name']
data_p = data['children'][0]['children'][0]['children'][i]['children'][0]['data']
depth = data['children'][0]['children'][0]['children'][i]['children'][1]['data']
self._populate_values(name, data_p, depth)

maxs = [{'name': i['name'], 'max': i['max'], 'depth': i['depth']} for i in self.values]

with open(model.config.get_output_dir() + '/max_depth.json', 'w') as f:
json.dump(maxs, f, indent=4)

for k, v in model.output_vars.items():
filtered_max = [x['max'] for x in maxs if v.cppname in x['name']]
if len(filtered_max) == 0:
continue
if len(filtered_max) > 1:
print('WARNING! Check names of FIFOs')
thesps marked this conversation as resolved.
Show resolved Hide resolved
v.pragma = (v.pragma[0], filtered_max[0] + 1)

# will not work with multiple i/o variables
thesps marked this conversation as resolved.
Show resolved Hide resolved
if model.config.get_config_value('Backend') == 'VivadoAccelerator':
inp = model.get_input_variables()[0]
out = model.get_output_variables()[0]
for x in maxs:
if 'in_local' in x['name']:
inp.pragma = (inp.pragma[0], x['max'] + 1)
elif 'out_local' in x['name']:
out.pragma = (out.pragma[0], x['max'] + 1)

model.write()
print('[hls4ml] - FIFO optimization completed')
return False
18 changes: 10 additions & 8 deletions hls4ml/backends/vivado_accelerator/vivado_accelerator_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ def create_initial_config(self, board='pynq-z2', part=None, clock_period=5, io_t
config['AcceleratorConfig']['Precision']['Output'] = output_type # float, double or ap_fixed<a,b>
return config

def get_default_flow(self):
return self._default_flow

def get_writer_flow(self):
return self._writer_flow

def _register_flows(self):
#TODO expand this to include new accelerator flow
parent_flows = get_backend_flows(backend='vivado')
for flow_name in parent_flows:
flow = get_flow(flow_name)
acc_flow = register_flow(flow_name.replace('vivado:', ''), flow.optimizers, requires=flow.requires, backend=self.name)
if ':write' in flow_name:
self._writer_flow = acc_flow
self._default_flow = 'vivadoaccelerator:ip'
vivado_writer = ['vivado:write']
vivado_accel_writer = ['vivadoaccelerator:write_hls']
self._writer_flow = register_flow('write', vivado_accel_writer, requires=vivado_writer, backend=self.name)
self._default_flow = 'vivado:ip'
1 change: 1 addition & 0 deletions hls4ml/model/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ def __init__(self, config, data_reader, layer_list, inputs=None, outputs=None):
self.output_vars = {}

self._top_function_lib = None
self.fifo_opt = False
thesps marked this conversation as resolved.
Show resolved Hide resolved

self._make_graph(layer_list)

Expand Down
17 changes: 11 additions & 6 deletions hls4ml/report/vivado_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,22 @@ def read_vivado_report(hls_dir, full_report=False):
print('Reports for solution "{}":\n'.format(sln))
_find_reports(sln_dir + '/' + sln, top_func_name, full_report)

def _parse_build_script(script_path):
def _parse_build_script(path):
prj_dir = None
top_func_name = None

with open(script_path, 'r') as f:
build_path = path + '/build_prj.tcl'
project_path = path + '/project.tcl'
with open(build_path, 'r') as f:
for line in f.readlines():
if 'open_project' in line:
prj_dir = line.split()[-1]
elif 'set_top' in line:
if 'set_top' in line:
top_func_name = line.split()[-1]

with open(project_path, 'r') as f:
for line in f.readlines():
if 'set myproject' in line:
prj_dir = line.split('"')[-2] + '_prj'

return prj_dir, top_func_name

def _find_solutions(sln_dir):
Expand Down Expand Up @@ -105,7 +110,7 @@ def parse_vivado_report(hls_dir):
top_func_name = None

if os.path.isfile(hls_dir + '/build_prj.tcl'):
prj_dir, top_func_name = _parse_build_script(hls_dir + '/build_prj.tcl')
prj_dir, top_func_name = _parse_build_script(hls_dir)

if prj_dir is None or top_func_name is None:
print('Unable to read project data. Exiting.')
Expand Down
125 changes: 120 additions & 5 deletions hls4ml/templates/vivado/build_prj.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,104 @@ array set opt {
vsynth 0
}

set tcldir [file dirname [info script]]
source [file join $tcldir project.tcl]

proc remove_recursive_log_wave {} {
set tcldir [file dirname [info script]]
source [file join $tcldir project.tcl]

set filename ${myproject}_prj/solution1/sim/verilog/${myproject}.tcl
set timestamp [clock format [clock seconds] -format {%Y%m%d%H%M%S}]
set temp $filename.new.$timestamp
# set backup $filename.bak.$timestamp

set in [open $filename r]
set out [open $temp w]

# line-by-line, read the original file
while {[gets $in line] != -1} {
if {[string equal "$line" "log_wave -r /"]} {
set line { }
}
puts $out $line
}

close $in
close $out

# move the new data to the proper filename
file delete -force $filename
file rename -force $temp $filename
}

proc add_vcd_instructions_tcl {} {
set tcldir [file dirname [info script]]
source [file join $tcldir project.tcl]

set filename ${myproject}_prj/solution1/sim/verilog/${myproject}.tcl
set timestamp [clock format [clock seconds] -format {%Y%m%d%H%M%S}]
set temp $filename.new.$timestamp
# set backup $filename.bak.$timestamp

set in [open $filename r]
set out [open $temp w]

# line-by-line, read the original file
while {[gets $in line] != -1} {
if {[string equal "$line" "log_wave -r /"]} {
set line {source "../../../../project.tcl"
if {[string equal "$backend" "vivadoaccelerator"]} {
current_scope [get_scopes -regex /apatb_${myproject}_axi_top/AESL_inst_${myproject}_axi/${myproject}_U0.*]
set scopes [get_scopes -regexp {layer(\d*)_.*data_0_V_U.*}]
append scopes { }
current_scope /apatb_${myproject}_axi_top/AESL_inst_${myproject}_axi
append scopes [get_scopes -regexp {(in_local_V_data.*_0_.*)}]
append scopes { }
append scopes [get_scopes -regexp {(out_local_V_data.*_0_.*)}]
} else {
current_scope [get_scopes -regex /apatb_${myproject}_top/AESL_inst_${myproject}]
set scopes [get_scopes -regexp {layer(\d*)_.*data_0_V_U.*}]
}
open_vcd fifo_opt.vcd
foreach scope $scopes {
current_scope $scope
if {[catch [get_objects usedw]] == 0} {
puts "$scope skipped"
continue
}
set usedw [get_objects usedw]
set depth [get_objects DEPTH]
add_wave $usedw
log_vcd $usedw
log_wave $usedw
add_wave $depth
log_vcd $depth
log_wave $depth
}
}

set line [string map [list "myproject" $myproject] $line]
}

if {[string equal "$line" "quit"]} {
set line {flush_vcd
close_vcd
quit
}
}
# then write the transformed line
puts $out $line
}

close $in
close $out

# move the new data to the proper filename
file delete -force $filename
file rename -force $temp $filename
}

foreach arg $::argv {
foreach o [lsort [array names opt]] {
regexp "$o=+(\\w+)" $arg unused opt($o)
Expand Down Expand Up @@ -50,9 +148,9 @@ set CSIM_RESULTS "./tb_data/csim_results.log"
set RTL_COSIM_RESULTS "./tb_data/rtl_cosim_results.log"

if {$opt(reset)} {
open_project -reset myproject_prj
open_project -reset ${myproject}_prj
} else {
open_project myproject_prj
open_project ${myproject}_prj
}
set_top myproject
add_files firmware/myproject.cpp -cflags "-std=c++0x"
Expand Down Expand Up @@ -91,10 +189,27 @@ if {$opt(cosim)} {
# TODO: This is a workaround (Xilinx defines __RTL_SIMULATION__ only for SystemC testbenches).
add_files -tb myproject_test.cpp -cflags "-std=c++0x -DRTL_SIM"
set time_start [clock clicks -milliseconds]
cosim_design -trace_level all

cosim_design -trace_level all -setup

if {$fifo_opt} {
thesps marked this conversation as resolved.
Show resolved Hide resolved
puts "\[hls4ml\] - FIFO optimization started"
add_vcd_instructions_tcl
}

remove_recursive_log_wave
set old_pwd [pwd]
cd ${myproject}_prj/solution1/sim/verilog/
source run_sim.tcl
cd $old_pwd

set time_end [clock clicks -milliseconds]
puts "INFO:"
puts [read [open myproject_prj/solution1/sim/report/myproject_cosim.rpt r]]
if {[string equal "$backend" "vivadoaccelerator"]} {
puts [read [open ${myproject}_prj/solution1/sim/report/${myproject}_axi_cosim.rpt r]]
} else {
puts [read [open ${myproject}_prj/solution1/sim/report/${myproject}_cosim.rpt r]]
}
report_time "C/RTL SIMULATION" $time_start $time_end
}

Expand All @@ -120,7 +235,7 @@ if {$opt(export)} {

if {$opt(vsynth)} {
puts "***** VIVADO SYNTHESIS *****"
if {[file exist myproject_prj/solution1/syn/vhdl]} {
if {[file exist ${myproject}_prj/solution1/syn/vhdl]} {
set time_start [clock clicks -milliseconds]
exec vivado -mode batch -source vivado_synth.tcl >@ stdout
set time_end [clock clicks -milliseconds]
Expand Down