v2.1.1 (1) support custom_json (2) add visualize_cgmap

This commit is contained in:
liulixinkerry 2024-04-18 12:56:30 +08:00
parent 60f0920cf6
commit 8db7d4f549
24 changed files with 143 additions and 32 deletions

View File

@ -110,6 +110,12 @@ Suppose there is a LEF/DEF benchmark named `toy` in `data/raw`, you can use the
python main.py --custom_path lef:data/raw/toy_input.lef,def:data/raw/toy_input.def,design_name:toy,benchmark:test --load_from_raw True --detail_placement True
```
You can also use the argument `--custom_json` to run multiple LEFs + DEF:
```
python main.py --custom_json examples/examples.json --load_from_raw True --target_density 0.9
```
Please provide your LEFs/DEF in the input `json` file. An example of [ASAP7](https://github.com/The-OpenROAD-Project/asap7) input is given in `./examples/examples.json`.
## Load design from preprocessed `pt` file (Optional)
The following script will dump the parsed design into a single torch `pt` file so Xplace can load the design from the `pt` file instead of parsing the input file from scratch.

View File

@ -32,6 +32,11 @@ void Database::load() {
setting.Format = "lefdef";
readLEF(setting.LefTech);
readLEF(setting.LefCell);
} else if (setting.LefFiles.size() > 0) {
setting.Format = "lefdef";
for (auto lef : setting.LefFiles) {
readLEF(lef);
}
}
if (setting.DefFile != "") {

View File

@ -70,8 +70,8 @@ public:
vector<int> parWidth;
vector<vector<int>> parWidthSpace; // 2D table: (parWidth, parLength) -> widthSpacing
Track track;
Track nonPreferDirTrack;
vector<Track> tracks;
vector<Track> nonPreferDirTracks;
Layer(const string& name = "", const char type = 'x') : _name(name), _type(type) {}

View File

@ -10,6 +10,7 @@ void Setting::reset() {
LefFile = "";
LefCell = "";
LefTech = "";
LefFiles.clear();
Constraints = "";
Verilog = "";
OutputFile = "";

View File

@ -27,6 +27,7 @@ public:
std::string LefFile = "";
std::string LefCell = "";
std::string LefTech = "";
std::vector<std::string> LefFiles;
std::string Constraints = "";
std::string Verilog = "";

View File

@ -490,10 +490,10 @@ bool Database::readBSAux(const std::string& auxFile, const std::string& plFile)
// } else if (i % 2) {
if (i % 2) {
layer.direction = m2direction;
layer.track.direction = m2direction;
// layer.track.direction = m2direction;
} else {
layer.direction = m1direction;
layer.track.direction = m1direction;
// layer.track.direction = m1direction;
}
if (isDac2012) {
layer.width = bsData.wireWidth[i];

View File

@ -1325,11 +1325,11 @@ int readDefTrack(defrCallbackType_e c, defiTrack* dtrack, defiUserData ud) {
Layer* layer = db->getLayer(layername);
if (layer) {
if (track->direction == 'x' || layer->direction == 'x') {
layer->track = *track;
layer->tracks.push_back(*track);
} else if (layer->direction == track->direction) {
layer->track = *track;
layer->tracks.push_back(*track);
} else if (layer->direction != track->direction) {
layer->nonPreferDirTrack = *track;
layer->nonPreferDirTracks.push_back(*track);
} else {
logger.error("wrong definition of tracks for layer %s", layername.c_str());
}

View File

@ -101,8 +101,10 @@ bool Drawer::run(const std::vector<double>& node_pos_x, // after die scale
const std::tuple<double, double, double, double>& die_info, // after die scale
const std::tuple<double, double>& site_info,
const std::tuple<double, double>& bin_size_info, // after die scale
std::vector<std::tuple<index_type, index_type, std::string>> node_types_indices) {
std::vector<std::tuple<index_type, index_type, std::string>> node_types_indices,
const std::vector<int>& node_special_type) {
// init cairo
bool special_cell = !node_special_type.empty();
cairo_surface_t* cs;
if (format == "png") {
cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
@ -190,7 +192,19 @@ bool Drawer::run(const std::vector<double>& node_pos_x, // after die scale
double node_lx = node_pos_x[i] - node_size_x[i] / 2;
double node_ly = node_pos_y[i] - node_size_y[i] / 2;
cairo_rectangle(c, node_lx, node_ly, node_size_x[i], node_size_y[i]);
set_rgba(node_type);
if (special_cell && i < node_special_type.size() && node_special_type[i] > 0){
switch (node_special_type[i]) {
case 1:
set_rgba("Buffer");
break;
case 2:
set_rgba("FF");
break;
default:
set_rgba(node_type);
break;
}
} else set_rgba(node_type);
cairo_fill(c);
if (draw_node_bd) {
cairo_rectangle(c, node_lx, node_ly, node_size_x[i], node_size_y[i]);

View File

@ -23,5 +23,6 @@ public:
const std::tuple<double, double, double, double>& die_info,
const std::tuple<double, double>& site_info,
const std::tuple<double, double>& bin_size_info,
std::vector<std::tuple<index_type, index_type, std::string>> node_types_indices);
std::vector<std::tuple<index_type, index_type, std::string>> node_types_indices,
const std::vector<int>& node_special_type);
};

View File

@ -11,6 +11,7 @@ bool DrawGlobalPlacement(
const std::tuple<double, double>& bin_size_info,
const std::vector<std::tuple<index_type, index_type, std::string>>& node_types_indices,
const std::vector<std::tuple<std::string, double, double, double, double>>& ele_type_to_rgba_vec,
const std::vector<int>& node_special_type,
const std::string& filename,
double width,
double height,
@ -25,8 +26,8 @@ bool DrawGlobalPlacement(
die_info,
site_info,
bin_size_info,
node_types_indices);
node_types_indices,
node_special_type);
return status;
}
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { m.def("draw", &DrawGlobalPlacement, "Draw placement results"); }

View File

@ -15,7 +15,7 @@ GRDatabase::GRDatabase(std::shared_ptr<db::Database> rawdb_, std::shared_ptr<gp:
} else {
csrnScale = grSetting.csrnScale;
}
if (db::setting.BookshelfVariety != "" || db::setting.LefFile == "") {
if (db::setting.BookshelfVariety != "" || db::setting.Format != "lefdef") {
// NOTE: GGR is a LEFDEF based detailed-routability driven global placer and it is not
// designed for the old bookshelf designs. For bookshelf, please consider to use
// NCTU-GR to generate the routing congestion maps. Note that existing bookshelf
@ -33,10 +33,13 @@ GRDatabase::GRDatabase(std::shared_ptr<db::Database> rawdb_, std::shared_ptr<gp:
auto rLayer = rawdb.getRLayer(l);
layerWidth[l] = rLayer->width;
layerPitch[l] = rLayer->pitch;
db::Track& track = rLayer->track;
for (int i = 0; i < track.num; i++) {
tracks[l].emplace_back(i * track.step + track.start);
for (auto& track : rLayer->tracks) {
for (int i = 0; i < track.num; i++) {
tracks[l].emplace_back(i * track.step + track.start);
}
}
sort(tracks[l].begin(), tracks[l].end());
tracks[l].erase(unique(tracks[l].begin(), tracks[l].end()), tracks[l].end());
}
m1direction = rawdb.getRLayer(0)->direction == 'v' ? 1 : 0;
microns = rawdb.LefConvertFactor;
@ -448,11 +451,11 @@ utils::PointT<int> GRDatabase::getObsMargin(RectOnLayer box, AggrParaRunSpace ag
utils::IntervalT<int> GRDatabase::rangeSearchTracks(const utils::IntervalT<int>& locRange, int layerIdx) {
auto& t = tracks[layerIdx];
int lpos = locRange.low / layerPitch[layerIdx];
while (lpos + 1 < t.size() && t[lpos] < locRange.low) lpos++;
int lpos = lower_bound(t.begin(), t.end(), locRange.low) - t.begin();
lpos = std::min(static_cast<int>(t.size()) - 1, lpos);
while (lpos > 0 && t[lpos - 1] >= locRange.low) lpos--;
int hpos = locRange.high / layerPitch[layerIdx];
while (hpos > 0 && t[hpos] > locRange.high) hpos--;
int hpos = upper_bound(t.begin(), t.end(), locRange.high) - t.begin() - 1;
hpos = std::max(hpos, 0);
return utils::IntervalT<int>(lpos, hpos);
}
@ -480,9 +483,9 @@ void GRDatabase::markObs(std::vector<RectOnLayer>& allObs,
vector<vector<vector<std::pair<utils::IntervalT<int>, int>>>> markingBufferLUT;
auto searchLowerBoundTrack = [&](int p) {
int pos = p / layerPitch[l];
while (pos + 1 < t.size() && t[pos] < p) pos++;
while (pos && t[pos - 1] >= p) pos--;
int pos = lower_bound(t.begin(), t.end(), p) - t.begin();
pos = std::min(static_cast<int>(t.size()) - 1, pos);
while (pos > 0 && t[pos - 1] >= p) pos--;
return pos;
};
@ -561,12 +564,12 @@ void GRDatabase::markObs(std::vector<RectOnLayer>& allObs,
}
for (int i = 0; i < markingBufferLUT.size(); i++) {
utils::IntervalT<int> gridTrackIntvl;
gridTrackIntvl.low = searchLowerBoundTrack(gridlines[1 - dir][i]);
gridTrackIntvl.high = searchLowerBoundTrack(gridlines[1 - dir][i + 1]) - 1;
for (int j = 0; j < markingBufferLUT[i].size(); j++) {
if (markingBufferLUT[i][j].size() == 0) continue;
const auto& buf = markingBufferLUT[i][j];
utils::IntervalT<int> gridTrackIntvl;
gridTrackIntvl.low = searchLowerBoundTrack(gridlines[1 - dir][i]);
gridTrackIntvl.high = searchLowerBoundTrack(gridlines[1 - dir][i + 1]) - 1;
vector<int> trackBlocked(gridTrackIntvl.range() + 1, 0); // blocked track length
for (auto& pair : buf) {
for (int k = pair.first.low; k <= pair.first.high; k++) {

View File

@ -55,6 +55,7 @@ void bindGPDatabase(pybind11::module& m) {
.def("m1direction", &gp::GPDatabase::getM1Direction, py::return_value_policy::copy)
.def("node_type_indices", &gp::GPDatabase::getNodeTypeIndices, py::return_value_policy::copy)
.def("node_id2node_name", &gp::GPDatabase::getNodeId2NodeName, py::return_value_policy::copy)
.def("node_id2celltype_name", &gp::GPDatabase::getNodeId2CellTypeName, py::return_value_policy::copy)
.def("node_lpos_tensor", &gp::GPDatabase::getNodeLPosTensor, py::return_value_policy::move)
.def("node_cpos_tensor", &gp::GPDatabase::getNodeCPosTensor, py::return_value_policy::move)
.def("node_size_tensor", &gp::GPDatabase::getNodeSizeTensor, py::return_value_policy::move)

View File

@ -27,6 +27,11 @@ bool loadParams(const py::dict& kwargs) {
} else if (kwargs.contains("cell_lef") && kwargs.contains("tech_lef")) {
db::setting.LefCell = kwargs["cell_lef"].cast<std::string>();
db::setting.LefTech = kwargs["tech_lef"].cast<std::string>();
} else if (kwargs.contains("lefs")) {
for (auto lef_py : kwargs["lefs"]) {
auto lef = lef_py.cast<std::string>();
db::setting.LefFiles.emplace_back(lef);
}
}
if (kwargs.contains("constraints")) {

View File

@ -21,6 +21,9 @@ void GPDatabase::addCellNode(index_type cell_id, std::string& node_type) {
node.setRegionId(static_cast<index_type>(cell->region->id));
regions[node.getRegionId()].addNode(node.getId());
std::string celltype_name_ = cell->ctype()->cls + "/" + cell->ctype()->name;
node.setCellTypeName(celltype_name_);
if (cell->fixed() && cell->ctype()->nonRegularRects().size() > 0) {
node.setIsPolygonShape(true);
}
@ -41,6 +44,8 @@ void GPDatabase::addIOPinNode(index_type iopin_id, std::string& node_type) {
node.setNodeType(node_type);
node.setOriDBId(iopin_id);
node.setCellTypeName(iopin->name);
iopin->gpdb_id = nodes.size() - 1;
}
@ -58,6 +63,8 @@ void GPDatabase::addBlockageNode(index_type blkg_id, std::string& node_type) {
node.setOrient(-1); // No orientation for placement blockage
node.setNodeType(node_type);
node.setOriDBId(blkg_id);
node.setCellTypeName(blockage_name);
}
void GPDatabase::addNet(index_type dbnet_id) {
@ -270,6 +277,7 @@ void GPDatabase::setupIndexMap() {
}
for (auto& node : nodes) {
node_id2node_name.emplace_back(node.getName());
node_id2celltype_name.emplace_back(node.getCellTypeName());
}
}

View File

@ -42,6 +42,8 @@ public:
const std::string& getNodeType() const { return node_type; }
void setOriDBId(const index_type& ori_db_id_) { ori_db_id = ori_db_id_; }
const index_type& getOriDBId() const { return ori_db_id; }
void setCellTypeName(const std::string& celltype_name_) { celltype_name = celltype_name_; }
const std::string& getCellTypeName() const { return celltype_name; }
void setRegionId(const index_type& region_id_) { region_id = region_id_; }
const index_type& getRegionId() const { return region_id; }
@ -62,6 +64,7 @@ protected:
index_type region_id = -1; // no region, we should ignore fixed nodes' fence region
std::vector<index_type> pins_id;
bool isPolygonShape = false;
std::string celltype_name = ""; // CORE/DFF*, BLOCK/RAM*, IOPIN/PinName
};
class GPPin : public Basic {
@ -161,6 +164,7 @@ protected:
std::vector<std::tuple<index_type, index_type, std::string>> node_types_indices; // (start_idx, end_idx, type)
std::vector<std::string> node_id2node_name;
std::vector<std::string> node_id2celltype_name;
std::vector<index_type> pin_id2node_id; // pin_id to node_id mapping
std::vector<index_type> pin_id2net_id; // pin_id to net_id mapping
@ -199,6 +203,7 @@ public:
return node_types_indices;
}
const std::vector<std::string>& getNodeId2NodeName() const { return node_id2node_name; }
const std::vector<std::string>& getNodeId2CellTypeName() const { return node_id2celltype_name; }
const std::tuple<int, int, int, int>& getDieInfo() const { return dieInfo; }
const std::tuple<int, int, int, int>& getCoreInfo() const { return coreInfo; }

11
examples/examples.json Normal file
View File

@ -0,0 +1,11 @@
{
"benchmark": "custom_asap7",
"design_name": "top",
"lefs": [
"/PDK/asap7/lef/asap7_tech_1x_201209.lef",
"/PDK/asap7/lef/asap7sc7p5t_28_L_1x_220121a.lef",
"/PDK/asap7/lef/asap7sc7p5t_28_R_1x_220121a.lef",
"/PDK/asap7/lef/asap7sc7p5t_28_SL_1x_220121a.lef"
],
"def": "/designs/top/def/top.def"
}

View File

@ -8,6 +8,7 @@ def get_option():
parser.add_argument('--dataset', type=str, default='ispd2015_fix', help='dataset name')
parser.add_argument('--design_name', type=str, default='mgc_superblue12', help='design name')
parser.add_argument('--custom_path', type=str, default='', help='custom design path, set it as token1:path1,token2:path2 e.g. lef:data/test.lef,def:data/test.def,design_name:mydesign,benchmark:mybenchmark')
parser.add_argument('--custom_json', type=str, default='', help='custom json path, support multi-lefs.')
parser.add_argument('--load_from_raw', type=str2bool, default=True, help='If True, parse and load from benchmark files. If False, load from pt')
parser.add_argument('--run_all', type=str2bool, default=False, help='If True, run all designs in the given dataset. If False, run the given design_name only.')
parser.add_argument('--seed', type=int, default=0, help='seed to initialize all the random modules')
@ -46,6 +47,7 @@ def get_option():
parser.add_argument('--route_weight', type=float, default=0, help='the weight of route')
parser.add_argument('--congest_weight', type=float, default=0, help='the weight of congested force')
parser.add_argument('--pseudo_weight', type=float, default=0, help='the weight of pseudo net')
parser.add_argument('--visualize_cgmap', type=str2bool, default=False, help='visualize congestion map')
# detailed placement and evaluation
parser.add_argument('--detail_placement', type=str2bool, default=True, help='perform dp')

View File

@ -9,6 +9,9 @@ def load_dataset(args, logger, placement=None):
rawdb, gpdb = None, None
if args.custom_path != "":
params = get_custom_design_params(args)
elif args.custom_json != "":
logger.info("Detect json mode. Please make sure that tech_lef are included first.")
params = get_custom_json_params(args)
else:
params = get_single_design_params(
args.dataset_root, args.dataset, args.design_name, placement
@ -80,6 +83,7 @@ class PlaceData(object):
site_info=None,
node_type_indices=None,
node_id2node_name=None,
node_id2celltype_name=None,
movable_index=None,
connected_index=None,
fixed_index=None,
@ -112,6 +116,15 @@ class PlaceData(object):
self.region_boxes = region_boxes
self.region_boxes_end = region_boxes_end
# TODO: more cases?
self.node_special_type = torch.zeros(len(node_id2celltype_name), dtype=torch.int32)
if False:
for node_id, celltype_name in enumerate(node_id2celltype_name):
if celltype_name.startswith("CORE/BUF"):
self.node_special_type[node_id] = 1
if celltype_name.startswith("CORE/DFF"):
self.node_special_type[node_id] = 2
dataset_format = ""
if "aux" in dataset_path.keys():
dataset_format = "bookshelf"
@ -122,6 +135,7 @@ class PlaceData(object):
self.__design_name__ = benchmark + "/" + dataset_path["design_name"]
self.__node_id2node_name__ = node_id2node_name
self.__node_id2celltype_name__ = node_id2celltype_name
# NOTE: we set float movable node as connected node for convenience purposes
self.__node_type_indices__ = node_type_indices
@ -196,6 +210,11 @@ class PlaceData(object):
if hasattr(self, "__node_id2node_name__"):
return self.__node_id2node_name__
@property
def node_id2celltype_name(self):
if hasattr(self, "__node_id2celltype_name__"):
return self.__node_id2celltype_name__
@property
def node_type_indices(self):
if hasattr(self, "__node_type_indices__"):

View File

@ -4,11 +4,9 @@ from src import run_placement_main_nesterov
def run_placement_single(args, logger):
logger.info("=================")
logger.info("Start place %s/%s" % (args.dataset , args.design_name))
assert torch.cuda.is_available()
torch.cuda.synchronize("cuda:{}".format(args.gpu))
set_random_seed(args)
setup_dataset_args(args)
res = run_placement_main_nesterov(args, logger)
return res

View File

@ -13,10 +13,12 @@ def get_trunc_node_pos_fn(mov_node_size, data):
def run_placement_main_nesterov(args, logger):
total_start = time.time()
data, rawdb, gpdb = load_dataset(args, logger)
setup_dataset_args(args)
device = torch.device(
"cuda:{}".format(args.gpu) if torch.cuda.is_available() else "cpu"
)
assert args.use_eplace_nesterov
logger.info("Start place %s/%s" % (args.dataset , args.design_name))
logger.info("Use Nesterov optimizer!")
if args.scale_design:
logger.warning("Eplace's nesterov optimizer cannot support normalized die. Disable scale_design.")
@ -187,7 +189,7 @@ def run_placement_main_nesterov(args, logger):
if ps.use_cell_inflate:
output = route_inflation(
args, logger, data, rawdb, gpdb, ps, mov_node_pos, mov_node_size, expand_ratio,
constraint_fn=trunc_node_pos_fn,
constraint_fn=trunc_node_pos_fn, visualize=args.visualize_cgmap
) # ps.use_cell_inflate is updated in route_inflation
if not ps.use_cell_inflate:
route_early_terminate_signal = True
@ -294,7 +296,7 @@ def run_placement_main_nesterov(args, logger):
ps.rerun_route = True
gr_metrics = run_gr_and_fft_main(
args, logger, data, rawdb, gpdb, ps, mov_node_pos, constraint_fn=trunc_node_pos_fn,
skip_m1_route=True, report_gr_metrics_only=True
skip_m1_route=True, report_gr_metrics_only=True, visualize=args.visualize_cgmap
)
ps.rerun_route = False
ps.push_gr_sol(gr_metrics, hpwl, overflow, mov_node_pos)

View File

@ -2,5 +2,5 @@ from .io_parser import IOParser
from .logger import setup_logger
from .visualization import *
from .tools import *
from .get_design_params import get_single_design_params, get_multiple_design_params, get_custom_design_params
from .get_design_params import get_single_design_params, get_multiple_design_params, get_custom_design_params, get_custom_json_params
from .setup_dataset import setup_dataset_args

View File

@ -185,3 +185,16 @@ def get_custom_design_params(args):
args.dataset = params["benchmark"]
args.design_name = params["design_name"]
return params
def get_custom_json_params(args):
import json
with open(args.custom_json, 'r') as f:
params = json.load(f)
if "benchmark" not in params.keys():
raise ValueError("Cannot find 'benchmark' in args.custom_path")
if "design_name" not in params.keys():
raise ValueError("Cannot find 'design_name' in args.custom_path")
args.dataset = params["benchmark"]
args.design_name = params["design_name"]
return params

View File

@ -41,7 +41,15 @@ class IOParser(object):
print("Can only support one input format!")
return False
if "def" in params.keys():
if "lef" not in params.keys():
if "lefs" in params.keys():
valid_flag = True
for lef in params["lefs"]:
if not os.path.exists(lef):
print("lef %s not exists." % lef)
valid_flag = False
if not valid_flag:
return False
elif "lef" not in params.keys():
if "cell_lef" not in params.keys() and "tech_lef" not in params.keys():
print("lef or (cell_lef and tech_lef) is not found")
return False
@ -171,6 +179,7 @@ class IOParser(object):
node_type_indices = gpdb.node_type_indices()
node_id2node_name = gpdb.node_id2node_name()
node_id2celltype_name = gpdb.node_id2celltype_name()
all_node_types = []
mov_end_idx = None
fix_end_idx = None
@ -195,6 +204,7 @@ class IOParser(object):
"dataset_path": self.params,
"node_type_indices": node_type_indices,
"node_id2node_name": node_id2node_name,
"node_id2celltype_name": node_id2celltype_name,
"movable_index": movable_index,
"connected_index": connected_index,
"fixed_index": fixed_index,

View File

@ -175,7 +175,11 @@ def draw_fig_with_cairo_cpp(node_pos, node_size, data, info, args, base_size=204
("Bin", 0.1, 0.1, 0.1, 1.0),
("Mov", 0.475, 0.706, 0.718, 0.8),
("Filler", 0.8, 0.8, 0.8, 0.8),
("Buffer", 0.65, 0.08, 0.9, 0.8),
("FF", 0.65, 0.9, 0.08, 0.7),
]
node_special_type: List[int] = (data.node_special_type.cpu()).tolist()
width = base_size
height = round(width * (hy - ly) / (hx - lx))
draw_contents: List[str] = ["Nodes", "NodesText"]
@ -191,6 +195,7 @@ def draw_fig_with_cairo_cpp(node_pos, node_size, data, info, args, base_size=204
bin_size_info,
node_type_indices,
ele_type_to_rgba_vec,
node_special_type,
png_path,
width,
height,