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"
// ***************************************************************************************
// Calculate Cost
// ***************************************************************************************
// Calculate the cost from scratch
float PBFNetlist::CalcCost(bool debug_mode) {
// reset
for (auto& grid : grids_)
grid->Reset();
// calculate wirelength cost
HPWL_ = 0.0;
for (auto& net : nets_) {
net->UpdateHPWL();
HPWL_ += net->HPWL_;
}
// calculate the density
for (auto& node_id : macro_clusters_) {
const Rect rect = objects_[node_id]->GetBBox();
const GridRect grid_rect = objects_[node_id]->GetGridBBox();
for (int y_id = grid_rect.ly_id; y_id <= grid_rect.uy_id; y_id++) {
for (int x_id = grid_rect.lx_id; x_id <= grid_rect.ux_id; x_id++) {
grids_[y_id * n_cols_ + x_id]->UpdateOverlap(rect, true);
}
}
}
// get the top 10 % density
std::vector<float> density_list;
for (auto& grid : grids_)
density_list.push_back(grid->GetDensity());
std::sort(density_list.begin(), density_list.end(), std::greater<float>());
density_ = 0.0;
for (int i = 0; i < top_k_; i++)
density_ += density_list[i];
density_ = density_ / top_k_ * 0.5;
// calculate the congestion cost
// update the congestion caused by net
for (auto& net : nets_) {
UpdateRouting(net, true);
}
// update the congestion caused by macro
for (auto& macro : macros_)
UpdateMacroCongestion(objects_[macro], true);
// smooth the congestion by net
for (auto& grid : grids_) {
// smooth vertical congestion
const int col_id = grid->x_id_;
const int row_id = grid->y_id_;
const int v_start = std::max(0, grid->x_id_ - smooth_factor_);
const int v_end = std::min(n_cols_ - 1, grid->x_id_ + smooth_factor_);
const float ver_congestion = grid->ver_congestion_ / (v_end - v_start + 1);
for (int i = v_start; i <= v_end; i++)
grids_[row_id * n_cols_ + i]->smooth_ver_congestion_ += ver_congestion;
const int h_start = std::max(0, grid->y_id_ - smooth_factor_);
const int h_end = std::min(n_rows_ - 1, grid->y_id_ + smooth_factor_);
const float hor_congestion = grid->hor_congestion_ / (h_end - h_start + 1);
for (int j = h_start; j <= h_end; j++)
grids_[j * n_cols_ + col_id]->smooth_hor_congestion_ += hor_congestion;
}
// get the top 0.1 congestion
std::vector<float> congestion_list;
for (auto& grid : grids_) {
congestion_list.push_back(
(grid->smooth_ver_congestion_ + grid->macro_ver_congestion_) / (grid_width_ * vroute_per_micro_)
);
congestion_list.push_back(
(grid->smooth_hor_congestion_ + grid->macro_hor_congestion_) / (grid_height_ * hroute_per_micro_)
);
}
std::sort(congestion_list.begin(), congestion_list.end(), std::greater<float>());
congestion_ = 0.0;
for (int i = 0; i < top_k_congestion_; i++) {
congestion_ += congestion_list[i];
}
congestion_ = congestion_ / top_k_congestion_;
float cost = HPWL_ / norm_HPWL_ + density_ * 0.5 + congestion_ * 0.5;
if (debug_mode == true) {
std::cout << "Scratch Cost = " << cost << " "
<< "HPWL = " << HPWL_ / norm_HPWL_ << " "
<< "density = " << density_ << " "
<< "congestion = " << congestion_ << std::endl;
}
return cost;
}
// Get routing information (general case)
// multi-pin nets are decomposed into multiple two-pin nets
void PBFNetlist::UpdateRouting(std::shared_ptr<Net> net, bool flag) {
std::set<LOC> pin_set;
for (auto& pin : net->pins_) {
pin_set.insert(LOC(pin->grid_bbox_.lx_id, pin->grid_bbox_.ly_id));
}
// check the conditions using switch clauses
switch(pin_set.size()) {
case 0:
return; // ignore empty net
case 1:
return; // ignore all single-pin net
case 2: {
LOC src_loc = LOC(net->pins_[0]->grid_bbox_.lx_id, net->pins_[0]->grid_bbox_.ly_id);
for (auto& loc : pin_set) {
if (src_loc.IsEqual(loc) == false) {
TwoPinNetRouting(src_loc, loc, net->weight_, flag);
}
}
}
return;
case 3: {
// define alias for better understanding
std::vector<LOC> pin_locs(pin_set.size());
std::copy(pin_set.begin(), pin_set.end(), pin_locs.begin());
std::sort(pin_locs.begin(), pin_locs.end(),
[](const LOC& a, const LOC& b) {
return a.x_id < b.x_id;
});
int col_id_1 = pin_locs[0].x_id;
int col_id_2 = pin_locs[1].x_id;
int col_id_3 = pin_locs[2].x_id;
int row_id_1 = pin_locs[0].y_id;
int row_id_2 = pin_locs[1].y_id;
int row_id_3 = pin_locs[2].y_id;
// checking L Routing condition
if (col_id_1 < col_id_2 &&
col_id_2 < col_id_3 &&
std::min(row_id_1, row_id_3) < row_id_2 &&
std::max(row_id_1, row_id_3) > row_id_2) {
// L routing
for (auto col_id = col_id_1; col_id < col_id_2; col_id++)
grids_[row_id_1 * n_cols_ + col_id]->UpdateCongestionH(net->weight_, flag);
for (auto col_id = col_id_2; col_id < col_id_3; col_id++)
grids_[row_id_2 * n_cols_ + col_id]->UpdateCongestionH(net->weight_, flag);
for (auto row_id = std::min(row_id_1, row_id_2); row_id < std::max(row_id_1, row_id_2); row_id++)
grids_[row_id * n_cols_ + col_id_2]->UpdateCongestionV(net->weight_, flag);
for (auto row_id = std::min(row_id_2, row_id_3); row_id < std::max(row_id_2, row_id_3); row_id++)
grids_[row_id * n_cols_ + col_id_3]->UpdateCongestionV(net->weight_, flag);
return;
} else if (col_id_2 == col_id_3 && col_id_1 < col_id_2 && row_id_1 < std::min(row_id_2, row_id_3)) {
// check if condition 2
for (auto col_id = col_id_1; col_id < col_id_2; col_id++)
grids_[row_id_1 * n_cols_ + col_id]->UpdateCongestionH(net->weight_, flag);
for (auto row_id = row_id_1; row_id < std::max(row_id_2, row_id_3); row_id++)
grids_[row_id * n_cols_ + col_id_2]->UpdateCongestionV(net->weight_, flag);
return;
} else if (row_id_2 == row_id_3) {
// check if condition 3
for (auto col_id = col_id_1; col_id < col_id_2; col_id++)
grids_[row_id_1 * n_cols_ + col_id]->UpdateCongestionH(net->weight_, flag);
for (auto col_id = col_id_2; col_id < col_id_3; col_id++)
grids_[row_id_2 * n_cols_ + col_id]->UpdateCongestionH(net->weight_, flag);
for (auto row_id = std::min(row_id_1, row_id_2); row_id < std::max(row_id_1, row_id_2); row_id++)
grids_[row_id * n_cols_ + col_id_2]->UpdateCongestionV(net->weight_, flag);
return;
} else {
// sort pins based on row_id
std::sort(pin_locs.begin(), pin_locs.end(),
[](const LOC& a, const LOC& b) {
return a.y_id < b.y_id;
});
int col_id_1 = pin_locs[0].x_id;
int col_id_2 = pin_locs[1].x_id;
int col_id_3 = pin_locs[2].x_id;
int row_id_1 = pin_locs[0].y_id;
int row_id_2 = pin_locs[1].y_id;
int row_id_3 = pin_locs[2].y_id;
int min_col_id = std::numeric_limits<int>::max();
int max_col_id = -1 * std::numeric_limits<int>::max();
for (auto& pin : net->pins_) {
min_col_id = std::min(min_col_id, pin->grid_bbox_.lx_id);
max_col_id = std::max(max_col_id, pin->grid_bbox_.lx_id);
}
for (auto col_id = min_col_id; col_id < max_col_id; col_id++)
grids_[row_id_2 * n_cols_ + col_id]->UpdateCongestionH(net->weight_, flag);
for (auto row_id = std::min(row_id_1, row_id_2); row_id < std::max(row_id_1, row_id_2); row_id++)
grids_[row_id * n_cols_ + col_id_1]->UpdateCongestionV(net->weight_, flag);
for (auto row_id = std::min(row_id_2, row_id_3); row_id < std::max(row_id_2, row_id_3); row_id++)
grids_[row_id * n_cols_ + col_id_3]->UpdateCongestionV(net->weight_, flag);
return;
}
}
return;
default: {
// multi-pin nets are decomposed into multiple two-pin nets using star model
LOC src_loc = LOC(net->pins_[0]->grid_bbox_.lx_id, net->pins_[0]->grid_bbox_.ly_id);
for (auto& loc : pin_set) {
if (src_loc.IsEqual(loc) == false) {
TwoPinNetRouting(src_loc, loc, net->weight_, flag);
}
}
}
return;
}
}
void PBFNetlist::TwoPinNetRouting(LOC src_loc, LOC sink_loc, float weight, bool flag) {
const int min_col_id = std::min(src_loc.x_id, sink_loc.x_id);
const int max_col_id = std::max(src_loc.x_id, sink_loc.x_id);
const int min_row_id = std::min(src_loc.y_id, sink_loc.y_id);
const int max_row_id = std::max(src_loc.y_id, sink_loc.y_id);
// update horizontal congestion
for (int col_id = min_col_id; col_id < max_col_id; col_id++) {
grids_[src_loc.y_id * n_cols_ + col_id]->UpdateCongestionH(weight, flag);
}
// update vertical congestion
for (int row_id = min_row_id; row_id < max_row_id; row_id++) {
grids_[row_id * n_cols_ + sink_loc.x_id]->UpdateCongestionV(weight, flag);
}
}
// Update the congestion caused by macro
// true for add and false for reduce
void PBFNetlist::UpdateMacroCongestion(std::shared_ptr<PlcObject> plc_object, bool flag) {
// calculate the horizontal and vertical congestion independently
const GridRect& grid_bbox = plc_object->grid_bbox_;
for (int row_id = grid_bbox.ly_id; row_id < grid_bbox.uy_id; row_id++) {
float v_overlap = grid_height_;
if (row_id == grid_bbox.ly_id)
v_overlap = (row_id + 1) * grid_height_ - plc_object->bbox_.ly;
for (int col_id = grid_bbox.lx_id; col_id < grid_bbox.ux_id; col_id++) {
float h_overlap = grid_width_;
if (col_id == grid_bbox.lx_id)
h_overlap = (col_id + 1) * grid_width_ - plc_object->bbox_.lx;
auto& grid = grids_[row_id * n_cols_ + col_id];
grid->UpdateMacroCongestionH(v_overlap * hrouting_alloc_, flag);
grid->UpdateMacroCongestionV(h_overlap * vrouting_alloc_, flag);
}
}
}
#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_netlist.h"
// **************************************************************************
// IO for class PBFNetlist
// **************************************************************************
// read netlist file for all the plc objects
void PBFNetlist::ParseNetlistFile(std::string netlist_pbf_file) {
// we need this std::string related to map to map macro_pin to macro
std::map<std::string, size_t> plc_object_id_map = { }; // map name to node_id
// read protocol buffer netlist
// Note that the order is specified by the Circuit Training
// You cannot change the order
const std::vector<std::string> float_values {
std::string("height"), std::string("weight"), std::string("width"),
std::string("x"), std::string("x_offset"), std::string("y"), std::string("y_offset")
};
const std::vector<std::string> placeholders {
std::string("macro_name"), std::string("orientation"),
std::string("side"), std::string("type")
};
// read the files and parse the files
std::string cur_line;
std::vector<std::string> header; // trace the header file
std::ifstream file_input(netlist_pbf_file);
if (!file_input.is_open()) {
std::cout << "[ERROR] We cannot open netlist file " << netlist_pbf_file << std::endl;
}
// define lamda function to remove qoutes
auto RemoveQuotes = [&](std::string word) {
std::string new_word = "";
for (auto& ch : word)
if (ch != '\"')
new_word += std::string(1, ch);
return new_word;
};
// Read lines
size_t object_id = 0;
std::string key = "";
while (std::getline(file_input, cur_line)) {
header.push_back(cur_line);
// split the string based on space
std::string word = "";
std::vector<std::string> items;
std::istringstream ss(cur_line);
while (ss >> word)
items.push_back(RemoveQuotes(word));
// check the contents of items
if (items[0] == std::string("node")) {
if (objects_.empty() == false && (*objects_.rbegin())->name_ == std::string("__metadata__")) {
objects_.pop_back();
object_id--;
for (size_t i = 0; i < header.size() - 1; i++)
pb_netlist_header_ += header[i] + "\n";
}
objects_.push_back(std::make_shared<PlcObject>(object_id));
object_id++;
} else if (items[0] == std::string("name:")) {
(*objects_.rbegin())->name_ = items[1];
} else if (items[0] == std::string("input:")) {
(*objects_.rbegin())->inputs_.push_back(items[1]);
} else if (items[0] == std::string("key:")) {
key = items[1]; // the attribute name
} else if (items[0] == std::string("placeholder:")) {
if (key == placeholders[0]) {
(*objects_.rbegin())->macro_name_ = items[1];
} else if (key == placeholders[1]) {
(*objects_.rbegin())->orient_ = ORIENT_MAP.at(items[1]);
} else if (key == placeholders[2]) {
(*objects_.rbegin())->side_ = items[1];
} else if (key == placeholders[3]) {
(*objects_.rbegin())->pb_type_ = PBTYPE_MAP.at(items[1]);
}
} else if (items[0] == std::string("f:")) {
if (key == float_values[0]) {
(*objects_.rbegin())->height_N_ = std::stof(items[1]);
} else if (key == float_values[1]) {
(*objects_.rbegin())->weight_ = std::stof(items[1]);
} else if (key == float_values[2]) {
(*objects_.rbegin())->width_N_ = std::stof(items[1]);
} else if (key == float_values[3]) {
(*objects_.rbegin())->x_ = std::stof(items[1]);
} else if (key == float_values[4]) {
(*objects_.rbegin())->x_offset_N_ = std::stof(items[1]);
} else if (key == float_values[5]) {
(*objects_.rbegin())->y_ = std::stof(items[1]);
} else if (key == float_values[6]) {
(*objects_.rbegin())->y_offset_N_ =std::stof(items[1]);
}
}
}
// Get all the macros, standard-cell clusters and IO ports
for (auto& plc_object : objects_) {
plc_object_id_map[plc_object->name_] = plc_object->node_id_;
if (plc_object->IsHardMacro() == true) {
plc_object->UpdateOrientation(grid_width_, grid_height_, n_cols_, n_rows_);
macros_.push_back(plc_object->node_id_);
} else if (plc_object->IsSoftMacro() == true) {
plc_object->UpdateOrientation(grid_width_, grid_height_, n_cols_, n_rows_);
stdcell_clusters_.push_back(plc_object->node_id_);
plc_object->MakeSquare(); // convert standard-cell clusters to square shape
} else if (plc_object->IsPort() == true) {
ports_.push_back(plc_object->node_id_);
}
}
// map each plc object to its corresponding macro
for (auto& plc_object : objects_) {
if (plc_object->IsHardMacroPin() == true || plc_object->IsSoftMacroPin() == true) {
auto& macro_id = plc_object_id_map[plc_object->macro_name_];
plc_object->macro_ptr_ = objects_[macro_id];
plc_object->macro_ptr_->macro_pins_.push_back(plc_object);
}
}
// create nets
for (auto& plc_object : objects_) {
if (plc_object->IsHardMacroPin() == true ||
plc_object->IsSoftMacroPin() == true ||
plc_object->IsPort() == true) {
std::vector<std::shared_ptr<PlcObject> > pins { plc_object };
for (auto& input_pin : plc_object->inputs_) {
pins.push_back(objects_[plc_object_id_map[input_pin]]);
}
if (pins.size() > 1) {
for (auto& pin : pins) {
pin->nets_.push_back(nets_.size());
}
nets_.push_back(std::make_shared<Net>(pins, plc_object->weight_));
}
}
}
// merge macros and stdcell_clusters
macro_clusters_.insert(macro_clusters_.end(),
macros_.begin(),
macros_.end());
macro_clusters_.insert(macro_clusters_.end(),
stdcell_clusters_.begin(),
stdcell_clusters_.end());
// sort macro_clusters.
// We need this beacuse the FD placer calculates the repulsive force
// following this order
std::sort(macro_clusters_.begin(), macro_clusters_.end());
}
// Parse Plc File
void PBFNetlist::RestorePlacement(std::string file_name) {
// read the plc file and parse the file
std::string cur_line;
std::ifstream file_input(file_name);
if (!file_input.is_open()) {
std::cout << "[ERROR] We cannot open plc file " << file_name << std::endl;
}
// Read lines
while (std::getline(file_input, cur_line)) {
// split the string based on space
std::string word = "";
std::vector<std::string> items;
std::istringstream ss(cur_line);
while (ss >> word)
items.push_back(word);
// check if the current line is related to header
if (items.empty() == false && items[0] == std::string("#")) {
plc_header_ += cur_line + "\n";
if (items.size() > 2 && items[1] == std::string("Columns")) {
n_cols_ = std::stoi(items[3]);
n_rows_ = std::stoi(items[6]);
} else if (items.size() > 2 && items[1] == std::string("Width")) {
canvas_width_ = std::stof(items[3]);
canvas_height_ = std::stof(items[6]);
} else if (items.size() == 10 && items[1] == std::string("Routes")) {
hroute_per_micro_ = std::stof(items[6]);
vroute_per_micro_ = std::stof(items[9]);
} else if (items.size() == 11 && items[1] == std::string("Routes")) {
hrouting_alloc_ = std::stof(items[7]);
vrouting_alloc_ = std::stof(items[10]);
} else if (items.size() == 5 && items[1] == std::string("Smoothing")) {
smooth_factor_ = static_cast<int>(std::floor(std::stof(items[4])));
} else if (items.size() == 5 && items[1] == std::string("Overlap")) {
overlap_threshold_ = std::floor(std::stof(items[4]));
}
} else if (items.size() == 5) {
size_t node_id = static_cast<size_t>(std::stoi(items[0]));
objects_[node_id]->x_ = std::stof(items[1]);
objects_[node_id]->y_ = std::stof(items[2]);
objects_[node_id]->orient_ = ORIENT_MAP.at(items[3]);
}
}
// grid info and create grids
grid_width_ = canvas_width_ / n_cols_;
grid_height_ = canvas_height_ / n_rows_;
for (int y_id = 0; y_id < n_rows_; y_id++) {
for (int x_id = 0; x_id < n_cols_; x_id++) {
grids_.push_back(std::make_shared<Grid>(grid_width_,
grid_height_,
n_cols_,
n_rows_,
x_id, y_id));
}
}
std::cout << "In restore design : " << std::endl;
std::cout << "CANVAS_WIDTH = " << canvas_width_ << std::endl;
std::cout << "CANVAS_HEIGHT = " << canvas_height_ << std::endl;
std::cout << "GRID_WIDTH = " << grid_width_ << std::endl;
std::cout << "GRID_HEIGHT = " << grid_height_ << std::endl;
std::cout << "NUM_COLS = " << n_cols_ << std::endl;
std::cout << "NUM_ROWS = " << n_rows_ << std::endl;
std::cout << "Routes per micro, hor : " << hrouting_alloc_ << " "
<< " ver : " << vrouting_alloc_ << std::endl;
std::cout << "Routes used by macros, hor : " << hroute_per_micro_ << " "
<< " ver : " << vroute_per_micro_ << std::endl;
std::cout << "Smoothing_factor = " << smooth_factor_ << std::endl;
// update the information about plc object
for (auto& plc_object : objects_) {
if (plc_object->IsHardMacro() == true)
plc_object->UpdateOrientation(grid_width_, grid_height_, n_cols_, n_rows_);
plc_object->SetPos(plc_object->x_, plc_object->y_, grid_width_, grid_height_, n_cols_, n_rows_);
}
// calculate norm HPWL
norm_HPWL_ = 0.0;
for (auto& net : nets_)
norm_HPWL_ += net->weight_;
norm_HPWL_ *= (canvas_width_ + canvas_height_);
// top k grids
top_k_ = std::max(1, static_cast<int>(std::floor(n_cols_ * n_rows_ * 0.1)));
top_k_congestion_ = std::max(1, static_cast<int>(std::floor(n_cols_ * n_rows_ * 0.1)));
}
// Write netlist
void PBFNetlist::WriteNetlist(std::string file_name) {
std::ofstream f;
f.open(file_name);
f << pb_netlist_header_;
for (auto& plc_object : objects_)
f << *plc_object;
f.close();
}
void PBFNetlist::WritePlcFile(std::string file_name) {
std::ofstream f;
f.open(file_name);
f << plc_header_;
for (auto& plc_object : objects_)
if (plc_object->IsHardMacro() == true ||
plc_object->IsSoftMacro() == true ||
plc_object->IsPort() == true) {
f << plc_object->SimpleStr();
}
f.close();
}
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <numeric>
#include "plc_netlist.h"
// Class PBFNetlist
// ***************************************************************************
// Simulated Annealing
// ***************************************************************************
// Simulated Annealing related functions
void PBFNetlist::InitMacroPlacement(bool spiral_flag) {
// determine the order of grids
for (auto& grid : grids_)
grid->available_ = true;
placed_macros_.clear();
std::vector<int> grid_order(grids_.size());
if (spiral_flag == true) {
// arrange grid in a spiral manner
std::vector<bool> visited_flag(grids_.size(), false);
// direction in a counter clock-wise manner
std::vector<int> dir_row { 0, 1, 0, -1};
std::vector<int> dir_col { 1, 0, -1, 0};
int row_id = 0;
int col_id = 0;
int dir_id = 0;
for (int i = 0; i < n_cols_ * n_rows_; i++) {
const int grid_id = row_id * n_cols_ + col_id;
visited_flag[grid_id] = true;
grid_order[i] = grid_id;
const int next_row = row_id + dir_row[dir_id];
const int next_col = col_id + dir_col[dir_id];
const int next_grid_id = next_row * n_cols_ + next_col;
if (0 <= next_row && next_row < n_rows_ &&
0 <= next_col && next_col < n_cols_ &&
visited_flag[next_grid_id] == false) {
col_id = next_col;
row_id = next_row;
} else {
dir_id = (dir_id + 1) % 4;
row_id += dir_row[dir_id];
col_id += dir_col[dir_id];
}
}
} else {
std::iota(grid_order.begin(), grid_order.end(), 0);
}
std::vector<size_t> sorted_macros = macros_;
// sort macros based on area in an non-decreasing order
std::sort(sorted_macros.begin(), sorted_macros.end(),
[&](size_t a, int b) {
return objects_[a]->width_ * objects_[a]->height_ >
objects_[b]->width_ * objects_[b]->height_;
});
for (auto& macro : sorted_macros) {
for (auto& grid_id : grid_order) {
if (grids_[grid_id]->available_ == false)
continue; // this grid has been occupied by other macros
objects_[macro]->SetPos(grids_[grid_id]->x_, grids_[grid_id]->y_,
grid_width_, grid_height_, n_cols_, n_rows_);
auto& rect = objects_[macro]->bbox_;
if (rect.lx < 0.0 || rect.ly < 0.0 || rect.ux > canvas_width_ || rect.uy > canvas_height_)
continue;
// check if there is an overlap with other macros
if (CheckOverlap(macro) == true)
continue; // This is some overlap with other placed macros
// place macro on this grid
grids_[grid_id]->available_ = false;
placed_macros_.push_back(macro);
break;
}
} // placed all the macros
if (placed_macros_.size() != macros_.size()) {
std::cout << "[ERROR] There is no valid initial macro tiling" << std::endl;
}
}
bool PBFNetlist::CheckOverlap(size_t macro) {
if (placed_macros_.empty() == true)
return false;
for (auto& placed_macro : placed_macros_) {
if (macro == placed_macro)
continue;
auto& rect1 = objects_[placed_macro]->bbox_;
auto& rect2 = objects_[macro]->bbox_;
if (rect1.lx > rect2.ux || rect1.ly > rect2.uy ||
rect1.ux < rect2.lx || rect1.uy < rect2.ly)
continue; // This is no overlap
else
return true;
}
return false;
}
bool PBFNetlist::IsFeasible(size_t macro) {
if (objects_[macro]->bbox_.lx < 0.0 || objects_[macro]->bbox_.ly < 0.0 ||
objects_[macro]->bbox_.ux > canvas_width_ ||
objects_[macro]->bbox_.uy > canvas_height_)
return false;
if (CheckOverlap(macro) == true)
return false;
return true;
}
bool PBFNetlist::Shuffle() {
// find four macros randomly
std::vector<int> random_macros;
while (random_macros.size() < 4) {
const int macro_id = static_cast<int>(std::floor(distribution_(generator_) * macros_.size()));
if (std::find(random_macros.begin(), random_macros.end(), macro_id) == random_macros.end())
random_macros.push_back(macros_[macro_id]);
}
// get the positions of macros
std::vector<std::pair<float, float> > macro_pos;
for (auto& macro_id : random_macros) {
const std::pair<float, float> pos { objects_[macro_id]->x_,
objects_[macro_id]->y_ };
macro_pos.push_back(pos);
pre_locs_[macro_id] = pos;
}
// randomly shuffle the locations
std::shuffle(macro_pos.begin(), macro_pos.end(), generator_);
// update the locations of macros
for (size_t i = 0; i < random_macros.size(); i++) {
objects_[random_macros[i]]->SetPos(macro_pos[i].first, macro_pos[i].second,
grid_width_, grid_height_, n_cols_, n_rows_);
}
// check feasibility
for (auto& macro_id : random_macros) {
if (IsFeasible(macro_id) == false) {
for (auto& loc : pre_locs_) {
objects_[loc.first]->SetPos(loc.second.first, loc.second.second, grid_width_, grid_height_, n_cols_, n_rows_);
}
return false;
}
}
return true;
}
// Randomly pick a macro, then shuffle all the neighboring locations
// Try the location one by one. If fail, restore the status
bool PBFNetlist::Shift() {
// randomly pick a macro
const int macro_id = macros_[static_cast<int>(std::floor(distribution_(generator_) * macros_.size()))];
// calculate the neighboring locations and randomly shuffle
std::vector<std::pair<float, float> > neighbor_locs;
const std::pair<float, float> macro_loc { objects_[macro_id]->x_, objects_[macro_id]->y_ };
pre_locs_[macro_id] = macro_loc;
neighbor_locs.push_back(std::pair<float, float>(macro_loc.first - grid_width_, macro_loc.second));
neighbor_locs.push_back(std::pair<float, float>(macro_loc.first + grid_width_, macro_loc.second));
neighbor_locs.push_back(std::pair<float, float>(macro_loc.first, macro_loc.second - grid_height_));
neighbor_locs.push_back(std::pair<float, float>(macro_loc.first, macro_loc.second + grid_height_));
std::shuffle(neighbor_locs.begin(), neighbor_locs.end(), generator_);
for (auto& loc : neighbor_locs) {
objects_[macro_id]->SetPos(loc.first, loc.second, grid_width_, grid_height_, n_cols_, n_rows_);
if (IsFeasible(macro_id) == true)
return true;
}
// restore the pos
objects_[macro_id]->SetPos(macro_loc.first, macro_loc.second, grid_width_, grid_height_, n_cols_, n_rows_);
return false;
}
bool PBFNetlist::Swap() {
for (int i = 0; i < 5; i++) {
const int macro_a = macros_[static_cast<int>(std::floor(distribution_(generator_) * macros_.size()))];
const int macro_b = macros_[static_cast<int>(std::floor(distribution_(generator_) * macros_.size()))];
if (macro_a == macro_b)
continue;
// store the location
pre_locs_[macro_a] = std::pair<float, float>(objects_[macro_a]->x_,
objects_[macro_a]->y_);
pre_locs_[macro_b] = std::pair<float, float>(objects_[macro_b]->x_,
objects_[macro_b]->y_);
// swap the location of macro_a and macro_b
objects_[macro_a]->SetPos(pre_locs_[macro_b].first, pre_locs_[macro_b].second,
grid_width_, grid_height_, n_cols_, n_rows_);
objects_[macro_b]->SetPos(pre_locs_[macro_a].first, pre_locs_[macro_a].second,
grid_width_, grid_height_, n_cols_, n_rows_);
// check feasbility
if (IsFeasible(macro_a) == true && IsFeasible(macro_b) == true)
return true;
// restore the location
objects_[macro_a]->SetPos(pre_locs_[macro_a].first, pre_locs_[macro_a].second,
grid_width_, grid_height_, n_cols_, n_rows_);
objects_[macro_b]->SetPos(pre_locs_[macro_b].first, pre_locs_[macro_b].second,
grid_width_, grid_height_, n_cols_, n_rows_);
}
return false;
}
// Move a randomly selected macro to a new random location
bool PBFNetlist::Move() {
const int macro_a = macros_[static_cast<int>(std::floor(distribution_(generator_) * macros_.size()))];
// store the location
pre_locs_[macro_a] = std::pair<float, float>(objects_[macro_a]->x_,
objects_[macro_a]->y_);
// randomly pick a grid
const int grid_id = static_cast<int>(std::floor(distribution_(generator_) * grids_.size()));
objects_[macro_a]->SetPos(grids_[grid_id]->x_, grids_[grid_id]->y_, grid_width_, grid_height_, n_cols_, n_rows_);
if (IsFeasible(macro_a) == true)
return true;
// restore the loc
objects_[macro_a]->SetPos(pre_locs_[macro_a].first, pre_locs_[macro_a].second,
grid_width_, grid_height_, n_cols_, n_rows_);
return false;
}
bool PBFNetlist::Flip() {
const int macro_a = macros_[static_cast<int>(std::floor(distribution_(generator_) * macros_.size()))];
// store the orienetation
pre_orients_[macro_a] = objects_[macro_a]->orient_;
const float prob = distribution_(generator_);
if (prob <= 0.33) {
objects_[macro_a]->Flip(true, grid_width_, grid_height_, n_cols_, n_rows_);
} else if (prob <= 0.66) {
objects_[macro_a]->Flip(false, grid_width_, grid_height_, n_cols_, n_rows_);
} else {
objects_[macro_a]->Flip(true, grid_width_, grid_height_, n_cols_, n_rows_);
objects_[macro_a]->Flip(false, grid_width_, grid_height_, n_cols_, n_rows_);
}
return true;
}
bool PBFNetlist::Action() {
pre_orients_.clear();
pre_locs_.clear();
const float prob = distribution_(generator_);
if (prob <= action_probs_[0])
return Swap();
else if (prob <= action_probs_[1])
return Shift();
else if (prob <= action_probs_[2])
return Flip();
else if (prob <= action_probs_[3])
return Move();
else
return Shuffle();
}
float PBFNetlist::CalcCostIncr(bool inverse_flag) {
std::map<size_t, std::pair<float, float> > cur_locs;
std::map<size_t, ORIENTATION> cur_orient;
std::vector<size_t> update_macros;
for (auto& value : pre_locs_) {
const int macro_id = value.first;
update_macros.push_back(macro_id);
cur_locs[macro_id] = std::pair<float, float>(objects_[macro_id]->x_,
objects_[macro_id]->y_);
}
for (auto& value : pre_orients_) {
const int macro_id = value.first;
update_macros.push_back(macro_id);
cur_orient[macro_id] = objects_[macro_id]->orient_;
}
// calculate wirelength cost
// update the net
std::set<size_t> nets;
for (auto& macro_id : update_macros) {
for (auto& pin : objects_[macro_id]->macro_pins_) {
for (auto& net : pin->nets_) {
nets.insert(net);
}
}
}
/////////////////////////////////////////////////////////////////////////
// Calculate HPWL for the net
for (auto& net : nets) {
nets_[net]->UpdateHPWL();
if (inverse_flag == false) {
HPWL_ += nets_[net]->HPWL_;
} else {
HPWL_ -= nets_[net]->HPWL_;
}
}
// calculate the density
for (auto& node_id : update_macros) {
const Rect rect = objects_[node_id]->GetBBox();
const GridRect grid_bbox = objects_[node_id]->grid_bbox_;
for (int y_id = grid_bbox.ly_id; y_id <= grid_bbox.uy_id; y_id++) {
for (int x_id = grid_bbox.lx_id; x_id <= grid_bbox.ux_id; x_id++) {
grids_[y_id * n_cols_ + x_id]->UpdateOverlap(rect, !inverse_flag);
}
}
}
// update the routing
for (auto& net : nets)
UpdateRouting(nets_[net], !inverse_flag);
// update the congestion caused by macro
for (auto& macro : update_macros)
UpdateMacroCongestion(objects_[macro], !inverse_flag);
// We need to remove the contributions for the pre_objects
// update the locations
for (auto& value : pre_locs_) {
const int macro_id = value.first;
objects_[macro_id]->SetPos(pre_locs_[macro_id].first,
pre_locs_[macro_id].second,
grid_width_, grid_height_, n_cols_, n_rows_);
}
for (auto& value : pre_orients_) {
const int macro_id = value.first;
objects_[macro_id]->orient_ = pre_orients_[macro_id];
objects_[macro_id]->UpdateOrientation(grid_width_, grid_height_, n_cols_, n_rows_);
}
// remove the HPWL contributed by previous locs
for (auto& net : nets) {
nets_[net]->UpdateHPWL();
if (inverse_flag == false) {
HPWL_ -= nets_[net]->HPWL_; // remove the HPWL
} else {
HPWL_ += nets_[net]->HPWL_;
}
}
// calculate the density
for (auto& node_id : update_macros) {
const Rect rect = objects_[node_id]->bbox_;
const GridRect grid_rect = objects_[node_id]->grid_bbox_;
for (int y_id = grid_rect.ly_id; y_id <= grid_rect.uy_id; y_id++) {
for (int x_id = grid_rect.lx_id; x_id <= grid_rect.ux_id; x_id++) {
grids_[y_id * n_cols_ + x_id]->UpdateOverlap(rect, inverse_flag);
}
}
}
// update the routing
for (auto& net : nets)
UpdateRouting(nets_[net], inverse_flag);
// update the congestion caused by macro
for (auto& macro : update_macros)
UpdateMacroCongestion(objects_[macro], inverse_flag);
if (inverse_flag == true)
return 0.0f;
// get the top 10 % density
std::vector<float> density_list;
for (auto& grid : grids_)
density_list.push_back(grid->GetDensity());
std::sort(density_list.begin(), density_list.end(), std::greater<float>());
density_ = 0.0;
for (int i = 0; i < top_k_; i++)
density_ += density_list[i];
density_ = density_ / top_k_ * 0.5;
// smooth the congestion by net
// reset smooth congestion
for (auto& grid : grids_) {
grid->smooth_hor_congestion_ = 0.0;
grid->smooth_ver_congestion_ = 0.0;
}
for (auto& grid : grids_) {
// smooth vertical congestion
const int col_id = grid->x_id_;
const int row_id = grid->y_id_;
const int v_start = std::max(0, grid->x_id_ - smooth_factor_);
const int v_end = std::min(n_cols_ - 1, grid->x_id_ + smooth_factor_);
const float ver_congestion = grid->ver_congestion_ / (v_end - v_start + 1);
for (int i = v_start; i <= v_end; i++)
grids_[row_id * n_cols_ + i]->smooth_ver_congestion_ += ver_congestion;
const int h_start = std::max(0, grid->y_id_ - smooth_factor_);
const int h_end = std::min(n_rows_ - 1, grid->y_id_ + smooth_factor_);
const float hor_congestion = grid->hor_congestion_ / (h_end - h_start + 1);
for (int j = h_start; j <= h_end; j++)
grids_[j * n_cols_ + col_id]->smooth_hor_congestion_ += hor_congestion;
}
// get the top 0.1 congestion
std::vector<float> congestion_list;
for (auto& grid : grids_) {
congestion_list.push_back(
(grid->smooth_ver_congestion_ + grid->macro_ver_congestion_) / (grid_width_ * vroute_per_micro_)
);
congestion_list.push_back(
(grid->smooth_hor_congestion_ + grid->macro_hor_congestion_) / (grid_height_ * hroute_per_micro_)
);
}
std::sort(congestion_list.begin(), congestion_list.end(), std::greater<float>());
congestion_ = 0.0;
for (int i = 0; i < top_k_congestion_; i++)
congestion_ += congestion_list[i];
congestion_ = congestion_ / top_k_congestion_;
float cost = HPWL_ / norm_HPWL_ + density_ * 0.5 + congestion_ * 0.5;
// restore the locations back
for (auto& value : cur_locs) {
const int macro_id = value.first;
objects_[macro_id]->SetPos(cur_locs[macro_id].first,
cur_locs[macro_id].second,
grid_width_, grid_height_, n_cols_, n_rows_);
}
for (auto& value : cur_orient) {
const int macro_id = value.first;
objects_[macro_id]->orient_ = cur_orient[macro_id];
objects_[macro_id]->UpdateOrientation(grid_width_, grid_height_, n_cols_, n_rows_);
}
return cost;
}
void PBFNetlist::Restore() {
CalcCostIncr(true);
for (auto& macro_pos : pre_locs_)
objects_[macro_pos.first]->SetPos(pre_locs_[macro_pos.first].first,
pre_locs_[macro_pos.first].second,
grid_width_, grid_height_,
n_cols_, n_rows_);
for (auto& macro_orient : pre_orients_) {
objects_[macro_orient.first]->orient_ = pre_orients_[macro_orient.first];
objects_[macro_orient.first]->UpdateOrientation(grid_width_, grid_height_, n_cols_, n_rows_);
}
}
void PBFNetlist::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) {
// Initialization
action_probs_ = action_probs;
for (size_t i = 1; i < action_probs_.size(); i++)
action_probs_[i] += action_probs_[i-1];
const int num_step_per_iter = num_actions;
seed_ = seed;
std::mt19937 generator(seed_);
generator_ = generator;
// Initialize the macro locations
InitMacroPlacement(spiral_flag);
std::cout << "[SA Params] Action probabilities : ";
for (auto& prob : action_probs)
std::cout << prob << " ";
std::cout << std::endl;
std::cout << "[SA Params] num_actions = " << num_actions << std::endl;
std::cout << "[SA Params] max_temperature = " << max_temperature << std::endl;
std::cout << "[SA Params] num_iters = " << num_iters << std::endl;
std::cout << "[SA Params] seed = " << seed << std::endl;
std::cout << "[SA Params] spiral_flag = " << spiral_flag << std::endl;
std::cout << "[SA Params] summary_file = " << summary_file << std::endl;
// calculate temperature updating factor firstly
const int N = num_actions * macros_.size();
const float t_min = 1e-8; // minimum temperature
const float t_factor = std::log(t_min / max_temperature);
float t = max_temperature;
float cur_cost = 0.0;
std::vector<float> cost_list; // we need to plot the cost curve
CallFDPlacer();
for (int num_iter = 0; num_iter < num_iters; num_iter++) {
// call FDPlacer to update the cost
CallFDPlacer();
cur_cost = CalcCost();
for (int step = 0; step < N; step++) {
if (Action() == true) {
const float new_cost = CalcCostIncr(false);
if (new_cost < cur_cost) {
cur_cost = new_cost;
}
float prob = std::exp((cur_cost - new_cost) / t);
if (prob < distribution_(generator_)) {
Restore();
} else {
cur_cost = new_cost;
}
}
cost_list.push_back(cur_cost);
}
// update the temperature
t = max_temperature * std::exp(t_factor * num_iter / num_iters);
}
std::ofstream f;
f.open(summary_file);
for (auto& value : cost_list)
f << value << std::endl;
f.close();
}
#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