5
5
from tabulate import tabulate
6
6
from ast import literal_eval
7
7
8
- def parse_quartus_report (hls_dir ):
9
- """
8
+ def parse_quartus_report (hls_dir , write_to_file = True ):
9
+ '''
10
10
Parse a report from a given Quartus project as a dictionary.
11
11
12
12
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
14
15
15
16
Returns:
16
- dict: the report dictionary
17
-
18
- Raises exceptions on errors
17
+ results (dict): The report dictionary, containing latency, resource usage etc.
19
18
20
- """
19
+ '''
21
20
if not os .path .exists (hls_dir ):
22
21
print ('Path {} does not exist. Exiting.' .format (hls_dir ))
23
22
return
@@ -29,16 +28,29 @@ def parse_quartus_report(hls_dir):
29
28
print ('Project {} does not exist. Rerun "hls4ml build -p {}".' .format (prj_dir , hls_dir ))
30
29
return
31
30
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
33
42
34
43
def read_quartus_report (hls_dir , open_browser = False ):
35
- """
44
+ '''
36
45
Parse and print the Quartus report to print the report. Optionally open a browser.
37
46
38
47
Args:
39
48
hls_dir (string): The directory where the project is found
40
49
open_browser, optional: whether to open a browser
41
- """
50
+
51
+ Returns:
52
+ None
53
+ '''
42
54
report = parse_quartus_report (hls_dir )
43
55
44
56
print ('HLS Resource Summary\n ' )
@@ -56,6 +68,15 @@ def read_quartus_report(hls_dir, open_browser=False):
56
68
webbrowser .open (url )
57
69
58
70
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
+ '''
59
80
top_func_name = None
60
81
61
82
with open (hls_dir + '/build_lib.sh' , 'r' ) as f :
@@ -65,103 +86,144 @@ def _find_project_dir(hls_dir):
65
86
66
87
return top_func_name + '-fpga.prj'
67
88
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 )
115
126
else :
116
- return True
117
- elif isinstance (node , asttypes .Identifier ):
118
- return node .value
127
+ raise ValueError ("Cannot + on anything other than two literals" )
119
128
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 ))
122
147
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)
127
151
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
+ '''
135
158
159
+ with open (filename ) as dataFile :
160
+ quartus_data = dataFile .read ()
161
+ quartus_data = read_js_object (quartus_data )
136
162
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
157
178
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
158
197
198
+ Args:
199
+ filename (string): Location of verification file
200
+
201
+ Returns:
202
+ results (dict): Verification data obtained from simulation
203
+ '''
159
204
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
166
223
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' ))
167
229
return results
0 commit comments