Commit a9e01189 by Dinple

updates

parent 3b030ffd
......@@ -19,4 +19,16 @@ CodeElements/EvalCT/saved_policy/run_00/111/train/*
CodeElements/EvalCT/saved_policy/run_00/111/snapshot*.plc
CodeElements/EvalCT/saved_policy/run_00/111/rl*.plc
CodeElements/EvalCT/saved_policy/run_os_64128_g657_ub5_nruns10_c5_r3_v3_rc1
CodeElements/epoch_*.plc
CodeElements/FDPlacement/test/*
CodeElements/Plc_client/placement_util*.py
CodeElements/StatTest/test/ariane/*_vs_*
CodeElements/StatTest/test/flow2_68_1.3_ct/*
CodeElements/StatTest/test/ariane/*.plc
CodeElements/*/__pycache__/*
CodeElements/*.png
CodeElements/StatTest/*.csv
CodeElements/os.txt
CodeElements/EvalCT/test/ariane/*.plc
CodeElements/FDPlacement/fd.txt
CodeElements/StatTest/test/ariane/*.pb.txt
###############################################################################################
# This script is used to place standard-cell clusters using force-directed method
# The input is protocol buffer netlist and plc file
# The input is the updated protocol buffer netlist and plc file
###############################################################################################
import os
import time
import shutil
import sys
import argparse
import matplotlib.pyplot as plt
from math import log
from math import sqrt
import json
import random
import math, statistics
# sys.path.append('../VisualPlacement/')
# from visual_placement import VisualPlacement
# ***************************************************************
# Define basic classes
# ***************************************************************
# Define the orientation map
'''
python3 -m Plc_client.plc_client_os_test --netlist ./Plc_client/test/ariane_fd/ariane.pb.txt
--plc ./Plc_client/test/ariane_fd/ariane.plc.3 --width 1599.99 --height 1598.8 --col 27 --row 23 --rpmh 70.330 --rpmv 74.510 --marh 51.790 --marv 51.790 --smooth 2
'''
OrientMap = {
"N" : "R0",
"S" : "R180",
"W" : "R90",
"E" : "R270",
"FN" : "MY",
"FS" : "MX",
"FW" : "MX90",
"FE" : "MY90"
}
# String Helper #
def print_placeholder(key, value):
line = " attr {\n"
line += f' key: "{key}"\n'
line += ' value {\n'
line += f' placeholder: {value}\n'
line += ' }\n'
line += ' }\n'
return line
def print_float(key, value):
value = round(value, 6)
line = " attr {\n"
line += f' key: "{key}"\n'
line += ' value {\n'
line += f' f: {value}\n'
line += ' }\n'
line += ' }\n'
return line
# Define the plc object
# This is a superset of attributes for different types of plc objects
# A plc object can only have some or all the attributes
# Please check Circuit Training repo (https://github.com/google-research/circuit_training/blob/main/docs/NETLIST_FORMAT.md) for detailed explanation
class PlcObject:
def __init__(self, id):
self.name = None
self.node_id = id
self.height = 0
self.width = 0
self.weight = 0
self.x = -1
self.x_offset = 0
self.y = -1
self.y_offset = 0
self.m_name = None # for macro name
self.m_node_id = -1 # the node id for macro
self.pb_type = None
self.side = None
self.orientation = None
self.inputs = []
self.disp_x = 0.0
self.disp_y = 0.0
def IsHardMacro(self):
if (self.pb_type == '"MACRO"'):
return True
else:
return False
def IsSoftMacro(self):
if (self.pb_type == '"macro"'):
return True
else:
return False
def IsFixed(self):
if (self.IsSoftMacro() == True):
return False
else:
return True
def IsPort(self):
if (self.pb_type == '"PORT"'):
return True
else:
return False
def IsPin(self):
if (self.pb_type == '"MACRO_PIN"' or self.pb_type == '"macro_pin"'):
return True
else:
return False
def GetLocation(self):
return self.x - self.width / 2.0 , self.y - self.height / 2.0
def GetPos(self):
return self.x , self.y
def GetDisp(self):
return self.disp_x, self.disp_y
def SetPos(self, xp, yp):
self.x = xp
self.y = yp
def InitDisp(self):
self.disp_x = 0.0
self.disp_y = 0.0
def AddDisp(self, f_x, f_y):
self.disp_x += f_x
self.disp_y += f_y
def SetDisp(self, f_x, f_y):
self.disp_x = f_x
self.disp_y = f_y
# limit the displacement. t is the threshold
def LimitDisp(self, t):
if (t <= 0.0):
self.disp_x = 0.0
self.disp_y = 0.0
dist = sqrt(self.disp_x * self.disp_x + self.disp_y * self.disp_y)
if (dist > t):
self.disp_x = self.disp_x / dist * t
self.disp_y = self.disp_y / dist * t
def UpdateLocation(self):
self.x += self.disp_x
self.y += self.disp_y
def GetWidth(self):
return self.width
def GetHeight(self):
return self.height
def __str__(self):
self.str = ""
if (self.IsPort() == True):
self.str += "node {\n"
self.str += ' name: ' + self.name + '\n'
for sink in self.inputs:
self.str += ' input: ' + sink + '\n'
self.str += print_placeholder('type', self.pb_type)
self.str += print_placeholder('side', self.side)
self.str += print_float('x', self.x)
self.str += print_float('y', self.y)
self.str += "}\n"
elif (self.IsPin() == True):
self.str += "node {\n"
self.str += ' name: ' + self.name + '\n'
for sink in self.inputs:
self.str += ' input: ' + sink + '\n'
self.str += print_placeholder('macro_name', self.m_name)
self.str += print_placeholder('type', self.pb_type)
if (self.weight > 1):
self.str += print_float('weight', int(self.weight))
self.str += print_float('x_offset', self.x_offset)
self.str += print_float('y_offset', self.y_offset)
self.str += print_float('x', self.x)
self.str += print_float('y', self.y)
self.str += "}\n"
else:
self.str += "node {\n"
self.str += ' name: ' + self.name + '\n'
self.str += print_placeholder('type', self.pb_type)
self.str += print_placeholder('orientation', self.orientation)
self.str += print_float('height', self.height)
self.str += print_float('width', self.width)
self.str += print_float('x', self.x)
self.str += print_float('y', self.y)
self.str += "}\n"
return self.str
def SimpleStr(self):
self.str = ""
self.str += str(self.node_id) + " "
self.str += str(round(self.x, 2)) + " "
self.str += str(round(self.y, 2)) + " "
if (self.IsPort() == True):
self.str += "- "
else:
# print("flag", self.orientation, self.node_id)
if self.orientation:
self.str += self.orientation[1:-1] + " "
else:
self.str += "N "
self.str += "0\n"
return self.str
class FDPlacement:
def __init__(self, pb_netlist_file, plc_file):
self.pb_netlist_file = pb_netlist_file
self.plc_file = plc_file
self.objects = [] # store all the plc objects
self.ports = [] # store the node_id for ports
self.macros = [] # store the node_id for hard macros
self.stdcell_clusters = [] # store the node_id for standard-cell clusters
self.adj_matrix = { } # store the adjacency matrix between nodes
self.n_rows = 0
self.n_cols = 0
self.canvas_width = 0.0
self.canvas_height = 0.0
self.grid_width = 0.0
self.grid_height = 0.0
self.grid_width = 0.0
self.grid_height = 0.0
self.init_flag = True
# force-related parameters
self.attractive_factor = 0.1
self.attractive_exponent = 1
self.repulsive_factor = 0.1
# self.repulsive_exponent = 4
self.max_displacement = 10.0
self.io_factor = 10.0
### User-specified parameters
self.num_steps = []
self.move_distance_factors = []
self.attract_factor = []
self.repel_factor = []
### Read the initial files
self.pb_netlist_header = ""
self.plc_header = ""
self.ParseProtocolBufferNetlist()
self.ParsePlcFile()
# Parse protocol buffer netlist
def ParseProtocolBufferNetlist(self):
plc_object_id_map = { } # map name to node_id
# read protocol buffer netlist
float_values = ['"height"', '"weight"', '"width"', '"x"', '"x_offset"', '"y"', '"y_offset"']
placeholders = ['"macro_name"', '"orientation"', '"side"', '"type"']
with open(self.pb_netlist_file) as f:
content = f.read().splitlines()
f.close()
object_id = 0
key = ""
header = []
for line in content:
header.append(line)
words = line.split()
if words[0] == 'node':
if len(self.objects) > 0 and self.objects[-1].name == '"__metadata__"':
self.objects.pop(-1)
object_id -= 1
for i in range(len(header) - 1):
self.pb_netlist_header += header[i] + "\n"
self.objects.append(PlcObject(object_id)) # add object
object_id += 1
elif words[0] == 'name:':
self.objects[-1].name = words[1]
elif words[0] == 'input:':
self.objects[-1].inputs.append(words[1])
elif words[0] == 'key:' :
key = words[1] # the attribute name
elif words[0] == 'placeholder:' :
if key == placeholders[0]:
self.objects[-1].m_name = words[1]
elif key == placeholders[1]:
# print("flag parse:", words)
self.objects[-1].orientation = words[1]
elif key == placeholders[2]:
self.objects[-1].side = words[1]
elif key == placeholders[3]:
self.objects[-1].pb_type = words[1]
elif words[0] == 'f:' :
if key == float_values[0]:
self.objects[-1].height = round(float(words[1]), 6)
elif key == float_values[1]:
self.objects[-1].weight = round(float(words[1]), 6)
elif key == float_values[2]:
self.objects[-1].width = round(float(words[1]), 6)
elif key == float_values[3]:
self.objects[-1].x = round(float(words[1]),6)
elif key == float_values[4]:
self.objects[-1].x_offset = round(float(words[1]), 6)
elif key == float_values[5]:
self.objects[-1].y = round(float(words[1]),6)
elif key == float_values[6]:
self.objects[-1].y_offset = round(float(words[1]), 6)
# Get all the macros, standard-cell clusters and IO ports
for plc_object in self.objects:
plc_object_id_map[plc_object.name] = plc_object.node_id
if (plc_object.IsHardMacro() == True):
self.macros.append(plc_object.node_id)
elif (plc_object.IsSoftMacro() == True):
self.stdcell_clusters.append(plc_object.node_id)
elif (plc_object.IsPort() == True):
self.ports.append(plc_object.node_id)
else:
pass
# Map macro pin with its macro
for plc_object in self.objects:
if (plc_object.IsPin() == True):
plc_object.m_node_id = plc_object_id_map[plc_object.m_name]
else:
plc_object.m_node_id = plc_object.node_id
# Calculate adjacency matrix
for macro in self.macros:
self.adj_matrix[macro] = set()
for stdcell_cluster in self.stdcell_clusters:
self.adj_matrix[stdcell_cluster] = set()
for port in self.ports:
self.adj_matrix[port] = set()
# We use star model to calculate adjacent matrix
# Get connections between nodes
for plc_object in self.objects:
driver = plc_object.m_node_id
sinks = set()
for sink in plc_object.inputs:
sinks.add(self.objects[plc_object_id_map[sink]].m_node_id)
# update adjacency matrix
for sink in sinks:
self.adj_matrix[driver].add(sink)
self.adj_matrix[sink].add(driver)
print("***************************************************")
print("num_macros = ", len(self.macros))
print("num_stdcell_clusters = ", len(self.stdcell_clusters))
print("num_ports = ", len(self.ports))
# Parse plc file
def ParsePlcFile(self):
# read plc file for all the plc objects
with open(self.plc_file) as f:
content = f.read().splitlines()
f.close()
# read the canvas and grid information
for line in content:
items = line.split()
if (len(items) > 2 and items[0] == "#" and items[1] == "Columns"):
self.n_cols = int(items[3])
self.n_rows = int(items[6])
elif (len(items) > 2 and items[0] == "#" and items[1] == "Width"):
self.canvas_width = float(items[3])
self.canvas_height = float(items[6])
if (len(items) > 0 and items[0] == "#"):
self.plc_header += line + "\n"
print("***************************************************")
print("canvas_width = ", self.canvas_width)
print("canvas_height = ", self.canvas_height)
print("n_cols = ", self.n_cols)
print("n_rows = ", self.n_rows)
self.grid_width = self.canvas_width / self.n_cols
self.grid_height = self.canvas_height / self.n_rows
# if overlap dont do anything
def ifOverlap(self, u_i, v_i, ux=0, uy=0, vx=0, vy=0):
u_side = self.make_square(u_i)
u_x1 = self.objects[u_i].x + ux - u_side/2 # left
u_x2 = self.objects[u_i].x + ux + u_side/2 # right
u_y1 = self.objects[u_i].y + uy + u_side/2 # top
u_y2 = self.objects[u_i].y + uy - u_side/2 # bottom
v_side = self.make_square(v_i)
v_x1 = self.objects[v_i].x + vx - v_side/2 # left
v_x2 = self.objects[v_i].x + vx + v_side/2 # right
v_y1 = self.objects[v_i].y + vy + v_side/2 # top
v_y2 = self.objects[v_i].y + vy - v_side/2 # bottom
return u_x1 < v_x2 and u_x2 > v_x1 and u_y1 > v_y2 and u_y2 < v_y1
# Define the forces
# Attractive force
# x is the distance between two vertices
def func_a(self, x, io_flag = True):
if (io_flag == True):
return self.io_factor * (x ** self.attractive_exponent)
else:
return self.attractive_factor * (x ** self.attractive_exponent)
# Calculate attractive force between two nodes (u, v are node_ids) (u - v)
def f_a(self, u, v, io_flag = True):
# distance should consider dimension as well
x_dist = self.objects[u].x - self.objects[v].x - self.make_square(u)/2 - self.make_square(v)/2
y_dist = self.objects[u].y - self.objects[v].y - self.make_square(u)/2 - self.make_square(v)/2
dist = sqrt(x_dist * x_dist + y_dist * y_dist)
if (dist <= 0.0 or self.ifOverlap(u, v)):
return 0.0, 0.0
else:
f = self.func_a(dist, io_flag)
# enforece distance not as close as we hope
f_x = x_dist / dist * f
f_y = y_dist / dist * f
return f_x, f_y
def make_square(self, u):
return sqrt(self.objects[u].width * self.objects[u].height)
# Repulsive force
# x is the distance between two vertices
# def func_r(self, x):
# return self.repulsive_factor / (x ** self.repulsive_exponent)
# Calculate repulsive force between two nodes (u, v are node_ids) (u - v)
# def f_r(self, u, v):
# if (self.repulsive_factor == 0.0):
# return 0.0, 0.0
# x_dist = self.objects[u].x - self.objects[v].x - self.make_square(u)/2 - self.make_square(v)/2
# y_dist = self.objects[u].y - self.objects[v].y - self.make_square(u)/2 - self.make_square(v)/2
# dist = sqrt(x_dist * x_dist + y_dist * y_dist)
# if (dist <= 1e-10):
# return sqrt(self.repulsive_factor), sqrt(self.repulsive_factor)
# else:
# return sqrt(self.repulsive_factor) * x_dist * 2, sqrt(self.repulsive_factor) * y_dist * 2
# # else:
def f_r(self, u, v):
if (self.repulsive_factor == 0.0):
return 0.0, 0.0
x_dist = self.objects[u].x - self.objects[v].x
y_dist = self.objects[u].y - self.objects[v].y
dist = sqrt(x_dist * x_dist + y_dist * y_dist)
if (dist <= 1e-10):
return sqrt(self.repulsive_factor), sqrt(self.repulsive_factor)
else:
f_x = self.repulsive_factor * x_dist / dist
f_y = self.repulsive_factor * y_dist / dist
return f_x, f_y
# f_x = self.repulsive_factor * x_dist / dist
# f_y = self.repulsive_factor * y_dist / dist
# return f_x, f_y
def f_r_m(self, u, v):
if (self.repulsive_factor == 0.0):
return 0.0, 0.0
x_dist = self.objects[u].x - self.objects[v].x
y_dist = self.objects[u].y - self.objects[v].y
dist = sqrt(x_dist * x_dist + y_dist * y_dist)
if (dist <= 1e-10):
# print("dist found", dist)
return x_dist / dist * (self.make_square(u)/2 + self.make_square(v)/2), \
y_dist / dist * (self.make_square(u)/2 + self.make_square(v)/2)
elif self.ifOverlap(u, v):
# if overlap, double the force? keep x
# print("overlap found", dist)
return x_dist / dist * (self.make_square(u)/2 + self.make_square(v)/2), \
y_dist / dist * (self.make_square(u)/2 + self.make_square(v)/2)
else:
return 0.0, 0.0
# Pull the objects to the nearest center of the gridcell
def RoundCenter(self, object_id):
col_id = round((self.objects[object_id].x - self.grid_width / 2.0) / self.grid_width)
if (col_id < 0):
col_id = 0
elif (col_id > self.n_cols - 1):
col_id = self.n_cols - 1
self.objects[object_id].x = (col_id + 0.5) * self.grid_width
row_id = round((self.objects[object_id].y - self.grid_height / 2.0) / self.grid_height)
if (row_id < 0):
row_id = 0
elif (row_id > self.n_rows - 1):
row_id = self.n_rows - 1
self.objects[object_id].y = (row_id + 0.5) * self.grid_height
def centeralize(self, object_id):
self.objects[object_id].SetPos(self.canvas_width / 2, self.canvas_height / 2)
def centeralize_circle(self, object_id):
r = 1 * sqrt(random.random())
theta = random.random() * 2 * math.pi
centerX = self.canvas_width / 2
centerY = self.canvas_height / 2
self.objects[object_id].SetPos(centerX + r * math.cos(theta), centerY + r * math.sin(theta))
# Make sure all the clusters are placed within the canvas
def FitCanvas(self, object_id):
if (self.objects[object_id].x <= 0.0):
self.objects[object_id].x = 0.0
if (self.objects[object_id].x >= self.canvas_width):
self.objects[object_id].x = self.canvas_width
if (self.objects[object_id].y <= 0.0):
self.objects[object_id].y = 0.0
if (self.objects[object_id].y >= self.canvas_height):
self.objects[object_id].y = self.canvas_height
def shift_sigmoid(self, x, a = 100):
return 1/(1 + math.exp(-x + a))
def shift_elu(self, x, shift = 100, a=0.3):
if x > shift + a:
return x
else:
return a * math.exp(x-shift)
def piecewise_leaky_relu(self, x, shift = 100, a = 0.1):
if x >= 0:
return max(a * (x - shift), (x - shift)) + a * shift
else:
return -max(a * (x - shift), (x - shift)) - a * shift
def piecewise_sigmoid(self, x, shift = 50):
if x >= 0:
return 1/(math.exp(-x+shift) + 1)
else:
return -1/(math.exp(x+shift) + 1)
# Force-directed Placement for standard-cell clusters
def Placement(self):
# initialize the displacement for standard-cell clusters
for cluster in self.stdcell_clusters:
self.objects[cluster].InitDisp()
# calculate the repulsive forces
# repulsive forces between stdcell clusters
################################################################################################################
# if False:
if self.repulsive_factor != 0.0:
xr_collection = [0] * len(self.objects)
yr_collection = [0] * len(self.objects)
for u in self.stdcell_clusters:
# randomize which stdcell to
for v in self.stdcell_clusters:
if (u <= v): # we just need calculate once
continue
f_x, f_y = self.f_r(u, v)
# self.objects[u].AddDisp(self.objects[u].height * self.objects[u].width * 200 * f_x, self.objects[u].height * self.objects[u].width * 200 * f_y)
# self.objects[v].AddDisp(self.objects[v].height * self.objects[v].width * -200 * f_x, self.objects[v].height * self.objects[v].width * -200 * f_y)
xr_collection[u] += 1.0 * f_x
yr_collection[u] += 1.0* f_y
xr_collection[v] += -1.0 * f_x
yr_collection[v] += -1.0 * f_y
# self.objects[u].AddDisp(1 * f_x, 1 * f_y)
# self.objects[v].AddDisp(-1 * f_x, -1 * f_y)
max_x_disp, max_y_disp = (0.0, 0.0)
min_x_disp, min_y_disp = (0.0, 0.0)
# limit max displacement to threshold : self.max_displacement
for xr, yr in zip(xr_collection, yr_collection):
if xr != 0.0:
max_x_disp = max(max_x_disp, abs(xr))
min_x_disp = min(min_x_disp, abs(xr))
if yr != 0.0:
max_y_disp = max(max_y_disp, abs(yr))
min_y_disp = min(min_y_disp, abs(yr))
scaling = 2.0
for cluster in self.stdcell_clusters:
# self.objects[cluster].AddDisp((2*((xr_collection[cluster]-min_x_disp)/(max_x_disp - min_x_disp)) - 1),
# (2*((yr_collection[cluster]-min_y_disp)/(max_y_disp-min_y_disp)) - 1))
# keeping the sign while normalize
self.objects[cluster].AddDisp(scaling * xr_collection[cluster] / max_x_disp, scaling * yr_collection[cluster]/ max_y_disp)
################################################################################################################
# if False:
xr_collection = [0] * len(self.objects)
yr_collection = [0] * len(self.objects)
# repulsive forces between stdcell clusters and macros
for u in self.stdcell_clusters:
for v in self.macros:
f_x, f_y = self.f_r_m(u, v)
# self.objects[u].AddDisp(5 * f_x, 5 * f_y)
xr_collection[u] += 1.0 * f_x
yr_collection[u] += 1.0 * f_y
max_x_disp, max_y_disp = (0.0, 0.0)
min_x_disp, min_y_disp = (0.0, 0.0)
# limit max displacement to threshold : self.max_displacement
for xr, yr in zip(xr_collection, yr_collection):
if xr != 0.0:
max_x_disp = max(max_x_disp, abs(xr))
min_x_disp = min(min_x_disp, abs(xr))
if yr != 0.0:
max_y_disp = max(max_y_disp, abs(yr))
min_y_disp = min(min_y_disp, abs(yr))
if max_x_disp == 0.0:
max_x_disp = 1.0
if max_y_disp == 0.0:
max_y_disp = 1.0
scaling = 4.0
for cluster in self.stdcell_clusters:
# self.objects[cluster].AddDisp((2*((xr_collection[cluster]-min_x_disp)/(max_x_disp - min_x_disp)) - 1),
# (2*((yr_collection[cluster]-min_y_disp)/(max_y_disp-min_y_disp)) - 1))
# keeping the sign while normalize
self.objects[cluster].AddDisp(scaling * xr_collection[cluster] / max_x_disp, scaling * yr_collection[cluster]/ max_y_disp)
################################################################################################################
# calculate the attractive force traverse each edge
# the adj_matrix is a symmetric matrix
if self.attractive_factor != 0:
# if False:
xr_collection = [0] * len(self.objects)
yr_collection = [0] * len(self.objects)
for driver, sinks in self.adj_matrix.items():
if (self.objects[driver].IsSoftMacro() == False):
continue
for sink in sinks:
if self.ifOverlap(driver, sink):
# if overlapped, no changes
# print("overlap intially")
continue
f_x, f_y = self.f_a(driver, sink)
if self.ifOverlap(driver, sink, self.piecewise_sigmoid(-1 * f_x), self.piecewise_sigmoid(-1 * f_y)):
# if overlapped after moving, no changes
# print("overlap after change")
continue
xr_collection[driver] += self.piecewise_sigmoid(-1.0 * f_x)
yr_collection[driver] += self.piecewise_sigmoid(-1.0 * f_y)
max_x_disp, max_y_disp = (0.0, 0.0)
min_x_disp, min_y_disp = (0.0, 0.0)
# limit max displacement to threshold : self.max_displacement
for xr, yr in zip(xr_collection, yr_collection):
if xr != 0.0:
max_x_disp = max(max_x_disp, abs(xr))
min_x_disp = min(min_x_disp, abs(xr))
if yr != 0.0:
max_y_disp = max(max_y_disp, abs(yr))
min_y_disp = min(min_y_disp, abs(yr))
# not too much attract
scaling = 0.1
for cluster in self.stdcell_clusters:
# self.objects[cluster].AddDisp((2*((xr_collection[cluster]-min_x_disp)/(max_x_disp - min_x_disp)) - 1),
# (2*((yr_collection[cluster]-min_y_disp)/(max_y_disp-min_y_disp)) - 1))
# keeping the sign while normalize
# self.objects[cluster].AddDisp(scaling * xr_collection[cluster] / max_x_disp, scaling * yr_collection[cluster]/ max_y_disp)
self.objects[cluster].AddDisp(scaling * xr_collection[cluster]/max_x_disp, scaling * yr_collection[cluster]/max_x_disp)
# self.objects[cluster].LimitDisp(self.max_displacement)
################################################################################################################
# push all the macros to the nearest center of gridcell
for cluster in self.stdcell_clusters:
self.objects[cluster].UpdateLocation()
#self.RoundCenter(cluster)
# Run placement
def Run(self):
# initialize the position for all the macros and stdcell clusters
if (self.init_flag == True):
for cluster in self.stdcell_clusters:
# self.RoundCenter(cluster)
# self.centeralize(cluster)
self.centeralize_circle(cluster)
for macro in self.macros:
self.RoundCenter(macro)
# check the initial parameter setting first
flag = True
flag = flag and (len(self.num_steps) > 0)
flag = flag and (len(self.num_steps) == len(self.move_distance_factors))
flag = flag and (len(self.num_steps) == len(self.attract_factor))
flag = flag and (len(self.num_steps) == len(self.repel_factor))
if (flag == False):
print("**************************************************")
print("Error ! Please check your inputs.")
exit()
# Write initial files
self.WritePbNetlist(self.pb_netlist_file + ".0")
self.WritePlcFile(self.plc_file + ".0")
print("*******************************************************")
print("Start Force-directed Placement")
print("\n")
for i in range(len(self.num_steps)):
print("************************************************")
print("Start Call - ", i + 1)
self.attractive_factor = self.attract_factor[i]
self.repulsive_factor = self.repel_factor[i]
factor = self.move_distance_factors[i] * max(self.canvas_width, self.canvas_height)
self.num_step = self.num_steps[i]
self.max_displacement = factor / self.num_step
print("[INFO] attractive_factor = ", self.attractive_factor)
print("[INFO] repulsive_factor = ", self.repulsive_factor)
print("[INFO] max_displaccment = ", self.max_displacement)
print("[INFO] num_step = ", self.num_step)
print("[INFO] io_factor = ", self.io_factor)
for j in range(self.num_step):
print("Runing Step ", j)
self.Placement()
# Based on our understanding, the stdcell clusters can be placed
# at any place in the canvas instead of the center of gridcells
#for cluster in self.stdcell_clusters:
# self.RoundCenter(cluster)
for cluster in self.stdcell_clusters:
self.FitCanvas(cluster)
self.WritePbNetlist(self.pb_netlist_file + "." + str(i + 1))
self.WritePlcFile(self.plc_file + "." + str(i + 1))
self.WritePbNetlist(self.pb_netlist_file + ".final")
self.WritePlcFile(self.plc_file + ".final")
# Write the output_file
def WritePbNetlist(self, netlist_file):
f = open(netlist_file, "w")
f.write(self.pb_netlist_header)
for pb_object in self.objects:
f.write(str(pb_object))
f.close()
def WritePlcFile(self, plc_file):
f = open(plc_file, "w")
f.write(self.plc_header)
for pb_object in self.objects:
if (pb_object.IsPin() == False):
f.write(pb_object.SimpleStr())
f.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--netlist", help="protocol buffer netlist", type = str, default = "./test/ariane.pb.txt")
parser.add_argument("--plc", help="plc_file", type = str, default = "./test/ariane.plc")
args = parser.parse_args()
netlist_file = args.netlist
plc_file = args.plc
io_factor = 1.0
num_steps = [100, 100, 100]
move_distance_factors = [1.0, 1.0, 1.0]
attract_factor = [100.0, 1.0e-3, 1.0e-3]
repel_factor = [0.0, 1.0e6, 1.0e7]
# Run force-directed placement
placement = FDPlacement(netlist_file, plc_file)
placement.io_factor = io_factor
placement.num_steps = num_steps
placement.move_distance_factors = move_distance_factors
placement.attract_factor = attract_factor
placement.repel_factor = repel_factor
placement.Run()
# Visual results
iterations = 1 + len(num_steps)
for i in range(iterations):
print("*********************************************")
print("Iteration ", i)
print("\n\n")
tp_netlist_file = netlist_file + "." + str(i)
tp_plc_file = plc_file + "." + str(i)
# VisualPlacement(tp_netlist_file, tp_plc_file)
......@@ -10,6 +10,7 @@ import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
import traceback, sys
import random
"""plc_client_os docstrings.
......@@ -2385,6 +2386,26 @@ class PlacementCost(object):
'''
FD Placement below shares the same functionality as the FDPlacement/fd_placement.py
'''
def __ifOverlap(self, u_i, v_i, ux=0, uy=0, vx=0, vy=0):
'''
Detect if the two modules are overlapping or not (w/o using block structure)
'''
# extract first macro
u_side = self.modules_w_pins[u_i].get_height()
u_x1 = self.modules_w_pins[u_i].get_pos()[0] + ux - u_side/2 # left
u_x2 = self.modules_w_pins[u_i].get_pos()[0] + ux + u_side/2 # right
u_y1 = self.modules_w_pins[u_i].get_pos()[1] + uy + u_side/2 # top
u_y2 = self.modules_w_pins[u_i].get_pos()[1] + uy - u_side/2 # bottom
# extract second macro
v_side = self.modules_w_pins[v_i].get_height()
v_x1 = self.modules_w_pins[v_i].get_pos()[0] + vx - v_side/2 # left
v_x2 = self.modules_w_pins[v_i].get_pos()[0] + vx + v_side/2 # right
v_y1 = self.modules_w_pins[v_i].get_pos()[1] + vy + v_side/2 # top
v_y2 = self.modules_w_pins[v_i].get_pos()[1] + vy - v_side/2 # bottom
return u_x1 < v_x2 and u_x2 > v_x1 and u_y1 > v_y2 and u_y2 < v_y1
def __repulsive_force(self, repel_factor, node_i, node_j):
'''
Calculate repulsive force between two nodes node_i, node_j
......@@ -2408,12 +2429,41 @@ class PlacementCost(object):
hypo_dist = math.sqrt(x_dist**2 + y_dist**2)
# compute force in x and y direction
if hypo_dist <= 1e-5:
if hypo_dist <= 1e-10:
return math.sqrt(repel_factor), math.sqrt(repel_factor)
else:
f_x = repel_factor * x_dist / hypo_dist
f_y = repel_factor * y_dist / hypo_dist
return f_x, f_y
def __repulsive_force_hard_macro(self, repel_factor, h_node_i, s_node_j):
'''
Calculate repulsive force between hard macro and soft macro
'''
if repel_factor == 0.0:
return 0.0, 0.0
# retrieve module instance
h_mod_i = self.modules_w_pins[h_node_i]
s_mod_j = self.modules_w_pins[s_node_j]
# retrieve module position
x_i, y_i = h_mod_i.get_pos()
x_j, y_j = s_mod_j.get_pos()
# get dist between x and y
x_dist = x_i - x_j
y_dist = y_i - y_j
# get dist of hypotenuse
hypo_dist = math.sqrt(x_dist**2 + y_dist**2)
# compute force in x and y direction
if hypo_dist <= 1e-10 or self.__ifOverlap(h_node_i, s_node_j):
return x_dist/hypo_dist * (h_mod_i.get_height()/2 + s_mod_j.get_height()/2),\
y_dist/hypo_dist * (h_mod_i.get_height()/2 + s_mod_j.get_height()/2)
else:
return 0.0, 0.0
def __attractive_force(self, io_factor, attract_factor, node_i, node_j, io_flag = True, attract_exponent = 1):
'''
......@@ -2428,14 +2478,14 @@ class PlacementCost(object):
x_j, y_j = mod_j.get_pos()
# get dist between x and y
x_dist = x_i - x_j
y_dist = y_i - y_j
x_dist = x_i - x_j - mod_i.get_height()/2 - mod_j.get_height()/2
y_dist = y_i - y_j - mod_i.get_height()/2 - mod_j.get_height()/2
# get dist of hypotenuse
hypo_dist = math.sqrt(x_dist**2 + y_dist**2)
# compute force in x and y direction
if hypo_dist <= 0.0:
if hypo_dist <= 0.0 or self.__ifOverlap(u_i=node_i, v_i=node_j):
return 0.0, 0.0
else:
if io_flag:
......@@ -2469,6 +2519,16 @@ class PlacementCost(object):
row = self.grid_row - 1
mod.set_pos((col + 0.5) * self.grid_width, (row + 0.5) * self.grid_height)
def __centeralize_circle(self, mod_id):
'''
Pull the modules to a randomized unit circle in the center of the canvas
'''
r = 1 * math.sqrt(random.random())
theta = random.random() * 2 * math.pi
centerX = self.width / 2
centerY = self.height / 2
self.modules_w_pins[mod_id].set_pos(centerX + r * math.cos(theta), centerY + r * math.sin(theta))
def __boundary_check(self, mod_id):
'''
......@@ -2501,64 +2561,145 @@ class PlacementCost(object):
soft_macro_disp[mod_idx] = [0.0, 0.0]
def add_displace(mod_id, x_disp, y_disp):
'''
Add the displacement
'''
soft_macro_disp[mod_id][0] += x_disp
soft_macro_disp[mod_id][1] += y_disp
# limit the displacement. max_displace is the threshold
def limit_displace(max_displace, mod_id):
if max_displace <= 0.0:
soft_macro_disp[mod_id][0] = 0.0
soft_macro_disp[mod_id][1] = 0.0
# get dist of hypotenuse
hypo_dist = math.sqrt(soft_macro_disp[mod_id][0]**2 + soft_macro_disp[mod_id][1]**2)
if hypo_dist > max_displace:
soft_macro_disp[mod_id][0] = soft_macro_disp[mod_id][0] / hypo_dist * max_displace
soft_macro_disp[mod_id][1] = soft_macro_disp[mod_id][1] / hypo_dist * max_displace
def update_location(mod_id, x_disp, y_disp):
'''
Update the displacement to the coordiante
'''
x_pos, y_pos = self.modules_w_pins[mod_id].get_pos()
# logging.info("{} {} {} {}".format(x_pos, y_pos, x_disp, y_disp))
self.modules_w_pins[mod_id].set_pos(x_pos + x_disp, y_pos + y_disp)
def piecewise_sigmoid(x, shift = 50):
if x >= 0:
return 1/(math.exp(-x+shift) + 1)
else:
return -1/(math.exp(x+shift) + 1)
##SOFT_SOFT REPEL###############################################################################################
# calculate the repulsive forces
# repulsive forces between stdcell clusters
for mod_i in self.soft_macro_indices:
for mod_j in self.soft_macro_indices:
if (mod_i <= mod_j):
continue
repul_x, repul_y = self.__repulsive_force(repel_factor=repel_factor,
node_i=mod_i, node_j=mod_j)
add_displace(mod_i, repul_x, repul_y)
add_displace(mod_j, -1.0 * repul_x, -1.0 * repul_y)
# repulsive forces between stdcell clusters and macros
for mod_i in self.soft_macro_indices:
for mod_j in self.hard_macro_indices:
repul_x, repul_y = self.__repulsive_force(repel_factor=repel_factor,
node_i=mod_i, node_j=mod_j)
add_displace(mod_i, repul_x, repul_y)
# calculate the attractive force
# traverse each edge
# the adj_matrix is a symmetric matrix
for driver_pin_idx, driver_pin in enumerate(self.modules_w_pins):
# only for soft macro
if driver_pin_idx in self.soft_macro_pin_indices and driver_pin.get_sink():
driver_mod_idx = self.get_ref_node_id(driver_pin_idx)
for sink_pin_name in driver_pin.get_sink().keys():
sink_mod_idx = self.mod_name_to_indices[sink_pin_name]
# print(self.modules_w_pins[sink_mod_idx].get_type())
attrac_x, attrac_y = self.__attractive_force(io_factor=io_factor,
attract_factor=attract_factor,
node_i=driver_mod_idx,
node_j=sink_mod_idx
)
add_displace(driver_mod_idx, -1.0 * attrac_x, -1.0 * attrac_y)
if repel_factor != 0.0:
# temp storing the soft macro count
xr_collection = [0] * len(self.modules_w_pins)
yr_collection = [0] * len(self.modules_w_pins)
# repulsive forces between stdcell clusters and stdcell clusters
for mod_i in self.soft_macro_indices:
for mod_j in self.soft_macro_indices:
if (mod_i <= mod_j):
continue
repul_x, repul_y = self.__repulsive_force(repel_factor=repel_factor,
node_i=mod_i, node_j=mod_j)
xr_collection[mod_i] += 1.0 * repul_x
yr_collection[mod_i] += 1.0 * repul_y
xr_collection[mod_j] += -1.0 * repul_x
yr_collection[mod_j] += -1.0 * repul_y
# finding max x y displacement
max_x_disp, max_y_disp = (0.0, 0.0)
for xr, yr in zip(xr_collection, yr_collection):
if xr != 0.0:
max_x_disp = max(max_x_disp, abs(xr))
if yr != 0.0:
max_y_disp = max(max_y_disp, abs(yr))
# prevent zero division
if max_x_disp == 0.0:
max_x_disp = 1.0
if max_y_disp == 0.0:
max_y_disp = 1.0
scaling = 2.0
for mod_idx in self.soft_macro_indices:
add_displace(mod_idx, scaling * xr_collection[mod_idx] / max_x_disp, scaling * yr_collection[mod_idx] / max_y_disp)
##SOFT_HARD REPEL###############################################################################################
if repel_factor != 0.0:
# temp storing the soft macro count
xr_collection = [0] * len(self.modules_w_pins)
yr_collection = [0] * len(self.modules_w_pins)
# repulsive forces between stdcell clusters and macros
for mod_i in self.soft_macro_indices:
for mod_j in self.hard_macro_indices:
repul_x, repul_y = self.__repulsive_force_hard_macro(repel_factor=repel_factor,
h_node_i=mod_i, s_node_j=mod_j)
xr_collection[mod_i] += 1.0 * repul_x
yr_collection[mod_i] += 1.0 * repul_y
# finding max x y displacement
max_x_disp, max_y_disp = (0.0, 0.0)
for xr, yr in zip(xr_collection, yr_collection):
if xr != 0.0:
max_x_disp = max(max_x_disp, abs(xr))
if yr != 0.0:
max_y_disp = max(max_y_disp, abs(yr))
# prevent zero division
if max_x_disp == 0.0:
max_x_disp = 1.0
if max_y_disp == 0.0:
max_y_disp = 1.0
scaling = 4.0
for mod_idx in self.soft_macro_indices:
add_displace(mod_idx, scaling * xr_collection[mod_idx] / max_x_disp, scaling * yr_collection[mod_idx] / max_y_disp)
##NET ATTRACT###################################################################################################
if attract_factor != 0.0:
# temp storing the soft macro count
xr_collection = [0] * len(self.modules_w_pins)
yr_collection = [0] * len(self.modules_w_pins)
# calculate the attractive force
# traverse each edge
# the adj_matrix is a symmetric matrix
for driver_pin_idx, driver_pin in enumerate(self.modules_w_pins):
# only for soft macro
if driver_pin_idx in self.soft_macro_pin_indices and driver_pin.get_sink():
driver_mod_idx = self.get_ref_node_id(driver_pin_idx)
for sink_pin_name in driver_pin.get_sink().keys():
sink_mod_idx = self.mod_name_to_indices[sink_pin_name]
# if overlapped, dont attract further
if self.__ifOverlap(driver_mod_idx, sink_mod_idx):
continue
attrac_x, attrac_y = self.__attractive_force(io_factor=io_factor,
attract_factor=attract_factor,
node_i=driver_mod_idx,
node_j=sink_mod_idx
)
# if overlapped, dont attract further
if self.__ifOverlap(driver_mod_idx, sink_mod_idx, attrac_x, attrac_y):
continue
xr_collection[driver_mod_idx] += piecewise_sigmoid(-1.0 * attrac_x)
yr_collection[driver_mod_idx] += piecewise_sigmoid(-1.0 * attrac_y)
# finding max x y displacement
max_x_disp, max_y_disp = (0.0, 0.0)
for xr, yr in zip(xr_collection, yr_collection):
if xr != 0.0:
max_x_disp = max(max_x_disp, abs(xr))
if yr != 0.0:
max_y_disp = max(max_y_disp, abs(yr))
# prevent zero division
if max_x_disp == 0.0:
max_x_disp = 1.0
if max_y_disp == 0.0:
max_y_disp = 1.0
# not too much attract
scaling = 0.1
for mod_idx in self.soft_macro_indices:
add_displace(mod_idx, scaling * xr_collection[mod_idx] / max_x_disp, scaling * yr_collection[mod_idx] / max_y_disp)
for mod_idx in soft_macro_disp.keys():
# limit max displacement to threshold : max_displacement
limit_displace(max_displace=max_displacement, mod_id=mod_idx)
# push all the macros to the nearest center of gridcell
update_location(mod_idx, *soft_macro_disp[mod_idx])
# Moved to here to save a for loop
......@@ -2575,7 +2716,7 @@ class PlacementCost(object):
# initialize the position for all the macros and stdcell clusters
# YW: here I will ignore centering Macros since CT placement does that
for mod_idx in self.soft_macro_indices:
self.__centralize(mod_id = mod_idx)
self.__centeralize_circle(mod_id = mod_idx)
for epoch_id, iterations in enumerate(num_steps):
logging.info("#[OPTIMIZING STDCELs] at num_step {}:".format(str(epoch_id)))
......@@ -2611,6 +2752,12 @@ class PlacementCost(object):
def get_orientation(self):
return self.orientation
def get_height(self):
return 0
def get_width(self):
return 0
def add_connection(self, module_name):
# NOTE: assume PORT names does not contain slash
......
......@@ -79,6 +79,18 @@ Example:
--marv 5\
--smooth 2
$ python3 -m Plc_client.plc_client_os_test --netlist ./Plc_client/test/ariane_fd/ariane.pb.txt\
--plc ./Plc_client/test/ariane_fd/ariane.plc\
--width 1599.99\
--height 1598.8\
--col 27\
--row 23\
--rpmh 70.330\
--rpmv 74.510\
--marh 51.790\
--marv 51.790\
--smooth 2
Todo:
* Clean up code
* Extract argument from command line
......@@ -247,6 +259,7 @@ class PlacementCostTest():
self.plc_os.set_canvas_size(self.CANVAS_WIDTH, self.CANVAS_HEIGHT)
self.plc_os.set_placement_grid(self.GRID_COL, self.GRID_ROW)
# show canvas
self.plc_os.make_soft_macros_square()
self.plc_os.display_canvas(annotate=False, amplify=False)
......@@ -854,6 +867,42 @@ class PlacementCostTest():
self.plc_util_os.display_canvas(annotate=False)
def test_fd(self):
print("############################ TEST GOOGLE's FD Placer ############################")
self.plc_util = placement_util.create_placement_cost(
plc_client=plc_client,
netlist_file=self.NETLIST_PATH,
init_placement=self.PLC_PATH
)
self.plc_util_os = placement_util.create_placement_cost(
plc_client=plc_client_os,
netlist_file=self.NETLIST_PATH,
init_placement=self.PLC_PATH
)
self.plc_util.set_routes_per_micron(self.RPMH, self.RPMV)
self.plc_util_os.set_routes_per_micron(self.RPMH, self.RPMV)
self.plc_util.set_macro_routing_allocation(self.MARH, self.MARV)
self.plc_util_os.set_macro_routing_allocation(self.MARH, self.MARV)
self.plc_util.set_congestion_smooth_range(self.SMOOTH)
self.plc_util_os.set_congestion_smooth_range(self.SMOOTH)
self.plc_util.set_canvas_size(self.CANVAS_WIDTH, self.CANVAS_HEIGHT)
self.plc_util.set_placement_grid(self.GRID_COL, self.GRID_ROW)
self.plc_util_os.set_canvas_size(self.CANVAS_WIDTH, self.CANVAS_HEIGHT)
self.plc_util_os.set_placement_grid(self.GRID_COL, self.GRID_ROW)
placement_util.fd_placement_schedule(self.plc_util)
for node_index in placement_util.nodes_of_types(self.plc_util, ['MACRO']):
x_pos, y_pos = self.plc_util.get_node_location(node_index)
self.plc_util_os.set_soft_macro_position(node_index, x_pos, y_pos)
self.plc_util_os.display_canvas(annotate=False, amplify=False)
def test_environment(self):
print("############################ TEST ENVIRONMENT ############################")
env = environment.CircuitEnv(
......@@ -942,6 +991,8 @@ class PlacementCostTest():
placement_util.fd_placement_schedule(self.plc_util_os)
self.plc_util_os.display_canvas(annotate=False, amplify=False)
def parse_flags(argv):
parser = argparse_flags.ArgumentParser(
description='An argparse + app.run example')
......@@ -1007,9 +1058,10 @@ def main(args):
# PCT.test_place_node()
# PCT.test_miscellaneous()
# PCT.test_observation_extractor()
PCT.view_canvas()
# PCT.view_canvas()
# PCT.test_fd()
# PCT.test_environment()
# PCT.test_fd_placement()
PCT.test_fd_placement()
if __name__ == '__main__':
......
......@@ -10,7 +10,22 @@ import pandas as pd
SHEET_ID = '1dtG4uHzdw-Lfe_Vcm5uBRNjjxXNA4gmVarTr86hjYVo'
SHEET_NAME = 'Proxy_Cost_Comparison'
url = f'https://docs.google.com/spreadsheets/d/{SHEET_ID}/gviz/tq?tqx=out:csv&sheet={SHEET_NAME}'
df = pd.read_csv(url)
print(df.head())
proxy_df = pd.read_csv(url)
proxy_df = proxy_df.loc[:, ~proxy_df.isnull().all()]
print(proxy_df.columns)
proxy_df['postCTS_Congestion (V)'] = proxy_df['postCTS_Congestion (V)'].str.rstrip('%').astype('float') / 100.0
# compute correlation between postRouteOpt_std_cell_area and density_cost
print("postRoute_std_cell_area VS. Density_Cost",
proxy_df["postRoute_std_cell_area (um^2)"].corr(proxy_df["Density_Cost"]))
print("postRouteOpt_std_cell_area VS. Density_Cost",
proxy_df["postRouteOpt_std_cell_area (um^2)"].corr(proxy_df["Density_Cost"]))
# compute correlation between ???
\ No newline at end of file
# compute correlation between postRouteOpt_wirelength (um) and wirelength_cost
print("postRoute_wirelength VS. Wirelength_Cost",
proxy_df["postRoute_wirelength (um)"].corr(proxy_df["Wirelength_Cost"]))
print("postRouteOpt_wirelength VS. Wirelength_Cost",
proxy_df["postRouteOpt_wirelength (um)"].corr(proxy_df["Wirelength_Cost"]))
# compute correlation between postCTS_Congestion and congestion_cost
print("postCTS_Congestion VS. Congestion_Cost",
proxy_df["postCTS_Congestion (V)"].corr(proxy_df["Congestion_Cost"]))
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment