Skip to content

Commit

Permalink
[Profile] Add interconnect profiling testbench support and python scr…
Browse files Browse the repository at this point in the history
…ipts.
  • Loading branch information
Aquaticfuller committed Dec 3, 2024
1 parent e601978 commit 0ee6833
Show file tree
Hide file tree
Showing 7 changed files with 957 additions and 18 deletions.
7 changes: 5 additions & 2 deletions config/config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ dram_axi_width_interleaved ?= 16
spm_bank_id_remap ?= 0

# Enable tile id remapping inside of each group
tile_id_remap ?= 1
tile_id_remap ?= 0

# Enable the spm access pattern profiling
spm_profiling ?= 0
spm_profiling ?= 0

# Enable the interconnect access pattern profiling
noc_profiling ?= 0
7 changes: 5 additions & 2 deletions hardware/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ vlog_defs += -DDRAM_AXI_WIDTH_INTERLEAVED=${dram_axi_width_interleaved}
ifeq (1, $(spm_profiling))
vlog_defs += -DSPM_PROFILING=$(spm_profiling)
endif
ifeq (1, $(noc_profiling))
vlog_defs += -DNOC_PROFILING=$(noc_profiling)
endif
ifeq (1, $(spm_bank_id_remap))
vlog_defs += -DSPM_BANK_ID_REMAP=$(spm_bank_id_remap)
endif
Expand Down Expand Up @@ -238,7 +241,7 @@ $(buildpath)/$(dpi_library)/mempool_dpi.so: $(dpi)

# Elaboration
.PHONY: elabvcs
elabvcs: dpivcs $(buildpath) $(buildpath)/compilevcs.sh update_opcodes
elabvcs: gen_selector dpivcs $(buildpath) $(buildpath)/compilevcs.sh update_opcodes
$(buildpath)/compilevcs.sh: $(bender) $(config_mk) Makefile $(MEMPOOL_DIR)/Bender.yml $(shell find {src,tb,deps} -type f)
$(bender) script vcs --vlogan-bin="$(vcs_cmd) vlogan" --vlog-arg="$(vlogan_args)" $(vlog_defs) -t rtl -t mempool_vsim > $(buildpath)/compilevcs.sh
echo "exit" >> $(buildpath)/compilevcs.sh
Expand Down Expand Up @@ -309,7 +312,7 @@ ifneq (${CLANG_PATH},)
VERILATOR_FLAGS += -LDFLAGS "-L $(CLANG_PATH)/lib -Wl,-rpath,$(CLANG_PATH)/lib -lc++ -nostdlib++"
endif

$(VERILATOR_MK): $(VERILATOR_CONF) $(VERILATOR_WAIVE) $(MEMPOOL_DIR)/Bender.yml $(shell find {src,tb,deps} -type f) $(bender) $(config_mk) Makefile
$(VERILATOR_MK): gen_selector $(VERILATOR_CONF) $(VERILATOR_WAIVE) $(MEMPOOL_DIR)/Bender.yml $(shell find {src,tb,deps} -type f) $(bender) $(config_mk) Makefile
rm -rf $(verilator_build); mkdir -p $(verilator_build)
# Overwrite Bootaddress to L2 base while we don't have a DPI to write a wake-up
$(eval boot_addr=$(l2_base))
Expand Down
8 changes: 4 additions & 4 deletions hardware/scripts/gen_xbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ def generate_crossbar(InNum, OutNum):
assertion = (
"initial begin\n"
f" assert (InNum == {InNum}) else "
"$fatal(1, \"Parameter InNum does not\n"
f" match expected value {InNum}\");\n"
"$fatal(1, \n\"Parameter InNum does not "
f"match expected value {InNum}\");\n"
f" assert (OutNum == {OutNum}) else "
"$fatal(1, \"Parameter OutNum does not\n"
f" match expected value {OutNum}\");\n"
"$fatal(1, \n\"Parameter OutNum does not "
f"match expected value {OutNum}\");\n"
"end\n"
)

