Skip to content

Commit e7497d5

Browse files
committed
scripts
1 parent 1e5de7f commit e7497d5

File tree

3 files changed

+523
-51
lines changed

3 files changed

+523
-51
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
#!/usr/bin/env python3
2+
3+
import json
4+
import argparse
5+
import os
6+
import sys
7+
import matplotlib.pyplot as plt
8+
import seaborn as sns
9+
import numpy as np
10+
11+
# Set the plotting style
12+
plt.style.use('seaborn-v0_8')
13+
sns.set_palette("husl")
14+
15+
# Color scheme
16+
LILAC_COLOR = '#C8A2C8'
17+
RV_COLOR = '#4A90E2' # Nice blue that contrasts with lilac
18+
19+
def load_json_data(json_file):
20+
"""Load data from JSON file generated by generate-json-table.py."""
21+
with open(json_file, 'r') as f:
22+
return json.load(f)
23+
24+
def extract_resource_data(data, resource):
25+
"""Extract data for a specific resource from the JSON structure."""
26+
lilac_data = {}
27+
rv_data = {}
28+
29+
# Extract Lilac data
30+
if 'Lilac' in data:
31+
for ii, values in data['Lilac'].items():
32+
if resource in values and values[resource] is not None:
33+
lilac_data[int(ii)] = values[resource]
34+
35+
# Extract RV data
36+
if 'RV' in data:
37+
for ii, values in data['RV'].items():
38+
if resource in values and values[resource] is not None:
39+
rv_data[int(ii)] = values[resource]
40+
41+
return lilac_data, rv_data
42+
43+
def create_side_by_side_plot(lilac_data, rv_data, resource, output_dir):
44+
"""Create side-by-side bar plot comparing Lilac and RV."""
45+
# Get common II values
46+
ii_values = sorted(set(lilac_data.keys()) | set(rv_data.keys()))
47+
48+
# Prepare data for plotting
49+
lilac_values = [lilac_data.get(ii, 0) for ii in ii_values]
50+
rv_values = [rv_data.get(ii, 0) for ii in ii_values]
51+
52+
# Create the plot
53+
fig, ax = plt.subplots(figsize=(10, 6))
54+
55+
x = np.arange(len(ii_values))
56+
width = 0.35
57+
58+
bars1 = ax.bar(x - width/2, lilac_values, width, label='Lilac', color=LILAC_COLOR, alpha=0.8)
59+
bars2 = ax.bar(x + width/2, rv_values, width, label='RV', color=RV_COLOR, alpha=0.8)
60+
61+
# Customize the plot
62+
ax.set_xlabel('II Value', fontsize=12)
63+
ax.set_ylabel(get_y_label(resource), fontsize=12)
64+
ax.set_title(f'{get_resource_title(resource)} Comparison', fontsize=14, fontweight='bold')
65+
ax.set_xticks(x)
66+
ax.set_xticklabels(ii_values)
67+
ax.legend()
68+
69+
# Set y-axis to start at 0
70+
ax.set_ylim(bottom=0)
71+
72+
# Add horizontal grid lines only
73+
ax.grid(True, axis='y', alpha=0.3)
74+
ax.set_axisbelow(True)
75+
76+
# Tight layout
77+
plt.tight_layout()
78+
79+
# Save the plot
80+
output_path = os.path.join(output_dir, f'{resource}.pdf')
81+
plt.savefig(output_path, format='pdf', bbox_inches='tight', dpi=300)
82+
plt.close()
83+
84+
return output_path
85+
86+
def create_normalized_plot(lilac_data, rv_data, resource, output_dir):
87+
"""Create normalized bar plot showing RV/Lilac ratios."""
88+
# Get common II values
89+
ii_values = sorted(set(lilac_data.keys()) & set(rv_data.keys()))
90+
91+
if not ii_values:
92+
print(f"Warning: No common II values for {resource}, skipping normalized plot")
93+
return None
94+
95+
# Calculate normalized values (RV/Lilac)
96+
normalized_values = []
97+
for ii in ii_values:
98+
if lilac_data[ii] != 0:
99+
normalized_values.append(rv_data[ii] / lilac_data[ii])
100+
else:
101+
normalized_values.append(0) # Handle division by zero
102+
103+
# Create the plot
104+
fig, ax = plt.subplots(figsize=(10, 6))
105+
106+
x = np.arange(len(ii_values))
107+
108+
# Use different colors based on whether RV is better (< 1.0) or worse (> 1.0)
109+
colors = [RV_COLOR if val <= 1.0 else '#E74C3C' for val in normalized_values]
110+
111+
bars = ax.bar(x, normalized_values, color=colors, alpha=0.8)
112+
113+
# Add horizontal line at y=1.0 for reference
114+
ax.axhline(y=1.0, color='black', linestyle='--', alpha=0.7, linewidth=1)
115+
116+
# Customize the plot
117+
ax.set_xlabel('II Value', fontsize=12)
118+
ax.set_ylabel(f'RV / Lilac Ratio', fontsize=12)
119+
ax.set_title(f'{get_resource_title(resource)} - Normalized (RV/Lilac)', fontsize=14, fontweight='bold')
120+
ax.set_xticks(x)
121+
ax.set_xticklabels(ii_values)
122+
123+
# Set y-axis to start at 0
124+
ax.set_ylim(bottom=0)
125+
126+
# Add horizontal grid lines only
127+
ax.grid(True, axis='y', alpha=0.3)
128+
ax.set_axisbelow(True)
129+
130+
# Add text annotation for the reference line
131+
ax.text(0.02, 1.05, 'Equal performance', transform=ax.transAxes,
132+
fontsize=9, alpha=0.7, verticalalignment='bottom')
133+
134+
# Tight layout
135+
plt.tight_layout()
136+
137+
# Save the plot
138+
output_path = os.path.join(output_dir, f'{resource}_normalized.pdf')
139+
plt.savefig(output_path, format='pdf', bbox_inches='tight', dpi=300)
140+
plt.close()
141+
142+
return output_path
143+
144+
def get_resource_title(resource):
145+
"""Get formatted title for resource."""
146+
titles = {
147+
'luts': 'LUT Utilization',
148+
'registers': 'Register Utilization',
149+
'latency': 'Latency (Cycles)',
150+
'freq_mhz': 'Frequency (MHz)',
151+
'frames_per_sec': 'Frames per Second'
152+
}
153+
return titles.get(resource, resource.replace('_', ' ').title())
154+
155+
def get_y_label(resource):
156+
"""Get y-axis label for resource."""
157+
labels = {
158+
'luts': 'LUTs',
159+
'registers': 'Registers',
160+
'latency': 'Cycles',
161+
'freq_mhz': 'MHz',
162+
'frames_per_sec': 'Frames/sec'
163+
}
164+
return labels.get(resource, resource.replace('_', ' ').title())
165+
166+
def main():
167+
parser = argparse.ArgumentParser(description='Generate bar plots from JSON synthesis data')
168+
parser.add_argument('--json-file', required=True,
169+
help='Path to JSON file from generate-json-table.py')
170+
parser.add_argument('--output-dir', default='.',
171+
help='Directory for output PDFs (default: current directory)')
172+
parser.add_argument('--normalized', action='store_true',
173+
help='Generate normalized plots (RV/Lilac ratios)')
174+
parser.add_argument('--resources', nargs='+',
175+
default=['luts', 'registers', 'latency', 'freq_mhz', 'frames_per_sec'],
176+
help='Resources to plot (default: all available)')
177+
178+
args = parser.parse_args()
179+
180+
# Load JSON data
181+
try:
182+
data = load_json_data(args.json_file)
183+
except FileNotFoundError:
184+
print(f"Error: JSON file '{args.json_file}' not found", file=sys.stderr)
185+
sys.exit(1)
186+
except json.JSONDecodeError as e:
187+
print(f"Error parsing JSON file: {e}", file=sys.stderr)
188+
sys.exit(1)
189+
190+
# Create output directory if it doesn't exist
191+
os.makedirs(args.output_dir, exist_ok=True)
192+
193+
# Generate plots for each resource
194+
generated_files = []
195+
196+
for resource in args.resources:
197+
print(f"Generating plots for {resource}...")
198+
199+
# Extract data for this resource
200+
lilac_data, rv_data = extract_resource_data(data, resource)
201+
202+
if not lilac_data and not rv_data:
203+
print(f"Warning: No data found for resource '{resource}', skipping")
204+
continue
205+
206+
try:
207+
if args.normalized:
208+
output_path = create_normalized_plot(lilac_data, rv_data, resource, args.output_dir)
209+
if output_path:
210+
generated_files.append(output_path)
211+
else:
212+
output_path = create_side_by_side_plot(lilac_data, rv_data, resource, args.output_dir)
213+
generated_files.append(output_path)
214+
except Exception as e:
215+
print(f"Error generating plot for {resource}: {e}", file=sys.stderr)
216+
continue
217+
218+
print(f"\nGenerated {len(generated_files)} plots:")
219+
for file_path in generated_files:
220+
print(f" {file_path}")
221+
222+
if __name__ == '__main__':
223+
main()
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env python3
2+
3+
import json
4+
import sys
5+
import argparse
6+
import importlib.util
7+
import os
8+
9+
# Import from the same directory
10+
script_dir = os.path.dirname(os.path.abspath(__file__))
11+
spec = importlib.util.spec_from_file_location("generate_latex_table", os.path.join(script_dir, "generate-latex-table.py"))
12+
generate_latex_table = importlib.util.module_from_spec(spec)
13+
spec.loader.exec_module(generate_latex_table)
14+
15+
collect_design_data = generate_latex_table.collect_design_data
16+
17+
def generate_json_table(results_file, filament_latencies_file, rv_latencies_file, add_fps=False):
18+
"""Generate a JSON table from synthesis results and latency data."""
19+
20+
design_data = collect_design_data(results_file, filament_latencies_file, rv_latencies_file)
21+
22+
# Structure data by design type and II value
23+
output = {}
24+
25+
for design in design_data:
26+
design_type = design['design_type']
27+
ii_value = design['ii']
28+
29+
if design_type not in output:
30+
output[design_type] = {}
31+
32+
# Create entry for this II value
33+
entry = {
34+
'luts': design['luts'],
35+
'registers': design['registers'],
36+
'latency': design['latency'],
37+
'freq_mhz': design['freq_mhz']
38+
}
39+
40+
# Add frames per second if requested
41+
if add_fps and design['latency'] is not None:
42+
fps = (design['freq_mhz'] * 1000000) / design['latency']
43+
entry['frames_per_sec'] = int(fps)
44+
elif add_fps:
45+
entry['frames_per_sec'] = None
46+
47+
output[design_type][str(ii_value)] = entry
48+
49+
return output
50+
51+
def main():
52+
parser = argparse.ArgumentParser(description='Generate JSON table from synthesis results and latency data')
53+
parser.add_argument('--results', required=True,
54+
help='Path to results.json file with resource utilization data')
55+
parser.add_argument('--fil-lat', required=True,
56+
help='Path to filament latencies JSON file')
57+
parser.add_argument('--rv-lat', required=True,
58+
help='Path to RV latencies JSON file (pyramid-cycles.json)')
59+
parser.add_argument('--add-fps', action='store_true',
60+
help='Add frames_per_sec field to the output')
61+
parser.add_argument('-o', '--output',
62+
help='Output file (default: stdout)')
63+
64+
args = parser.parse_args()
65+
66+
try:
67+
json_data = generate_json_table(args.results, args.fil_lat, args.rv_lat, args.add_fps)
68+
69+
json_output = json.dumps(json_data, indent=2)
70+
71+
if args.output:
72+
with open(args.output, 'w') as f:
73+
f.write(json_output)
74+
print(f"JSON output written to {args.output}", file=sys.stderr)
75+
else:
76+
print(json_output)
77+
78+
except FileNotFoundError as e:
79+
print(f"Error: {e}", file=sys.stderr)
80+
sys.exit(1)
81+
except Exception as e:
82+
print(f"Error: {e}", file=sys.stderr)
83+
sys.exit(1)
84+
85+
if __name__ == '__main__':
86+
main()

0 commit comments

Comments
 (0)