Commit 7b98470b by ZhiangWang033

add SA implemented in c++

parent c510ccb8
cmake_minimum_required(VERSION 3.8)
project(sa)
include_directories(./src)
file(GLOB SOURCES "./src/*.cpp")
add_executable(${PROJECT_NAME} ${SOURCES})
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
# ---------------------------------------------------------------------------
# Run Simulated Annealing in parallel with multiple cores
# You must set up the environment correctly before you run this script
# Please check Circuit Training for environment setup
# --------------------------------------------------------------------------
import sys
import os
import subprocess
import multiprocessing
import time
from datetime import datetime
import json
import glob
sa_path = "/home/zf4_projects/macro_placer/TILOS_repo/new_repo/MacroPlacement/CodeElements/SimulatedAnnealing/build/sa"
### Wrapper function for calling Simulated Annealing
def RunSA(run_id, dir, netlist, plc_file, action_probs, num_actions, max_temperature, num_iters, seed, spiral_flag):
run_dir = dir + "/run_" + str(run_id)
os.mkdir(run_dir)
# create param_map
param_map = { }
param_map["id"] = run_id
param_map["dir"] = dir
param_map["netlist"] = netlist
param_map["plc_file"] = plc_file
param_map["action_probs"] = action_probs
param_map["num_actions"] = num_actions
param_map["max_temperature"] = max_temperature
param_map["num_iters"] = num_iters
param_map["seed"] = seed
param_map["spiral_flag"] = spiral_flag
param_file = run_dir + "/params.json"
f = open(param_file, "w")
f.write(json.dumps(param_map, indent = 4))
f.close()
design = netlist.split('/')[-1].split('.')[0]
cmd = sa_path + " " + netlist + " " + plc_file + " " + str(num_actions) + " "
cmd += str(max_temperature) + " " + str(num_iters) + " " + str(seed) + " "
cmd += str(spiral_flag) + " "
for action_prob in action_probs:
cmd += str(action_prob) + " "
cmd += design + " " + run_dir
print(cmd)
os.system(cmd)
### Run Simulated Annealing with multiple threads
def RunParallelSA(config_json_file):
# parse the configuration json file
f = open(config_json_file)
params = json.load(f)
f.close()
netlist = params["netlist"]
plc_file = params["plc_file"]
action_probs_list = params["action_probs"]
num_actions_list = params["num_actions(xn)"]
max_temperature_list = params["max_temperature"]
num_iters_list = params["num_iters"]
seed_list = params["seed"]
num_cores = params["num_cores"]
spiral_flag_list = params["spiral_flag"]
time_str = str(datetime.fromtimestamp(time.time()))
new_time_str = ""
for item in time_str.split():
new_time_str += "_" + item
result_dir ="./result" + new_time_str
os.mkdir(result_dir)
params_file = result_dir + "/params.json"
f = open(params_file, "w")
f.write(json.dumps(params, indent = 4))
f.close()
param_list = []
for action_probs in action_probs_list:
for num_actions in num_actions_list:
for max_temperature in max_temperature_list:
for num_iters in num_iters_list:
for seed in seed_list:
for spiral_flag in spiral_flag_list:
param_map = { }
param_map["id"] = len(param_list)
param_map["dir"] = result_dir
param_map["netlist"] = netlist
param_map["plc_file"] = plc_file
param_map["action_probs"] = action_probs
param_map["num_actions"] = num_actions
param_map["max_temperature"] = max_temperature
param_map["num_iters"] = num_iters
param_map["seed"] = seed
param_map["spiral_flag"] = (spiral_flag == 'True' or spiral_flag == 'TRUE')
param_list.append(param_map)
with multiprocessing.Manager() as manager:
# create lists in server memory
runtime_list = manager.list([])
cut_list = manager.list([])
remaining_runs = len(param_list)
run_id = 0
while (remaining_runs > 0):
num_thread = min(num_cores, remaining_runs)
process_list = []
for i in range(num_thread):
param_map = param_list[run_id]
p = multiprocessing.Process(target = RunSA,
args = (param_map["id"],
param_map["dir"],
param_map["netlist"],
param_map["plc_file"],
param_map["action_probs"],
param_map["num_actions"],
param_map["max_temperature"],
param_map["num_iters"],
param_map["seed"],
param_map["spiral_flag"]))
run_id = run_id + 1
p.start()
process_list.append(p)
for process in process_list:
process.join()
remaining_runs -= num_thread
summary_files = glob.glob(result_dir + "/*/*.summary")
cost_list = []
for summary_file in summary_files:
with open(summary_file) as f:
content = f.read().splitlines()
f.close()
# find the start_idx
start_idx = 0
for i in range(len(content)):
items = content[i].split()
if (len(items) == 1):
start_idx = i
break
if (len(cost_list) == 0):
for i in range(start_idx, len(content)):
cost_list.append(float(content[i]))
else:
for i in range(start_idx, len(content)):
idx = i - start_idx
if (idx >= len(cost_list)):
cost_list.append(float(content[i]))
else:
cost_list[idx] = min(cost_list[idx], float(content[i]))
f = open(result_dir + "/cost_summary.txt", "w")
for cost in cost_list:
f.write(str(cost) + "\n")
f.close()
if __name__ == "__main__":
config_file = "config.json"
RunParallelSA(config_file)
#pragma once
#include <vector>
#include <map>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <memory>
#include <random>
#include <set>
#include <cassert>
// Define the class to handle netlist in Protocol Buffer Format
// Basic data structure
// Enumerate Type
// MACROPIN for hard macro pin
// GRPIN for soft macro pin (standard-cell cluster)
// GRP for soft macro (standard-cell cluster)
enum PBTYPE { MACRO, PORT, MACROPIN, GRPPIN, GRP };
// string representation
inline std::string GetString(PBTYPE pb_type) {
switch (pb_type)
{
case MACRO:
return std::string("MACRO");
case PORT:
return std::string("PORT");
case MACROPIN:
return std::string("MACRO_PIN");
case GRPPIN:
return std::string("macro_pin");
case GRP:
return std::string("macro");
default:
return std::string("MACRO");
}
}
static const std::map<std::string, PBTYPE> PBTYPE_MAP = {
{ std::string("MACRO"), MACRO } ,
{ std::string("PORT"), PORT } ,
{ std::string("MACRO_PIN"), MACROPIN } ,
{ std::string("macro_pin"), GRPPIN } ,
{ std::string("macro"), GRP }
};
enum ORIENTATION { N, S, W, E, FN, FS, FW, FE, NONE};
// string representation
inline std::string GetString(ORIENTATION orient) {
switch (orient)
{
case N:
return std::string("N");
case S:
return std::string("S");
case W:
return std::string("W");
case E:
return std::string("E");
case FN:
return std::string("FN");
case FS:
return std::string("FS");
case FW:
return std::string("FW");
case FE:
return std::string("FE");
default:
return std::string("-");
}
}
static const std::map<std::string, ORIENTATION> ORIENT_MAP = {
{ std::string("N"), N },
{ std::string("S"), S },
{ std::string("W"), W },
{ std::string("E"), E },
{ std::string("FN"), N },
{ std::string("FS"), FS },
{ std::string("FW"), FW },
{ std::string("FE"), FE },
{ std::string("-"), NONE}
};
// ***************************************************************
// Define basic classes
// ***************************************************************
struct Rect {
float lx = 0.0;
float ly = 0.0;
float ux = 0.0;
float uy = 0.0;
Rect() {
lx = 0.0;
ly = 0.0;
ux = 0.0;
uy = 0.0;
}
Rect(float lx_, float ly_, float ux_, float uy_) {
lx = lx_;
ly = ly_;
ux = ux_;
uy = uy_;
}
};
struct GridRect {
int lx_id = 0;
int ly_id = 0;
int ux_id = 0;
int uy_id = 0;
GridRect() {
lx_id = 0;
ly_id = 0;
ux_id = 0;
uy_id = 0;
}
GridRect(int lx_id_, int ly_id_, int ux_id_, int uy_id_) {
lx_id = lx_id_;
ly_id = ly_id_;
ux_id = ux_id_;
uy_id = uy_id_;
}
};
struct LOC {
int x_id = 0;
int y_id = 0;
LOC() { }
LOC(int x_id_, int y_id_) {
x_id = x_id_;
y_id = y_id_;
}
bool IsEqual(const LOC& loc) const {
if (this->x_id == loc.x_id && this->y_id == loc.y_id)
return true;
else
return false;
}
bool operator < (const LOC& loc) const {
if (this->x_id < loc.x_id)
return true;
else if (this->x_id == loc.x_id && this->y_id < loc.y_id)
return true;
else
return false;
}
};
#pragma once
#include <vector>
#include <map>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <memory>
#include <random>
#include <set>
#include <cassert>
#include "basic_object.h"
class PBFNetlist;
// Define the Grid Structure
// The entire floorplan have been dividied into grids
class Grid {
public:
Grid() { }
Grid(float grid_width, float grid_height,
int num_cols, int num_rows,
int x_id, int y_id) {
grid_area_ = grid_width * grid_height;
x_id_ = x_id;
y_id_ = y_id;
id_ = y_id_ * num_cols + x_id_;
bbox_ = Rect(x_id * grid_width, y_id * grid_height, (x_id_ + 1) * grid_width, (y_id_ + 1) * grid_height);
x_ = (bbox_.lx + bbox_.ux) / 2.0;
y_ = (bbox_.ly + bbox_.uy) / 2.0;
}
// reset the status of grids
inline void Reset() {
overlap_area_ = 0.0;
hor_congestion_ = 0.0;
ver_congestion_ = 0.0;
macro_hor_congestion_ = 0.0;
macro_ver_congestion_ = 0.0;
smooth_hor_congestion_ = 0.0;
smooth_ver_congestion_ = 0.0;
}
// Check overlap area
inline float CalcOverlap(Rect rect) const {
const float x_overlap = std::min(bbox_.ux, rect.ux) - std::max(bbox_.lx, rect.lx);
const float y_overlap = std::min(bbox_.uy, rect.uy) - std::max(bbox_.ly, rect.ly);
if (x_overlap <= 0.0 || y_overlap <= 0.0)
return 0.0;
else
return x_overlap * y_overlap;
}
// check overlap threshold
inline std::pair<float, float> CalcHVOverlap(Rect rect) const {
const float x_overlap = std::min(bbox_.ux, rect.ux) - std::max(bbox_.lx, rect.lx);
const float y_overlap = std::min(bbox_.uy, rect.uy) - std::max(bbox_.ly, rect.ly);
return std::pair<float, float>(std::max(x_overlap, 0.0f),
std::max(y_overlap, 0.0f));
}
// For Macro and Standard-cell cluster
// update overlap: True for Add and False for Reduce
inline void UpdateOverlap(Rect rect, bool flag = false) {
const float x_overlap = std::min(bbox_.ux, rect.ux) - std::max(bbox_.lx, rect.lx);
const float y_overlap = std::min(bbox_.uy, rect.uy) - std::max(bbox_.ly, rect.ly);
if (x_overlap <= 0.0 || y_overlap <= 0.0)
return;
else if (flag == true)
overlap_area_ += x_overlap * y_overlap;
else if (flag == false)
overlap_area_ -= x_overlap * y_overlap;
}
// Calculate density
inline float GetDensity() const {
return overlap_area_ / grid_area_;
}
// Check congestion
// Update congestion: true for add and false for reduce
inline void UpdateCongestionH(float congestion, bool flag = false) {
if (flag == true)
hor_congestion_ += congestion;
else
hor_congestion_ -= congestion;
}
inline void UpdateCongestionV(float congestion, bool flag = false) {
if (flag == true)
ver_congestion_ += congestion;
else
ver_congestion_ -= congestion;
}
inline void UpdateMacroCongestionH(float congestion, bool flag = false) {
if (flag == true)
macro_hor_congestion_ += congestion;
else
macro_hor_congestion_ -= congestion;
}
inline void UpdateMacroCongestionV(float congestion, bool flag = false) {
if (flag == true)
macro_ver_congestion_ += congestion;
else
macro_ver_congestion_ -= congestion;
}
private:
int x_id_ = 0;
int y_id_ = 0;
int id_ = 0.0; // all grids are arranged in a row-based manner
float grid_area_ = 0.0;
float overlap_area_ = 0.0; // the area of macros overlapped with the current grid
float hor_congestion_ = 0.0;
float ver_congestion_ = 0.0;
float macro_hor_congestion_ = 0.0;
float macro_ver_congestion_ = 0.0;
float smooth_hor_congestion_ = 0.0;
float smooth_ver_congestion_ = 0.0;
Rect bbox_; // bounding box of current grid
float x_ = 0.0; // center location
float y_ = 0.0; // center location
bool available_ = true; // if the grid has been occupied by a hard macro
friend class PBFNetlist;
};
#include <iostream>
#include <chrono>
#include <sstream>
#include "plc_netlist.h"
int main(int argc, char* argv[]) {
std::string netlist_file = argv[1];
std::string plc_file = argv[2];
std::vector<float> action_probs;
unsigned int num_actions = std::stoul(std::string(argv[3]));
float max_temperature = std::stof(std::string(argv[4]));
unsigned int num_iters = std::stoul(std::string(argv[5]));
int seed = std::stoi(std::string(argv[6]));
bool spiral_flag;
std::istringstream(argv[7]) >> spiral_flag;
for (int i = 0; i < 5; i++) {
action_probs.push_back(std::stof(std::string(argv[i+8])));
}
std::string design_name = std::string(argv[13]);
std::string run_dir = std::string(argv[14]);
std::string summary_file = run_dir + "/" + design_name + ".summary";
std::cout << std::string(80, '*') << std::endl;
std::cout << "The Parameters for Simulated Annealing" << std::endl;
std::cout << "[PARAMS] num_actions = " << num_actions << std::endl;
std::cout << "[PARAMS] max_temperature = " << max_temperature << std::endl;
std::cout << "[PARAMS] num_iters = " << num_iters << std::endl;
std::cout << "[PARAMS] seed = " << seed << std::endl;
std::cout << "[PARAMS] spiral_flag = " << spiral_flag << std::endl;
std::cout << "[PARAMS] action_probs = { ";
for (auto& value : action_probs)
std::cout << value << " ";
std::cout << " } " << std::endl;
PBFNetlist design(netlist_file);
std::string new_netlist_file = run_dir + "/" + design_name + ".pb.txt.final";
std::string new_plc_file = run_dir + "/" + design_name + ".plc.final";
design.RestorePlacement(plc_file);
auto start_timestamp_global = std::chrono::high_resolution_clock::now();
design.SimulatedAnnealing(action_probs, num_actions, max_temperature,
num_iters, seed, spiral_flag, summary_file);
//design.CalcCost();
auto end_timestamp_global = std::chrono::high_resolution_clock::now();
double total_global_time
= std::chrono::duration_cast<std::chrono::nanoseconds>(
end_timestamp_global - start_timestamp_global)
.count();
total_global_time *= 1e-9;
std::cout << "Runtime of simulated annealing : " << total_global_time << std::endl;
design.WritePlcFile(new_plc_file);
design.WriteNetlist(new_netlist_file);
return 0;
}
#pragma once
#include <vector>
#include <map>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <memory>
#include <random>
#include <set>
#include <cassert>
#include "basic_object.h"
#include "plc_object.h"
class PBFNetlist;
// Net Class
class Net {
public:
// constructor
Net(std::vector<std::shared_ptr<PlcObject> > pins, float weight) {
pins_ = pins; // the first pin is always the source pin
weight_ = weight;
}
inline void Reset() {
HPWL_ = 0.0;
}
inline void UpdateHPWL() {
float x_min = std::numeric_limits<float>::max();
float y_min = std::numeric_limits<float>::max();
float x_max = -1.0 * x_min;
float y_max = -1.0 * y_min;
for (auto& pin : pins_) {
x_min = std::min(x_min, pin->x_);
x_max = std::max(x_max, pin->x_);
y_min = std::min(y_min, pin->y_);
y_max = std::max(y_max, pin->y_);
}
HPWL_ = weight_ * (y_max - y_min + x_max - x_min);
}
private:
std::vector<std::shared_ptr<PlcObject> > pins_;
float weight_ = 1.0;
float HPWL_ = 0.0; // wirelength
friend class PBFNetlist;
};
#pragma once
#include <vector>
#include <map>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <memory>
#include <random>
#include <set>
#include <cassert>
#include "basic_object.h"
#include "net.h"
#include "grid.h"
#include "plc_object.h"
class PlcObject;
class Net;
class Grid;
// Class PBFNetlist representing the netlist
class PBFNetlist {
public:
PBFNetlist(std::string netlist_pbf_file) {
ParseNetlistFile(netlist_pbf_file);
}
void RestorePlacement(std::string file_name);
void FDPlacer(float io_factor, std::vector<int> num_steps,
std::vector<float> move_distance_factor,
std::vector<float> attract_factor,
std::vector<float> repel_factor,
bool use_current_loc,
bool debug_mode = true);
// wrapper function for FDPlacer
void CallFDPlacer() {
const float io_factor = 1.0;
const std::vector<int> num_steps { 100, 100, 100 };
const std::vector<float> attract_factor { 100.0, 1.0e-3, 1.0e-5 };
const std::vector<float> repel_factor { 0.0, 1.0e6, 1.0e7 };
const std::vector<float> move_distance_factor { 1.0, 1.0, 1.0 };
const bool use_current_loc = false;
const bool debug_mode = false;
FDPlacer(io_factor, num_steps, move_distance_factor,
attract_factor, repel_factor,
use_current_loc, debug_mode);
};
// Simulated Annealing
void SimulatedAnnealing(std::vector<float> action_probs,
unsigned int num_actions,
float max_temperature,
unsigned int num_iters,
int seed,
bool spiral_flag,
std::string summary_file);
// calculate the cost
float CalcCost(bool debug_mode = false);
// Write the netilist and plc file
void WriteNetlist(std::string file_name);
void WritePlcFile(std::string file_name);
private:
// information from protocol buffer netlist
std::string pb_netlist_header_ = "";
std::vector<std::shared_ptr<PlcObject> > objects_;
std::vector<std::shared_ptr<PlcObject> > pre_objects_;
std::vector<size_t> macros_;
std::vector<size_t> stdcell_clusters_;
std::vector<size_t> macro_clusters_;
std::vector<size_t> ports_;
std::vector<size_t> placed_macros_;
std::vector<std::shared_ptr<Net> > nets_;
std::vector<std::shared_ptr<Grid> > grids_;
// information from plc file
std::string plc_header_ = "";
int n_cols_ = -1;
int n_rows_ = -1;
float canvas_width_ = 0.0;
float canvas_height_ = 0.0;
float grid_width_ = 0.0;
float grid_height_ = 0.0;
// routing information
int smooth_factor_ = 2;
float overlap_threshold_ = 0.0;
float vrouting_alloc_ = 0.0;
float hrouting_alloc_ = 0.0;
float hroute_per_micro_ = 0.0;
float vroute_per_micro_ = 0.0;
const float min_dist_ = 1e-4; // defined by circuit training
// random seed setting
int seed_ = 0;
std::mt19937 generator_;
std::uniform_real_distribution<float> distribution_;
// cost function
float HPWL_ = 0.0;
float norm_HPWL_ = 1.0;
int top_k_ = 1;
float density_ = 0.0;
int top_k_congestion_ = 1;
float congestion_ = 0.0;
// probabilities
std::vector<float> action_probs_ { 0.0, 0.0, 0.0, 0.0, 0.0 };
std::map<size_t, std::pair<float, float> > pre_locs_;
std::map<size_t, ORIENTATION> pre_orients_;
// utility functions
void ParseNetlistFile(std::string netlist_pbf_file);
// Force-directed placement
void InitSoftMacros(); // put all the soft macros to the center of canvas
void CalcAttractiveForce(float attractive_factor, float io_factor, float max_displacement);
void CalcRepulsiveForce(float repulsive_factor, float max_displacement);
void MoveSoftMacros(float attractive_factor, float repulsive_factor,
float io_factor, float max_displacement);
void MoveNode(size_t node_id, float x_dist, float y_dist);
// Cost Related information
// Routing congestion
void TwoPinNetRouting(LOC src_loc, LOC sink_loc, float weight, bool flag);
void UpdateRouting(std::shared_ptr<Net> net, bool flag = true);
void UpdateMacroCongestion(std::shared_ptr<PlcObject> plc_object, bool flag);
// Simulated Annealing related macros
void InitMacroPlacement(bool spiral_flag = true);
bool Action();
void Restore();
bool CheckOverlap(size_t macro);
bool IsFeasible(size_t macro);
// actions
bool Shuffle(); // permute 4 macros at a time
bool Shift(); // randomly pick a macro, then shuffle all the neighbor locations
bool Swap(); // Randomly pick two macros and swap them
bool Move(); // Move to a ramdom position
bool Flip();
float CalcCostIncr(bool inverse_flag = false);
};
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <numeric>
#include "plc_netlist.h"
// ***************************************************************************
// Force-directed placement
// **************************************************************************
void PBFNetlist::InitSoftMacros() {
const float x = canvas_width_ / 2.0;
const float y = canvas_height_ / 2.0;
for (auto& node_id : stdcell_clusters_) {
objects_[node_id]->SetPos(x, y, grid_width_, grid_height_, n_cols_, n_rows_);
}
}
void PBFNetlist::CalcAttractiveForce(float attractive_factor,
float io_factor,
float max_displacement) {
// traverse the nets to calculate the attractive force
for (auto& net : nets_) {
std::shared_ptr<PlcObject>& src_pin = net->pins_[0];
// convert multi-pin nets to two-pin nets using star model
auto iter = net->pins_.begin();
iter++;
while (iter != net->pins_.end()) {
std::shared_ptr<PlcObject>& target_pin = *iter;
// check the distance between src_pin and target_pin
std::pair<float, float> src_pos = src_pin->GetPos();
std::pair<float, float> target_pos = target_pin->GetPos();
const float x_dist = -1.0 * (src_pos.first - target_pos.first);
const float y_dist = -1.0 * (src_pos.second - target_pos.second);
if (x_dist != 0.0 || y_dist != 0.0) {
float k = net->weight_; // spring constant
if (src_pin->IsPort() == true || target_pin->IsPort() == true)
k = k * io_factor * attractive_factor;
else
k = k * attractive_factor;
const float f_x = k * x_dist;
const float f_y = k * y_dist;
if (src_pin->IsSoftMacroPin() == true)
src_pin->macro_ptr_->AddForce(f_x, f_y);
if (target_pin->IsSoftMacroPin() == true)
target_pin->macro_ptr_->AddForce(-1.0 * f_x, -1.0 * f_y);
}
iter++;
} // finish current net
} // finish traversing nets
}
void PBFNetlist::CalcRepulsiveForce(float repulsive_factor,
float max_displacement) {
std::sort(macro_clusters_.begin(), macro_clusters_.end(),
[&](size_t src, size_t target) {
return objects_[src]->bbox_.lx < objects_[target]->bbox_.lx;
});
// traverse the soft macros and hard macros to check possible overlap
auto iter = macro_clusters_.begin();
while (iter != macro_clusters_.end()) {
size_t init_src_macro = *iter;
for (auto iter_loop = ++iter;
iter_loop != macro_clusters_.end(); iter_loop++) {
size_t target_macro = *iter_loop;
size_t src_macro = init_src_macro;
if (src_macro > target_macro) {
std::swap(src_macro, target_macro);
}
const Rect src_bbox = objects_[src_macro]->GetBBox();
const Rect target_bbox = objects_[target_macro]->GetBBox();
if (src_bbox.ux <= target_bbox.lx)
break;
if (src_bbox.ly >= target_bbox.uy || src_bbox.uy <= target_bbox.ly)
continue;
// check the overlap
float x_dir = 0.0; // overlap on x direction
float y_dir = 0.0; // overlap on y direction
const float src_width = src_bbox.ux - src_bbox.lx;
const float src_height = src_bbox.uy - src_bbox.ly;
const float target_width = target_bbox.ux - target_bbox.lx;
const float target_height = target_bbox.uy - target_bbox.ly;
const float src_cx = (src_bbox.lx + src_bbox.ux) / 2.0;
const float src_cy = (src_bbox.ly + src_bbox.uy) / 2.0;
const float target_cx = (target_bbox.lx + target_bbox.ux) / 2.0;
const float target_cy = (target_bbox.ly + target_bbox.uy) / 2.0;
const float x_min_dist = (src_width + target_width) / 2.0 - min_dist_;
const float y_min_dist = (src_height + target_height) / 2.0 - min_dist_;
// if there is no overlap
if (std::abs(target_cx - src_cx) > x_min_dist)
continue;
if (std::abs(target_cy - src_cy) > y_min_dist)
continue;
// fully overlap
if (src_cx == target_cx && src_cy == target_cy) {
x_dir = -1;
y_dir = -1;
} else {
x_dir = src_cx - target_cx;
y_dir = src_cy - target_cy;
const float dist = std::sqrt(x_dir * x_dir + y_dir * y_dir);
x_dir = x_dir / dist;
y_dir = y_dir / dist;
}
// calculate the force
const float f_x = repulsive_factor * max_displacement * x_dir;
const float f_y = repulsive_factor * max_displacement * y_dir;
objects_[src_macro]->AddForce(f_x, f_y);
objects_[target_macro]->AddForce(-1.0 * f_x, -1.0 * f_y);
}
} // finish traverse all the macros and stdcell_clusters
}
void PBFNetlist::MoveNode(size_t node_id, float x_dist, float y_dist) {
float x = objects_[node_id]->x_ + x_dist;
float y = objects_[node_id]->y_ + y_dist;
float width = objects_[node_id]->width_ / 2.0;
float height = objects_[node_id]->height_ / 2.0;
if (x - width >= 0.0 && y - height >= 0.0 && x + width <= canvas_width_ && y + height <= canvas_height_)
objects_[node_id]->SetPos(x, y, grid_width_, grid_height_, n_cols_, n_rows_);
}
void PBFNetlist::MoveSoftMacros(float attractive_factor, float repulsive_factor,
float io_factor, float max_displacement) {
// move soft macros based on forces
// reset forces
for (auto& cluster_id : stdcell_clusters_)
objects_[cluster_id]->ResetForce();
// calculate forces
if (repulsive_factor == 0 || (attractive_factor / repulsive_factor > 1e-4))
CalcAttractiveForce(attractive_factor, io_factor, max_displacement);
if (attractive_factor == 0.0 || (repulsive_factor / attractive_factor > 1e-4))
CalcRepulsiveForce(repulsive_factor, max_displacement);
// normalization
float max_f_x = 0.0;
float max_f_y = 0.0;
for (auto& cluster_id : stdcell_clusters_) {
const std::pair<float, float> forces = objects_[cluster_id]->GetForce();
max_f_x = std::max(max_f_x, std::abs(forces.first));
max_f_y = std::max(max_f_y, std::abs(forces.second));
}
for (auto& cluster_id : stdcell_clusters_) {
objects_[cluster_id]->NormalForce(max_f_x, max_f_y);
const std::pair<float, float> forces = objects_[cluster_id]->GetForce();
MoveNode(cluster_id, forces.first * max_displacement, forces.second * max_displacement);
}
}
void PBFNetlist::FDPlacer(float io_factor, std::vector<int> num_steps,
std::vector<float> move_distance_factor,
std::vector<float> attract_factor,
std::vector<float> repel_factor,
bool use_current_loc,
bool debug_mode)
{
// io_factor is a scalar
// num_steps, max_move_distance, attract_factor, repel_factor are vectors of
// the same size
if (debug_mode == true) {
std::cout << std::string(80, '*') << std::endl;
std::cout << "Start Force-directed Placement" << std::endl;
}
std::vector<float> max_move_distance;
const float max_size = std::max(canvas_width_, canvas_height_);
auto step_iter = num_steps.begin();
auto move_iter = move_distance_factor.begin();
while (step_iter != num_steps.end()) {
max_move_distance.push_back((*move_iter) * max_size / (*step_iter));
step_iter++;
move_iter++;
}
// initialize the location
if (use_current_loc == false)
InitSoftMacros();
for (size_t i = 0; i < num_steps.size(); i++) {
const float attractive_factor = attract_factor[i];
const float repulsive_factor = repel_factor[i];
if (attractive_factor == 0.0 && repulsive_factor == 0.0)
continue;
int num_step = num_steps[i];
float max_displacement = max_move_distance[i];
if (debug_mode == true) {
std::cout << std::string(80, '-') << std::endl;
std::cout << "Iteration " << i << std::endl;
std::cout << "attractive_factor = " << attractive_factor << std::endl;
std::cout << "repulsive_factor = " << repulsive_factor << std::endl;
std::cout << "max_displacement = " << max_displacement << std::endl;
std::cout << "num_step = " << num_step << std::endl;
std::cout << "io_factor = " << io_factor << std::endl;
}
for (auto j = 0; j < num_step; j++)
MoveSoftMacros(attractive_factor, repulsive_factor, io_factor, max_displacement);
}
}
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <numeric>
#include "plc_object.h"
// Main member functions for PlcObject
void PlcObject::SetPos(float x, float y, float grid_width, float grid_height, int num_cols, int num_rows) {
// We cannot the location for MACROPIN and GRPPIN
if (pb_type_ == MACROPIN || pb_type_ == GRPPIN)
return;
// update the location
x_ = x;
y_ = y;
UpdateBBox(grid_width, grid_height, num_cols, num_rows);
if (pb_type_ == GRP || pb_type_ == MACRO) { // update corresponding bbox
for (auto& pin : macro_pins_) {
pin->x_ = x + x_offset_;
pin->y_ = y + y_offset_;
pin->UpdateBBox(grid_width, grid_height, num_cols, num_rows);
}
}
}
void PlcObject::UpdateBBox(float grid_width, float grid_height, int num_cols, int num_rows) {
// update bounding box
bbox_.lx = x_ - width_ / 2.0;
bbox_.ly = y_ - height_ / 2.0;
bbox_.ux = x_ + width_ / 2.0;
bbox_.uy = y_ + height_ / 2.0;
// If the canvas is not specified
if (grid_width <= 0.0 || grid_height <= 0.0) {
return;
}
grid_bbox_.lx_id = static_cast<int>(std::floor(bbox_.lx / grid_width));
grid_bbox_.ly_id = static_cast<int>(std::floor(bbox_.ly / grid_height));
grid_bbox_.ux_id = static_cast<int>(std::floor(bbox_.ux / grid_width));
grid_bbox_.uy_id = static_cast<int>(std::floor(bbox_.uy / grid_height));
}
// IO related functions for PlcObject
std::string PlcObject::SimpleStr() const {
std::string str = "";
str += std::to_string(node_id_) + " ";
std::stringstream stream;
stream << std::fixed << std::setprecision(3) << x_ << " ";
stream << std::fixed << std::setprecision(3) << y_;
str += stream.str() + " ";
if (IsPort() == true) {
str += "- ";
} else {
str += GetString(orient_) + " ";
}
str += "0\n";
return str;
}
std::string PrintPlaceholder(std::string key, std::string value) {
std::string line = "";
line = " attr {\n";
line += " key: \"" + key + "\"\n";
line += " value {\n";
line += " placeholder: \"" + value + "\"\n";
line += " }\n";
line += " }\n";
return line;
}
std::string PrintPlaceholder(std::string key, float value) {
std::string line = "";
line = " attr {\n";
line += " key: \"" + key + "\"\n";
line += " value {\n";
std::stringstream stream;
stream << std::fixed << std::setprecision(6) << value;
line += " f: " + stream.str() + "\n";
line += " }\n";
line += " }\n";
return line;
}
// for protocol buffer netlist
std::ostream& operator <<(std::ostream &out, PlcObject& object) {
if (object.IsPort() == true) {
out << "node {\n";
out << " name: \"" << object.name_ << "\"\n";
for (auto& sink : object.inputs_) {
out << " input: \"" << sink << "\"\n";
}
out << PrintPlaceholder(std::string("side"), object.side_);
out << PrintPlaceholder(std::string("type"), GetString(object.pb_type_));
out << PrintPlaceholder(std::string("x"), object.x_);
out << PrintPlaceholder(std::string("y"), object.y_);
out << "}\n";
} else if (object.IsSoftMacroPin() == true || object.IsHardMacroPin() == true) {
out << "node {\n";
out << " name: \"" << object.name_ << "\"\n";
for (auto& sink : object.inputs_) {
out << " input: \"" << sink << "\"\n";
}
out << PrintPlaceholder(std::string("macro_name"), object.macro_ptr_->name_);
out << PrintPlaceholder(std::string("type"), GetString(object.pb_type_));
if (object.weight_ > 1) {
out << PrintPlaceholder(std::string("weight"), static_cast<int>(object.weight_));
}
out << PrintPlaceholder(std::string("x"), object.x_);
out << PrintPlaceholder(std::string("x_offset"), object.x_offset_N_);
out << PrintPlaceholder(std::string("y"), object.y_);
out << PrintPlaceholder(std::string("y_offset"), object.y_offset_N_);
out << "}\n";
} else if (object.IsHardMacro() == true) {
out << "node {\n";
out << " name: \"" << object.name_ << "\"\n";
out << PrintPlaceholder(std::string("height"), object.height_N_);
out << PrintPlaceholder(std::string("orientation"), GetString(object.orient_));
out << PrintPlaceholder(std::string("type"), GetString(object.pb_type_));
out << PrintPlaceholder(std::string("width"), object.width_N_);
out << PrintPlaceholder(std::string("x"), object.x_);
out << PrintPlaceholder(std::string("y"), object.y_);
out << "}\n";
} else {
out << "node {\n";
out << " name: \"" << object.name_ << "\"\n";
out << PrintPlaceholder(std::string("height"), object.height_N_);
out << PrintPlaceholder(std::string("type"), GetString(object.pb_type_));
out << PrintPlaceholder(std::string("width"), object.width_N_);
out << PrintPlaceholder(std::string("x"), object.x_);
out << PrintPlaceholder(std::string("y"), object.y_);
out << "}\n";
}
return out;
}
#pragma once
#include <vector>
#include <map>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <memory>
#include <random>
#include <set>
#include <cassert>
#include "basic_object.h"
class PBFNetlist;
// Define the class to handle netlist in Protocol Buffer Format
// Basic data structure
// PlcObject : a superset of attributes for different types of plc objects
// 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 {
public:
// constructor
PlcObject(size_t node_id) : node_id_(node_id) { }
// functions
// GetType Information
inline bool IsHardMacro() const {
return pb_type_ == MACRO;
}
inline bool IsSoftMacro() const {
return pb_type_ == GRP;
}
inline bool IsSoftMacroPin() const {
return pb_type_ == GRPPIN;
}
inline bool IsHardMacroPin() const {
return pb_type_ == MACROPIN;
}
inline bool IsPort() const {
return pb_type_ == PORT;
}
// Make Square
inline void MakeSquare() {
const float area = width_N_ * height_N_;
width_N_ = std::sqrt(area);
height_N_ = width_N_;
width_ = width_N_;
height_ = height_N_;
}
// Get real location instead of relative position
inline const std::pair<float, float> GetPos() const {
return std::pair<float, float>(x_, y_);
}
inline float GetX() const {
return x_;
}
inline float GetY() const {
return y_;
}
inline const Rect GetBBox() const {
return bbox_;
}
inline const GridRect GetGridBBox() const {
return grid_bbox_;
}
// we need grid_width and grid_height to calculate the grid_bbox
// We cannot set the location of MACROPIN and GRPPIN.
// Because the locations of MACROPIN and GRPPIN are determined by MACRO and GRP.
// When you specify the location of MACRO and GRP, the locations of coorepsonding MACROPINs and
// GRPPINs will also be updated.
// We only call this function after reading plc file
void SetPos(float x, float y, float grid_width, float grid_height, int num_cols, int num_rows);
// Force related functions
inline void ResetForce() {
f_x_ = 0.0;
f_y_ = 0.0;
}
inline void AddForce(float f_x, float f_y) {
f_x_ += f_x;
f_y_ += f_y;
}
inline void NormalForce(float max_f_x, float max_f_y) {
if (max_f_x > 0.0)
f_x_ = f_x_ / max_f_x;
if (max_f_y > 0.0)
f_y_ = f_y_ / max_f_y;
}
inline std::pair<float, float> GetForce() const {
return std::pair<float, float>(f_x_, f_y_);
}
// Flip operation
// Flop operation can only be applied to hard macro
// if x_flag == true, flip across x axis
// else, flip across y axis
void Flip(bool x_flag, float grid_width, float grid_height, int num_cols, int num_rows) {
if (pb_type_ != MACRO)
return;
if (x_flag == true) { // flip around x axis
switch (orient_)
{
case N:
orient_ = FS;
break;
case FN:
orient_ = S;
break;
case S:
orient_ = FN;
break;
case FS:
orient_ = N;
break;
case E:
orient_ = FW;
break;
case FE:
orient_ = W;
break;
case FW:
orient_ = E;
break;
case W:
orient_ = FE;
break;
default:
orient_ = N;
break;
}
} else { // flip around y axis
switch (orient_)
{
case N:
orient_ = FN;
break;
case FN:
orient_ = N;
break;
case S:
orient_ = FS;
break;
case FS:
orient_ = S;
break;
case E:
orient_ = FE;
break;
case FE:
orient_ = E;
break;
case FW:
orient_ = W;
break;
case W:
orient_ = FW;
break;
default:
orient_ = N;
break;
}
}
// we need update cooresponding items after orientation change
UpdateOrientation(grid_width, grid_height, num_cols, num_rows);
}
// string representation
friend std::ostream& operator <<(std::ostream &out, PlcObject& object); // for protocol buffer netlist
std::string SimpleStr() const; // for plc file
private:
// inherient attributes
std::string name_ = "";
size_t node_id_ = 0;
float weight_ = 1.0;
PBTYPE pb_type_ = MACRO; // hard macro
// we define xx_N_ becuase the Portocol buffer netlist required these values to generate the netlist file
float width_N_ = 0.0;
float height_N_ = 0.0;
float x_offset_N_ = 0.0;
float y_offset_N_ = 0.0;
std::string macro_name_ = ""; // the correponding macro names
std::shared_ptr<PlcObject> macro_ptr_ = nullptr;
std::vector<std::shared_ptr<PlcObject> > macro_pins_; // pins connnected to this macro
std::vector<size_t> nets_; // nets (id) connecte to this node (src and sink)
std::vector<std::string> inputs_; // sinks driven by this node
std::string side_ = "LEFT"; // attribute for IOPORT (LEFT, RIGHT, TOP, BOTTOM)
// real information (can be updated)
float x_ = 0.0; // center of the object
float y_ = 0.0; // center of the object
float height_ = 0.0;
float width_ = 0.0;
float x_offset_ = 0.0;
float y_offset_ = 0.0;
ORIENTATION orient_ = N; // orientation
Rect bbox_;
GridRect grid_bbox_; // gridding box
// the forces applied to this node
float f_x_ = 0.0;
float f_y_ = 0.0;
// utility function
void UpdateBBox(float grid_width, float grid_height, int num_cols, int num_rows);
// update the orientation
void UpdateOrientation(float grid_width, float grid_height, int num_cols, int num_rows) {
// update the hard macro information first
if (orient_ == E || orient_ == FE || orient_ == W || orient_ == FW) {
width_ = height_N_;
height_ = width_N_;
} else {
width_ = width_N_;
height_ = height_N_;
}
UpdateBBox(grid_width, grid_height, num_cols, num_rows);
// update the cooresponding bboxes of pins
for (auto& pin : macro_pins_) {
switch (orient_)
{
case N:
pin->x_offset_ = pin->x_offset_N_;
pin->y_offset_ = pin->y_offset_N_;
break;
case FN:
pin->x_offset_ = -1 * pin->x_offset_N_;
pin->y_offset_ = pin->y_offset_N_;
break;
case S:
pin->x_offset_ = -1 * pin->x_offset_N_;
pin->y_offset_ = -1 * pin->y_offset_N_;
break;
case FS:
pin->x_offset_ = pin->x_offset_N_;
pin->y_offset_ = -1 * pin->y_offset_N_;
break;
case E:
pin->x_offset_ = pin->y_offset_N_;
pin->y_offset_ = -1 * pin->x_offset_N_;
break;
case FE:
pin->x_offset_ = -1 * pin->y_offset_N_;
pin->y_offset_ = -1 * pin->x_offset_N_;
break;
case FW:
pin->x_offset_ = -1 * pin->y_offset_N_;
pin->y_offset_ = pin->x_offset_N_;
break;
case W:
pin->x_offset_ = pin->y_offset_N_;
pin->y_offset_ = pin->x_offset_N_;
break;
default:
break;
}
pin->UpdateBBox(grid_width, grid_height, num_cols, num_rows);
}
}
// utility function
friend class PBFNetlist;
friend class Net;
};
// Utility functions
std::string PrintPlaceholder(std::string key, std::string value);
std::string PrintPlaceholder(std::string key, float value);
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