Expand Down
303 changes: 303 additions & 0 deletions hardware/scripts/noc_profiling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import ast


scale_factor = 10

# Function to plot congestion intervals inline
def plot_intervals_inline(interval_results, output_int):
# Extract intervals and percentages
intervals = list(interval_results.keys())
percentages = list(interval_results.values())

# Plot the data
plt.figure(figsize=(10, 6))
bars = plt.bar(intervals, percentages, color="skyblue", edgecolor="black", alpha=0.7)

# Add labels on top of each bar
for bar, percent in zip(bars, percentages):
plt.text(bar.get_x() + bar.get_width() / 2, bar.get_height(), f"{percent:.1f}%",
ha='center', va='bottom', fontsize=10)

plt.xlabel("Intervals", fontsize=14)
plt.ylabel("Percentage (%)", fontsize=14)
plt.title("Percentage of Values in 10% Intervals", fontsize=16)
plt.xticks(rotation=45, fontsize=12)
plt.yticks(fontsize=12)
plt.tight_layout()
# Save the output as PNG and PDF
plt.savefig(output_int, format='png', bbox_inches='tight')
# plt.savefig(output_pdf, format='pdf', bbox_inches='tight')
plt.clf()
plt.close()


def visualize_mesh_noc_congestion_optimized(file_path, output_png, output_pdf, output_int, req_rsp, bw, NumX = 4, NumY = 4):
if bw == 0:
target = "Congestion"
else:
target = "Bandwidth"

# Load and preprocess the data
with open(file_path, 'r') as file:
raw_content = file.readlines()

parsed_data = []
for line in raw_content:
line = line.strip()
if line.startswith("{") and not line.endswith("}"):
line += "}" # Append a closing brace if missing
try:
entry = ast.literal_eval(line) # Parse JSON-like entries
parsed_data.append(entry)
except Exception:
continue

df = pd.DataFrame(parsed_data)
if df.empty:
print("Error: No valid data found in the file.")
return

# Filter data for REQ_RSP == 0 (request NoC)
req_noc_data = df[df['REQ_RSP'] == req_rsp]

# Calculate congestion for inbound and outbound links (1 - handshake/valid)
if bw:
max_hsk = req_noc_data['out_hsk_cyc_num'].max()
# req_noc_data['in_congestion'] = (req_noc_data['in_hsk_cyc_num'] / (max_hsk))
req_noc_data['out_congestion'] = (req_noc_data['out_hsk_cyc_num'] / (max_hsk))
# req_noc_data['out_congestion'] = (req_noc_data['out_hsk_cyc_num'])
filtered_req_noc_data = req_noc_data

# Normalize congestion for color mapping (0: least congested, 1: most congested)
# req_noc_data['in_congestion_norm'] = np.clip(req_noc_data['in_congestion'], 0, 1)
req_noc_data['out_congestion_norm'] = np.clip(req_noc_data['out_congestion'], 0, 1)

# Collect normalized congestion values into a list
data_list = req_noc_data['out_congestion_norm'].tolist()

print(f'Total flits transmitted:', req_noc_data['out_hsk_cyc_num'].sum())

else:
req_noc_data['in_congestion'] = 1 - (req_noc_data['in_hsk_cyc_num'] / (req_noc_data['in_vld_cyc_num'] + 1e-5))
req_noc_data['out_congestion'] = 1 - (req_noc_data['out_hsk_cyc_num'] / (req_noc_data['out_vld_cyc_num'] + 1e-5))


# Remove entries equal to 1 in 'in_congestion' before normalization
filtered_req_noc_data = req_noc_data[req_noc_data['out_congestion'] < 1]

# Normalize congestion for color mapping (0: least congested, 1: most congested)
max_out_congestion = filtered_req_noc_data['out_congestion'].max()
filtered_req_noc_data['out_congestion_norm'] = np.clip(filtered_req_noc_data['out_congestion'], 0, max_out_congestion) / max_out_congestion

