Skip to content

Commit 1a6f1f2

Browse files
committed
Temp change, will be dropped
1 parent 07a7dfb commit 1a6f1f2

File tree

3 files changed

+193
-122
lines changed

3 files changed

+193
-122
lines changed

hls4ml/backends/quartus/quartus_backend.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -94,35 +94,40 @@ def create_initial_config(self, part='Arria10', clock_period=5, io_type='io_para
9494

9595
return config
9696

97-
def build(self, model, synth=True, fpgasynth=False):
97+
def build(self, model, synth=True, fpgasynth=False, logging=1, cont_if_large_area=False):
9898
"""
9999
Builds the project using Intel HLS compiler.
100100
101-
Users should generally not call this function directly but instead use `ModelGraph.build()`.
102-
This function assumes the model was written with a call to `ModelGraph.write()`
103-
104101
Args:
105102
model (ModelGraph): The model to build
106-
synth, optional: Whether to run synthesis
107-
fpgasynth, optional: Whether to run fpga synthesis
108-
103+
synth, optional: Whether to run HLS synthesis
104+
fpgasynth, optional: Whether to run FPGA synthesis (Quartus Compile)
105+
logging, optional: Logging level to be displayed during HLS synthesis (0, 1, 2)
106+
cont_if_large_area: Instruct the HLS compiler to continue synthesis if the estimated resource usaga exceeds device resources
109107
Errors raise exceptions
110108
"""
109+
110+
# Check software needed is present
111111
found = os.system('command -v i++ > /dev/null')
112112
if found != 0:
113113
raise Exception('Intel HLS installation not found. Make sure "i++" is on PATH.')
114114

115-
with chdir(model.config.get_output_dir()):
116-
if synth:
117-
os.system('make {}-fpga'.format(model.config.get_project_name()))
118-
os.system('./{}-fpga'.format(model.config.get_project_name()))
119-
120-
if fpgasynth:
115+
if fpgasynth:
116+
if fpgasynth and not synth:
117+
raise Exception('HLS Synthesis needs to be run before FPGA synthesis')
121118
found = os.system('command -v quartus_sh > /dev/null')
122119
if found != 0:
123120
raise Exception('Quartus installation not found. Make sure "quartus_sh" is on PATH.')
124-
os.chdir(model.config.get_project_name() + '-fpga.prj/quartus')
125-
os.system('quartus_sh --flow compile quartus_compile')
121+
122+
with chdir(model.config.get_output_dir()):
123+
if synth:
124+
quartus_compile = 'QUARTUS_COMPILE=--quartus-compile' if fpgasynth else ''
125+
cont_synth = 'CONT_IF_LARGE_AREA=--dont-error-if-large-area-est' if cont_if_large_area else ''
126+
log_1 = 'LOGGING_1=-v ' if logging >= 1 else ''
127+
log_2 = 'LOGGING_2=-v ' if logging >= 2 else ''
128+
os.system(f'make {model.config.get_project_name()}-fpga {log_1} {log_2} {cont_synth} {quartus_compile}')
129+
130+
os.system('singularity exec /home/boz/Desktop/hls4ml/software/quartus.sif ./{}-fpga'.format(model.config.get_project_name()))
126131

127132
return parse_quartus_report(model.config.get_output_dir())
128133

@@ -209,7 +214,7 @@ def init_conv2d(self, layer):
209214
# Dense matrix multiply properties
210215
layer.set_attr('rfpad', 0)
211216
layer.set_attr('bfpad', 0)
212-
217+
213218
# Reuse and parallelization factors
214219
layer.set_attr('strategy', 'resource')
215220
n_in, n_out = self.get_layer_mult_size(layer)

hls4ml/report/quartus_report.py

Lines changed: 161 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,18 @@
55
from tabulate import tabulate
66
from ast import literal_eval
77

8-
def parse_quartus_report(hls_dir):
9-
"""
8+
def parse_quartus_report(hls_dir, write_to_file=True):
9+
'''
1010
Parse a report from a given Quartus project as a dictionary.
1111
1212
Args:
13-
hls_dir (string): The directory where the project is found
13+
hls_dir (string): The directory where the project is found
14+
write_to_file (bool): A flag indicating whether to write the results to a separate file
1415
1516
Returns:
16-
dict: the report dictionary
17-
18-
Raises exceptions on errors
17+
results (dict): The report dictionary, containing latency, resource usage etc.
1918
20-
"""
19+
'''
2120
if not os.path.exists(hls_dir):
2221
print('Path {} does not exist. Exiting.'.format(hls_dir))
2322
return
@@ -29,16 +28,29 @@ def parse_quartus_report(hls_dir):
2928
print('Project {} does not exist. Rerun "hls4ml build -p {}".'.format(prj_dir, hls_dir))
3029
return
3130

32-
return _find_reports(rpt_dir)
31+
results = _find_reports(rpt_dir)
32+
print(results)
33+
if write_to_file:
34+
print("Here")
35+
f = open(hls_dir + '/' 'synthesis-report.txt', 'w')
36+
f.write('HLS Synthesis Latency & Resource Usage Report')
37+
for key in results:
38+
f.write(str(key) + ':' + str(results[key]) + '\n')
39+
print("There")
40+
print(f'Saved latency & resource usage summary to {hls_dir}/synthesis-report.txt')
41+
return results
3342

3443
def read_quartus_report(hls_dir, open_browser=False):
35-
"""
44+
'''
3645
Parse and print the Quartus report to print the report. Optionally open a browser.
3746
3847
Args:
3948
hls_dir (string): The directory where the project is found
4049
open_browser, optional: whether to open a browser
41-
"""
50+
51+
Returns:
52+
None
53+
'''
4254
report = parse_quartus_report(hls_dir)
4355

4456
print('HLS Resource Summary\n')
@@ -56,6 +68,15 @@ def read_quartus_report(hls_dir, open_browser=False):
5668
webbrowser.open(url)
5769

5870
def _find_project_dir(hls_dir):
71+
'''
72+
Finds the synthesis folder from the HLS project directory
73+
74+
Args:
75+
hls_dir (string): HLS project location
76+
77+
Returns:
78+
project_dir (string): Synthesis folder within HLS project directory
79+
'''
5980
top_func_name = None
6081

6182
with open(hls_dir + '/build_lib.sh', 'r') as f:
@@ -65,103 +86,144 @@ def _find_project_dir(hls_dir):
6586

6687
return top_func_name + '-fpga.prj'
6788

68-
def _find_reports(rpt_dir):
69-
def read_js_object(js_script):
70-
"""
71-
Reads the JS input (js_script, a string), and return a dictionary of
72-
variables definded in the JS.
73-
"""
74-
def visit(node):
75-
if isinstance(node, asttypes.Program):
76-
d = {}
77-
for child in node:
78-
if not isinstance(child, asttypes.VarStatement):
79-
raise ValueError("All statements should be var statements")
80-
key, val = visit(child)
81-
d[key] = val
82-
return d
83-
elif isinstance(node, asttypes.VarStatement):
84-
return visit(node.children()[0])
85-
elif isinstance(node, asttypes.VarDecl):
86-
return (visit(node.identifier), visit(node.initializer))
87-
elif isinstance(node, asttypes.Object):
88-
d = {}
89-
for property in node:
90-
key = visit(property.left)
91-
value = visit(property.right)
92-
d[key] = value
93-
return d
94-
elif isinstance(node, asttypes.BinOp):
95-
# simple constant folding
96-
if node.op == '+':
97-
if isinstance(node.left, asttypes.String) and isinstance(node.right, asttypes.String):
98-
return visit(node.left) + visit(node.right)
99-
elif isinstance(node.left, asttypes.Number) and isinstance(node.right, asttypes.Number):
100-
return visit(node.left) + visit(node.right)
101-
else:
102-
raise ValueError("Cannot + on anything other than two literals")
103-
else:
104-
raise ValueError("Cannot do operator '%s'" % node.op)
105-
106-
elif isinstance(node, asttypes.String) or isinstance(node, asttypes.Number):
107-
return literal_eval(node.value)
108-
elif isinstance(node, asttypes.Array):
109-
return [visit(x) for x in node]
110-
elif isinstance(node, asttypes.Null):
111-
return None
112-
elif isinstance(node, asttypes.Boolean):
113-
if str(node) == "false":
114-
return False
89+
def read_js_object(js_script):
90+
'''
91+
Reads the JavaScript file and return a dictionary of variables definded in the script.
92+
93+
Args:
94+
js_script (string) - path to JavaScript File
95+
96+
Returns:
97+
Dictionary of variables defines in script
98+
'''
99+
def visit(node):
100+
if isinstance(node, asttypes.Program):
101+
d = {}
102+
for child in node:
103+
if not isinstance(child, asttypes.VarStatement):
104+
raise ValueError("All statements should be var statements")
105+
key, val = visit(child)
106+
d[key] = val
107+
return d
108+
elif isinstance(node, asttypes.VarStatement):
109+
return visit(node.children()[0])
110+
elif isinstance(node, asttypes.VarDecl):
111+
return (visit(node.identifier), visit(node.initializer))
112+
elif isinstance(node, asttypes.Object):
113+
d = {}
114+
for property in node:
115+
key = visit(property.left)
116+
value = visit(property.right)
117+
d[key] = value
118+
return d
119+
elif isinstance(node, asttypes.BinOp):
120+
# simple constant folding
121+
if node.op == '+':
122+
if isinstance(node.left, asttypes.String) and isinstance(node.right, asttypes.String):
123+
return visit(node.left) + visit(node.right)
124+
elif isinstance(node.left, asttypes.Number) and isinstance(node.right, asttypes.Number):
125+
return visit(node.left) + visit(node.right)
115126
else:
116-
return True
117-
elif isinstance(node, asttypes.Identifier):
118-
return node.value
127+
raise ValueError("Cannot + on anything other than two literals")
119128
else:
120-
raise Exception("Unhandled node: %r" % node)
121-
return visit(es5(js_script))
129+
raise ValueError("Cannot do operator '%s'" % node.op)
130+
131+
elif isinstance(node, asttypes.String) or isinstance(node, asttypes.Number):
132+
return literal_eval(node.value)
133+
elif isinstance(node, asttypes.Array):
134+
return [visit(x) for x in node]
135+
elif isinstance(node, asttypes.Null):
136+
return None
137+
elif isinstance(node, asttypes.Boolean):
138+
if str(node) == "false":
139+
return False
140+
else:
141+
return True
142+
elif isinstance(node, asttypes.Identifier):
143+
return node.value
144+
else:
145+
raise Exception("Unhandled node: %r" % node)
146+
return visit(es5(js_script))
122147

123-
def _read_quartus_file(filename, results):
124-
with open(filename) as dataFile:
125-
quartus_data = dataFile.read()
126-
quartus_data = read_js_object(quartus_data)
148+
def _read_quartus_file(filename):
149+
'''
150+
Reads results (clock frequency, resource usage) obtained through FPGA synthesis (full Quartus compilation)
127151
128-
if(quartus_data['quartusJSON']['quartusFitClockSummary']['nodes'][0]['clock'] != "TBD"):
129-
results['Clock'] = quartus_data['quartusJSON']['quartusFitClockSummary']['nodes'][0]['clock']
130-
results['Quartus ALM'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['alm']
131-
results['Quartus REG'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['reg']
132-
results['Quartus DSP'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['dsp']
133-
results['Quartus RAM'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['ram']
134-
results['Quartus MLAB'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['mlab']
152+
Args:
153+
filename (string): Location of Quartus report
154+
155+
Returns:
156+
results (dict): Resource usage obtained through Quartus Compile
157+
'''
135158

159+
with open(filename) as dataFile:
160+
quartus_data = dataFile.read()
161+
quartus_data = read_js_object(quartus_data)
136162

137-
def _read_report_file(filename, results):
138-
with open(filename) as dataFile:
139-
report_data = dataFile.read()
140-
report_data = report_data[: report_data.rfind('var fileJSON')]
141-
report_data = read_js_object(report_data)
142-
results['HLS ALUT'], results['HLS FF'], results['HLS RAM'], results['HLS DSP'], results['HLS MLAB'] = report_data['areaJSON']['total']
143-
results['HLS ALUT percent'], results['HLS FF percent'], results['HLS RAM percent'], results['HLS DSP percent'], results['HLS MLAB percent'] = report_data['areaJSON']['total_percent']
144-
145-
146-
147-
def _read_verification_file(filename, results):
148-
if os.path.isfile(filename):
149-
with open(filename) as dataFile:
150-
verification_data = dataFile.read()
151-
verification_data = read_js_object(verification_data)
152-
results['num_invocation'] = verification_data['verifJSON']['functions'][0]['data'][0]
153-
results['Latency'] = verification_data['verifJSON']['functions'][0]['data'][1].split(",")
154-
results['ii'] = verification_data['verifJSON']['functions'][0]['data'][2].split(",")
155-
else:
156-
print('Verification file not found. Run ./[projectname]-fpga to generate.')
163+
results = {}
164+
if(quartus_data['quartusJSON']['quartusFitClockSummary']['nodes'][0]['clock'] != "TBD"):
165+
results['Clock'] = quartus_data['quartusJSON']['quartusFitClockSummary']['nodes'][0]['clock']
166+
results['Quartus ALM'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['alm']
167+
results['Quartus REG'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['reg']
168+
results['Quartus DSP'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['dsp']
169+
results['Quartus RAM'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['ram']
170+
results['Quartus MLAB'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['mlab']
171+
else:
172+
print('Quartus report not found. Run Quartus Compilation using Quartus Shell or Full Compilation from Intel Quartus Prime')
173+
return results
174+
175+
def _read_hls_file(filename):
176+
'''
177+
Reads HLS resource estimate obtained through HLS synthesis
157178
179+
Args:
180+
filename (string): Location of HLS report
181+
182+
Returns:
183+
results (dict): Resource usage obtained through HLS Estimation
184+
'''
185+
with open(filename) as dataFile:
186+
report_data = dataFile.read()
187+
report_data = report_data[: report_data.rfind('var fileJSON')]
188+
report_data = read_js_object(report_data)
189+
results = {}
190+
results['HLS Estimate ALUT'], results['HLS Estimate FF'], results['HLS Estimate RAM'], results['HLS Estimate DSP'], results['HLS Estimate MLAB'] = report_data['areaJSON']['total']
191+
results['HLS Estimate ALUT (%)'], results['HLS Estimate FF(%)'], results['HLS Estimate RAM (%)'], results['HLS Estimate DSP (%)'], results['HLS Estimate MLAB (%)'] = report_data['areaJSON']['total_percent']
192+
return results
193+
194+
def _read_verification_file(filename):
195+
'''
196+
Reads verification data (latency, initiation interval) obtained through simulation
158197
198+
Args:
199+
filename (string): Location of verification file
200+
201+
Returns:
202+
results (dict): Verification data obtained from simulation
203+
'''
159204
results = {}
160-
quartus_file = rpt_dir + '/lib/quartus_data.js'
161-
report_file = rpt_dir + '/lib/report_data.js'
162-
verification_file = rpt_dir + '/lib/verification_data.js'
163-
_read_report_file(report_file, results)
164-
_read_verification_file(verification_file, results)
165-
_read_quartus_file(quartus_file, results)
205+
if os.path.isfile(filename):
206+
with open(filename) as dataFile:
207+
verification_data = dataFile.read()
208+
verification_data = read_js_object(verification_data)
209+
results['Number of Invoations'] = verification_data['verifJSON']['functions'][0]['data'][0]
210+
211+
latency = verification_data['verifJSON']['functions'][0]['data'][1].split(",")
212+
results['Latency (MIN)'] = latency[0]
213+
results['Latency (MAX)'] = latency[1]
214+
results['Latency (AVG)'] = latency[2]
215+
216+
ii = verification_data['verifJSON']['functions'][0]['data'][2].split(",")
217+
results['ii (MIN)'] = ii[0]
218+
results['ii (MAX)'] = ii[1]
219+
results['ii (AVG)'] = ii[2]
220+
else:
221+
print('Verification file not found. Run ./[projectname]-fpga to generate.')
222+
return results
166223

224+
def _find_reports(rpt_dir):
225+
results = {}
226+
results.update(_read_hls_file(rpt_dir + '/lib/report_data.js'))
227+
results.update(_read_verification_file(rpt_dir + '/lib/verification_data.js'))
228+
results.update(_read_quartus_file(rpt_dir + '/lib/quartus_data.js'))
167229
return results

0 commit comments

Comments
 (0)