# # Normalize congestion for color mapping (0: least congested, 1: most congested)
# req_noc_data['in_congestion_norm'] = np.clip(filtered_req_in_noc_data, 0, 1)
# req_noc_data['out_congestion_norm'] = np.clip(req_noc_data['out_congestion'], 0, 1)

# Collect normalized congestion values into a list
data_list = filtered_req_noc_data['out_congestion_norm'].tolist()

# Calculate the average congestion
average_congestion = np.mean(data_list)
print(f"Average {target}: {average_congestion:.2f}")


# draw interval
total_count = len(data_list)

# Define intervals for 10% ranges
intervals = [(i / 10, (i + 1) / 10) for i in range(10)] # [(0.0, 0.1), (0.1, 0.2), ..., (0.9, 1.0)]

# Initialize a dictionary to store percentages for each interval
interval_results = {}

# Calculate the percentage of values within each interval
for lower, upper in intervals:
count_in_interval = np.sum((np.array(data_list) >= lower) & (np.array(data_list) < upper))
percent_in_interval = (count_in_interval / total_count) * 100
interval_results[f"{lower:.1f}-{upper:.1f}"] = percent_in_interval

# Display the results
for interval, percent in interval_results.items():
print(f"Percentage of values in {interval}: {percent:.2f}%")

plot_intervals_inline(interval_results, output_int)


# Define a color map for congestion visualization (green -> yellow -> red)
congestion_cmap = plt.cm.get_cmap('RdYlGn_r')

# Helper function to get router coordinates from group ID
def get_router_coords(group_id):
x = group_id // NumX # Column index
y = group_id % NumY # Row index
# return x * scale_factor, ((NumY-1) - y) * scale_factor # Reverse Y-axis for visualization
return x * scale_factor, y * scale_factor # Reverse Y-axis for visualization

# Draw the mesh NoC with congestion-based links
plt.figure(figsize=(10, 8.4))
for _, row in filtered_req_noc_data.iterrows():
src_coords = get_router_coords(row['GROUP'])

# Skip invalid links based on edge and corner conditions
if row['DIR'] == 0 and src_coords[1] == 3 * scale_factor: # North link for top row routers
continue
if row['DIR'] == 1 and src_coords[0] == 3 * scale_factor: # East link for rightmost column routers
continue
if row['DIR'] == 2 and src_coords[1] == 0 * scale_factor: # South link for bottom row routers
continue
if row['DIR'] == 3 and src_coords[0] == 0 * scale_factor: # West link for leftmost column routers
continue

# Determine destination coordinates
if row['DIR'] == 0: # North
dest_coords = (src_coords[0], src_coords[1] + 1 * scale_factor)
elif row['DIR'] == 1: # East
dest_coords = (src_coords[0] + 1 * scale_factor, src_coords[1])
elif row['DIR'] == 2: # South
dest_coords = (src_coords[0], src_coords[1] - 1 * scale_factor)
elif row['DIR'] == 3: # West
dest_coords = (src_coords[0] - 1 * scale_factor, src_coords[1])
else:
continue

# Determine the congestion level and color
# congestion_level = (row['in_congestion_norm'] + row['out_congestion_norm']) / 2
congestion_level = row['out_congestion_norm'] # we only need outbound, because it is the inbound of its pair routers
link_color = congestion_cmap(congestion_level)

# Offset
granularity = 0.05
offset_x = 0
offset_y = 0
if row['DIR'] == 1 or row['DIR'] == 3:
offset_y = row['TILE'] * granularity * 2 + row['PORT'] * granularity
else:
offset_x = row['TILE'] * granularity * 2 + row['PORT'] * granularity

if row['DIR'] == 1:
offset_y += granularity * 20
elif row['DIR'] == 3:
offset_y -= granularity * 20
elif row['DIR'] == 0:
offset_x += granularity * 20
elif row['DIR'] == 2:
offset_x -= granularity * 20
else:
continue

plt.plot(
[src_coords[0] + offset_x, dest_coords[0] + offset_x],
[src_coords[1] + offset_y, dest_coords[1] + offset_y],
color=link_color,
linewidth=1,
alpha=1,
)

# Add routers as nodes
offset_dir = 0.8
for group_id in range(NumX*NumY): # 4x4 mesh
x, y = get_router_coords(group_id)
plt.scatter(x+offset_dir, y+offset_dir, color='orange', s=1200, edgecolor='black', zorder=11)
plt.text(x+offset_dir, y+offset_dir, f'R{group_id}', ha='center', va='center', fontsize=15, zorder=12)


for direction in range(4): # 4 router directions
offset_x = 0
offset_y = 0
x_2 = x
y_2 = y
offset_arrow = 0.5

# Skip invalid links based on edge and corner conditions
if y == (NumY-1)*scale_factor and direction == 0: # North link for top row routers
continue
if x == (NumX-1)*scale_factor and direction == 1: # East link for rightmost column routers
continue
if y == 0 and direction == 2: # South link for bottom row routers
continue
if x == 0 and direction == 3: # West link for leftmost column routers
continue

if direction == 0: # North link for top row routers
offset_x = 0
offset_y = scale_factor/2
x_2 = x + offset_arrow + offset_dir
elif direction == 1: # East link for rightmost column routers
offset_x = scale_factor/2
offset_y = 0
y_2 = y + offset_arrow + offset_dir
elif direction == 2: # South link for bottom row routers
offset_x = 0
offset_y = -scale_factor/2
x_2 = x - offset_arrow + offset_dir
elif direction == 3: # West link for leftmost column routers
offset_x = -scale_factor/2
offset_y = 0
y_2 = y - offset_arrow + offset_dir
else:
continue

plt.arrow(
x_2,
y_2,
offset_x,
offset_y,
head_width=1,
head_length=1,
color="black",
length_includes_head=True,
alpha=0.8,
width=0.02,
zorder=10
)



# Configure plot
if req_rsp:
plt.title(f'4x4 Mesh NoC {target} Visualization (resp network)', fontsize=16)
else:
plt.title(f'4x4 Mesh NoC {target} Visualization (req network)', fontsize=16)
plt.axis('off')
plt.colorbar(plt.cm.ScalarMappable(cmap=congestion_cmap, norm=mcolors.Normalize(vmin=0, vmax=1)),
label=f'{target} Level')

# Save the output as PNG and PDF
plt.savefig(output_png, format='png', bbox_inches='tight')
plt.savefig(output_pdf, format='pdf', bbox_inches='tight')
# plt.show()
plt.clf()
plt.close()


req_rsp = 0
for bw in range (2):
if bw == 0:
target = "congestion"
else:
target = "bw"

for req_rsp in range(2):
# Define file paths
file_path = 'spm_profiling/run_logs_f_1024/tests/router_level_profile_q_00038000.log'
# file_path = '/home/zexifu/Downloads/modified_router_level_profile.log'
output_png = f"out/mesh_noc_{target}_{req_rsp}.png"
output_pdf = f"out/mesh_noc_{target}_{req_rsp}.pdf"
output_int = f"out/mesh_noc_{target}_{req_rsp}_intreval.png"

# Call the visualization function
visualize_mesh_noc_congestion_optimized(file_path, output_png, output_pdf, output_int, req_rsp, bw)

# Define file paths
file_path = 'spm_profiling/run_logs_remap_f_1024/tests/router_level_profile_q_00038000.log'
# file_path = '/home/zexifu/Downloads/modified_router_level_profile.log'
output_png = f"out/mesh_noc_remap_{target}_{req_rsp}.png"
output_pdf = f"out/mesh_noc_remap_{target}_{req_rsp}.pdf"
output_int = f"out/mesh_noc_remap_{target}_{req_rsp}_intreval.png"

# Call the visualization function
visualize_mesh_noc_congestion_optimized(file_path, output_png, output_pdf, output_int, req_rsp, bw)
Loading

0 comments on commit 0ee6833

Please sign in to comment.