Skip to content

Commit 74c7c7a

Browse files
[AP] Created AP Netlist and Fixed Bug in B2B
Instead of iterating over the AtomNetlist, created our own netlist object which is a vector of vectors. This can be made much more efficient by using the Netlist class as a base class; however, doing this now to make is easier. In the process of implementing this feature, fixed a bug in the B2B formulation which was causing it not to converge.
1 parent 027b2c5 commit 74c7c7a

File tree

5 files changed

+101
-86
lines changed

5 files changed

+101
-86
lines changed

vpr/src/place/analytical_placement/AnalyticalSolver.cpp

Lines changed: 40 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,12 @@
99
#include <limits>
1010
#include <memory>
1111
#include <random>
12-
#include <unordered_set>
1312
#include <utility>
1413
#include <vector>
1514
#include "PartialPlacement.h"
16-
#include "atom_netlist.h"
1715
#include "globals.h"
18-
#include "partition_region.h"
1916
#include "vpr_context.h"
2017
#include "vpr_error.h"
21-
#include "vpr_types.h"
2218
#include "vtr_assert.h"
2319

2420
std::unique_ptr<AnalyticalSolver> make_analytical_solver(e_analytical_solver solver_type) {
@@ -96,11 +92,9 @@ static inline void populate_hybrid_matrix(Eigen::SparseMatrix<double> &A_sparse,
9692
Eigen::VectorXd &b_y,
9793
PartialPlacement& p_placement) {
9894
size_t num_star_nodes = 0;
99-
const AtomNetlist& netlist = p_placement.atom_netlist;
100-
for (AtomNetId net_id : netlist.nets()) {
101-
if (p_placement.net_is_ignored_for_placement(net_id))
102-
continue;
103-
if (netlist.net_pins(net_id).size() > 3)
95+
for (const std::vector<size_t> &net : p_placement.ap_netlist) {
96+
size_t num_pins = net.size();
97+
if (num_pins > 3)
10498
num_star_nodes++;
10599
}
106100

@@ -109,28 +103,19 @@ static inline void populate_hybrid_matrix(Eigen::SparseMatrix<double> &A_sparse,
109103
b_x = Eigen::VectorXd::Zero(num_moveable_nodes + num_star_nodes);
110104
b_y = Eigen::VectorXd::Zero(num_moveable_nodes + num_star_nodes);
111105

112-
const AtomContext& atom_ctx = g_vpr_ctx.atom();
113-
106+
size_t num_nets = p_placement.ap_netlist.size();
114107
std::vector<Eigen::Triplet<double>> tripletList;
115-
tripletList.reserve(num_moveable_nodes * netlist.nets().size());
108+
tripletList.reserve(num_moveable_nodes * num_nets);
116109

117110
size_t star_node_offset = 0;
118-
// FIXME: Instead of iterating over the whole nelist and reverse looking up
119-
// it may make more sense to pre-compute the netlist.
120-
for (AtomNetId net_id : netlist.nets()) {
121-
if (p_placement.net_is_ignored_for_placement(net_id))
122-
continue;
123-
int num_pins = netlist.net_pins(net_id).size();
111+
for (const std::vector<size_t> &net : p_placement.ap_netlist) {
112+
size_t num_pins = net.size();
124113
VTR_ASSERT(num_pins > 1);
125114
if (num_pins > 3) {
126-
// FIXME: THIS WAS DIRECTLY COPIED FROM THE STAR FORMULATION. MOVE TO OWN FUNCTION.
127-
// (with the exeption of the star node offset).
128115
// Using the weight from FastPlace
129116
double w = static_cast<double>(num_pins) / static_cast<double>(num_pins - 1);
130117
size_t star_node_id = num_moveable_nodes + star_node_offset;
131-
for (AtomPinId pin_id : netlist.net_pins(net_id)) {
132-
AtomBlockId blk_id = netlist.pin_block(pin_id);
133-
size_t node_id = p_placement.get_node_id_from_blk(blk_id, atom_ctx.atom_molecules);
118+
for (size_t node_id : net) {
134119
// Note: the star node is always moveable
135120
if (p_placement.is_moveable_node(node_id)) {
136121
tripletList.emplace_back(star_node_id, star_node_id, w);
@@ -145,19 +130,13 @@ static inline void populate_hybrid_matrix(Eigen::SparseMatrix<double> &A_sparse,
145130
}
146131
star_node_offset++;
147132
} else {
148-
// FIXME: THIS WAS DIRECTLY COPIED FROM THE CLIQUE FORMULATION. MOVE TO OWN FUNCTION.
149133
// Using the weight from FastPlace
150134
double w = 1.0 / static_cast<double>(num_pins - 1);
151135

152-
for (int ipin = 0; ipin < num_pins; ipin++) {
153-
// FIXME: Is it possible for two pins to be connected to the same block?
154-
// I am wondering if this doesnt matter because it would appear as tho
155-
// this block really wants to be connected lol.
156-
AtomBlockId first_block_id = netlist.net_pin_block(net_id, ipin);
157-
size_t first_node_id = p_placement.get_node_id_from_blk(first_block_id, atom_ctx.atom_molecules);
158-
for (int jpin = ipin + 1; jpin < num_pins; jpin++) {
159-
AtomBlockId second_block_id = netlist.net_pin_block(net_id, jpin);
160-
size_t second_node_id = p_placement.get_node_id_from_blk(second_block_id, atom_ctx.atom_molecules);
136+
for (size_t inode_idx = 0; inode_idx < num_pins; inode_idx++) {
137+
size_t first_node_id = net[inode_idx];
138+
for (size_t jnode_idx = inode_idx + 1; jnode_idx < num_pins; jnode_idx++) {
139+
size_t second_node_id = net[jnode_idx];
161140
// Make sure that the first node is moveable. This makes creating the connection easier.
162141
if (!p_placement.is_moveable_node(first_node_id)) {
163142
if (!p_placement.is_moveable_node(second_node_id)) {
@@ -268,7 +247,7 @@ void B2BSolver::initialize_placement_least_dense(PartialPlacement &p_placement)
268247
}
269248

270249
// This function return the two nodes on the bound of a netlist, (max, min)
271-
std::pair<size_t, size_t> B2BSolver::boundNode(std::vector<size_t>& node_ids, std::vector<double>& node_locs){
250+
std::pair<size_t, size_t> B2BSolver::boundNode(const std::vector<size_t>& node_ids, const std::vector<double>& node_locs){
272251
auto compare = [&node_locs](size_t a, size_t b) {
273252
return node_locs[a] < node_locs[b];
274253
};
@@ -279,54 +258,36 @@ std::pair<size_t, size_t> B2BSolver::boundNode(std::vector<size_t>& node_ids, st
279258
}
280259

281260
void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
282-
const AtomContext& atom_ctx = g_vpr_ctx.atom();
283-
const AtomNetlist& netlist = p_placement.atom_netlist;
284261
// Resetting As bs
285262
A_sparse_x = Eigen::SparseMatrix<double>(A_sparse_x.rows(), A_sparse_x.cols());
286263
A_sparse_y = Eigen::SparseMatrix<double>(A_sparse_y.rows(), A_sparse_y.cols());
287264
// A_sparse_x.setZero();
288265
// A_sparse_y.setZero();
266+
size_t num_nets = p_placement.ap_netlist.size();
289267
std::vector<Eigen::Triplet<double>> tripletList_x;
290-
tripletList_x.reserve(p_placement.num_moveable_nodes * netlist.nets().size());
268+
tripletList_x.reserve(p_placement.num_moveable_nodes * num_nets);
291269
std::vector<Eigen::Triplet<double>> tripletList_y;
292-
tripletList_y.reserve(p_placement.num_moveable_nodes * netlist.nets().size());
270+
tripletList_y.reserve(p_placement.num_moveable_nodes * num_nets);
293271
b_x = Eigen::VectorXd::Zero(p_placement.num_moveable_nodes);
294272
b_y = Eigen::VectorXd::Zero(p_placement.num_moveable_nodes);
295273

296-
for (AtomNetId net_id : netlist.nets()) {
297-
if (p_placement.net_is_ignored_for_placement(net_id))
298-
continue;
299-
300-
int num_pins = netlist.net_pins(net_id).size();
274+
for (const std::vector<size_t> &net : p_placement.ap_netlist) {
275+
int num_pins = net.size();
301276
VTR_ASSERT(num_pins > 1 && "net least has at least 2 pins");
302-
303-
std::vector<size_t> node_ids;
304-
for (AtomPinId pin_id : netlist.net_pins(net_id)) {
305-
AtomBlockId blk_id = netlist.pin_block(pin_id);
306-
size_t node_id = p_placement.get_node_id_from_blk(blk_id, atom_ctx.atom_molecules);
307-
node_ids.push_back(node_id);
308-
}
309-
// remove duplicated node, they are there becaues of prepacked molecules.
310-
// FIXME: duplicate exists because atoms are packed in to molecules so some edges are now hidden.
311-
// We can create our own netlist class to resolve this problem.
312-
std::set<size_t> node_ids_set(node_ids.begin(), node_ids.end());
313-
std::vector<size_t> node_ids_no_duplicate(node_ids_set.begin(), node_ids_set.end());
314-
315-
if (node_ids_no_duplicate.size() <= 1){
316-
continue;
317-
}
318-
// TODO: do this in a for loop instead of creating vectors
319-
auto [maxXId, minXId] = boundNode(node_ids_no_duplicate, p_placement.node_loc_x);
320-
auto [maxYId, minYId] = boundNode(node_ids_no_duplicate, p_placement.node_loc_y);
277+
278+
// TODO: do this in a single for loop. Will likely be more efficient than
279+
// iterating 4 times.
280+
auto [maxXId, minXId] = boundNode(net, p_placement.node_loc_x);
281+
auto [maxYId, minYId] = boundNode(net, p_placement.node_loc_y);
321282
// assign arbitrary node as bound node when they are all equal
322283
// TODO: although deterministic, investigate other ways to break ties.
323284
if (maxXId == minXId) {
324-
maxXId = node_ids_no_duplicate[0];
325-
minXId = node_ids_no_duplicate[1];
285+
maxXId = net[0];
286+
minXId = net[1];
326287
}
327288
if (maxYId == minYId) {
328-
maxYId = node_ids_no_duplicate[0];
329-
minYId = node_ids_no_duplicate[1];
289+
maxYId = net[0];
290+
minYId = net[1];
330291
}
331292
auto add_node = [&](size_t first_node_id, size_t second_node_id, unsigned num_nodes, bool is_x){
332293
if (!p_placement.is_moveable_node(first_node_id)) {
@@ -366,20 +327,19 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
366327
}
367328
}
368329
};
369-
// TODO: when adding custom netlist, also modify here.
370-
size_t num_nodes = node_ids_no_duplicate.size();
371-
for (size_t node_id = 0; node_id < num_nodes; node_id++) {
330+
331+
for (size_t node_id : net) {
372332
if (node_id != maxXId && node_id != minXId) {
373-
add_node(node_id, maxXId, num_nodes, true);
374-
add_node(node_id, minXId, num_nodes, true);
333+
add_node(node_id, maxXId, num_pins, true);
334+
add_node(node_id, minXId, num_pins, true);
375335
}
376336
if (node_id != maxYId && node_id != minYId) {
377-
add_node(node_id, maxYId, num_nodes, false);
378-
add_node(node_id, minYId, num_nodes, false);
337+
add_node(node_id, maxYId, num_pins, false);
338+
add_node(node_id, minYId, num_pins, false);
379339
}
380340
}
381-
add_node(maxXId, minXId, num_nodes, true);
382-
add_node(maxYId, minYId, num_nodes, false);
341+
add_node(maxXId, minXId, num_pins, true);
342+
add_node(maxYId, minYId, num_pins, false);
383343
}
384344
A_sparse_x.setFromTriplets(tripletList_x.begin(), tripletList_x.end());
385345
A_sparse_y.setFromTriplets(tripletList_y.begin(), tripletList_y.end());
@@ -388,8 +348,11 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
388348
// This function adds anchors for legalized solution. Anchors are treated as fixed node,
389349
// each connecting to a movable node. Number of nodes in a anchor net is always 2.
390350
void B2BSolver::populate_matrix_anchor(PartialPlacement& p_placement, unsigned iteration) {
391-
double coeff_pseudo_anchor = 0.001 * std::exp((double)iteration/29.0);
351+
// double coeff_pseudo_anchor = 0.001 * std::exp((double)iteration/29.0);
392352
// double coeff_pseudo_anchor = std::exp((double)iteration/1.0);
353+
354+
// Using alpha from the SimPL paper
355+
double coeff_pseudo_anchor = 0.01 * (1.0 + static_cast<double>(iteration));
393356
for (size_t i = 0; i < p_placement.num_moveable_nodes; i++){
394357
// Anchor node are always 2 pins.
395358
double pseudo_w_x = coeff_pseudo_anchor*2.0/std::max(std::abs(p_placement.node_loc_x[i] - node_loc_x_legalized[i]), epsilon);
@@ -479,4 +442,4 @@ void B2BSolver::solve(unsigned iteration, PartialPlacement &p_placement) {
479442
// store solved position in data structure of this class
480443
// node_loc_x_solved = p_placement.node_loc_x;
481444
// node_loc_y_solved = p_placement.node_loc_y;
482-
}
445+
}

vpr/src/place/analytical_placement/AnalyticalSolver.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class B2BSolver : public AnalyticalSolver {
4545
void initialize_placement_least_dense(PartialPlacement &p_placement);
4646
void populate_matrix(PartialPlacement &p_placement);
4747
void populate_matrix_anchor(PartialPlacement& p_placement, unsigned iteration);
48-
std::pair<size_t, size_t> boundNode(std::vector<size_t> &node_id, std::vector<double> &node_loc);
48+
std::pair<size_t, size_t> boundNode(const std::vector<size_t> &node_id, const std::vector<double> &node_loc);
4949

5050
static inline const double epsilon = 1e-6;
5151
static inline const unsigned inner_iterations = 30;
@@ -63,4 +63,4 @@ class B2BSolver : public AnalyticalSolver {
6363
std::vector<double> node_loc_y_solved;
6464
std::vector<double> node_loc_x_legalized;
6565
std::vector<double> node_loc_y_legalized;
66-
};
66+
};

vpr/src/place/analytical_placement/PartialPlacement.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ PartialPlacement::PartialPlacement(const AtomNetlist& netlist,
2020

2121
const AtomContext& atom_ctx = g_vpr_ctx.atom();
2222

23+
std::vector<std::unordered_set<t_pack_molecule*>> interesting_nets;
24+
interesting_nets.reserve(atom_netlist.nets().size());
25+
2326
// Collect the unique moveable and fixed molecules from the netlist.
2427
std::unordered_set<t_pack_molecule*> moveable_mols;
2528
std::unordered_set<t_pack_molecule*> fixed_mols;
2629
for (const AtomNetId& net_id : atom_netlist.nets()) {
2730
if (net_is_ignored_for_placement(net_id))
2831
continue;
32+
std::unordered_set<t_pack_molecule*> net_mols;
2933
for (const AtomPinId& pin_id : atom_netlist.net_pins(net_id)) {
3034
AtomBlockId blk_id = atom_netlist.pin_block(pin_id);
3135
// Get the molecule for this block.
@@ -35,7 +39,9 @@ PartialPlacement::PartialPlacement(const AtomNetlist& netlist,
3539
fixed_mols.insert(mol);
3640
else
3741
moveable_mols.insert(mol);
42+
net_mols.insert(mol);
3843
}
44+
interesting_nets.push_back(std::move(net_mols));
3945
}
4046

4147
// Ensure that no fixed molecules are moveable (safety check)
@@ -74,6 +80,35 @@ PartialPlacement::PartialPlacement(const AtomNetlist& netlist,
7480
node_loc_y[fixed_node_id] = fixed_blocks_y[fixed_blk_id];
7581
}
7682

83+
// Create the AP Netlist
84+
// FIXME: We should experiment with having duplicate pins! It is possible
85+
// that a block would have multiple pin inputs connected to the same
86+
// net. This should give that block more power...
87+
for (const std::unordered_set<t_pack_molecule*> &mols : interesting_nets) {
88+
// If the number of molecules in a net is 1 or less, we do not care.
89+
// This can happen when a LUT + FF are packed together and now the
90+
// net connects to itself.
91+
if (mols.size() <= 1)
92+
continue;
93+
// If the molecules connected by a net are all fixed, then we do not care
94+
// about this net.
95+
bool is_all_fixed = true;
96+
for (t_pack_molecule *mol : mols) {
97+
if (moveable_mols.find(mol) != moveable_mols.end()) {
98+
is_all_fixed = false;
99+
break;
100+
}
101+
}
102+
if (is_all_fixed)
103+
continue;
104+
// Insert these nodes into the AP Netlist.
105+
std::vector<size_t> net_nodes;
106+
net_nodes.reserve(mols.size());
107+
for (t_pack_molecule *mol : mols) {
108+
net_nodes.push_back(mol_to_node_id[mol]);
109+
}
110+
ap_netlist.emplace_back(std::move(net_nodes));
111+
}
77112
}
78113

79114
double PartialPlacement::get_HPWL() {
@@ -83,6 +118,11 @@ double PartialPlacement::get_HPWL() {
83118
// FIXME: Confirm if this should be here.
84119
if (net_is_ignored_for_placement(atom_netlist, net_id))
85120
continue;
121+
// FIXME: This is not necessarily correct for solving HPWL. The x and y
122+
// positions should be clamped to the grid coordinates.
123+
// However, this may make it harder to debug since we lose the
124+
// granualirty of the HPWL. Perhaps create an option to get a
125+
// clamped / unclamped version.
86126
double min_x = std::numeric_limits<double>::max();
87127
double max_x = std::numeric_limits<double>::lowest();
88128
double min_y = std::numeric_limits<double>::max();
@@ -226,4 +266,4 @@ void PartialPlacement::unicode_art(){
226266
}
227267
VTR_LOG("unicode_art end\n");
228268
fflush(stderr);
229-
}
269+
}

vpr/src/place/analytical_placement/PartialPlacement.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,21 @@ class PartialPlacement {
6464
VTR_LOG("Number of moveable nodes: %zu\n", num_moveable_nodes);
6565
VTR_LOG("Number of fixed nodes: %zu\n", num_nodes - num_moveable_nodes);
6666
VTR_LOG("Number of total nodes: %zu\n", num_nodes);
67+
VTR_LOG("Number of AP nets: %zu\n", ap_netlist.size());
6768
}
6869

6970
const AtomNetlist& atom_netlist;
71+
// Analytical Placement-specific Netlist
72+
// This is a netlist containing the nets that AP cares about, using the node
73+
// type that AP uses. It also removes duplicate nodes from the net. This
74+
// will not include all nets.
75+
// Nets which are ignored:
76+
// - nets "ignored for placement", see net_is_ignored_for_placement
77+
// - nets that only connect to 1 (or less) nodes
78+
// - nets that do not contain any moveable nodes
79+
// TODO: Eventually we should use the actual Netlist class to contain this
80+
// information. vector of vectors is inefficient and may be dangerous.
81+
std::vector<std::vector<size_t>> ap_netlist;
7082
std::map<t_pack_molecule*, size_t> mol_to_node_id;
7183
std::vector<t_pack_molecule*> node_id_to_mol;
7284
std::vector<double> node_loc_x;

vpr/src/place/analytical_placement/analytical_placement_flow.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,25 +80,25 @@ void run_analytical_placement_flow() {
8080

8181
// Set up the partial placement object
8282
PartialPlacement p_placement = PartialPlacement(atom_netlist, fixed_blocks, fixed_blocks_x, fixed_blocks_y);
83+
p_placement.print_stats();
8384
// Solve the QP problem
8485
std::unique_ptr<AnalyticalSolver> solver = make_analytical_solver(e_analytical_solver::B2B);
8586
// This for loop always starts at iteration 0
86-
for (unsigned iteration = 0; iteration < 300; iteration++) {
87+
for (unsigned iteration = 0; iteration < 100; iteration++) {
8788
VTR_LOG("iteration: %ld\n", iteration);
8889
solver->solve(iteration, p_placement);
8990
VTR_ASSERT(p_placement.is_valid_partial_placement() && "placement not valid after solve!");
90-
p_placement.print_stats();
9191
double post_solve_hpwl = p_placement.get_HPWL();
9292
VTR_LOG("HPWL: %f\n", post_solve_hpwl);
9393
// Partial legalization using cut spreading algorithm
9494
FlowBasedLegalizer().legalize(p_placement);
9595
VTR_ASSERT(p_placement.is_valid_partial_placement() && "placement not valid after legalize!");
9696
double post_legalize_hpwl = p_placement.get_HPWL();
9797
VTR_LOG("Post-Legalized HPWL: %f\n", post_legalize_hpwl);
98-
// if(std::abs(post_solve_hpwl - post_legalize_hpwl) < 20){
99-
// VTR_LOG("ended because of convergence\n");
100-
// break;
101-
// }
98+
if(std::abs(post_solve_hpwl - post_legalize_hpwl) < 20){
99+
VTR_LOG("ended because of convergence\n");
100+
break;
101+
}
102102
// p_placement.unicode_art();
103103
}
104104
FullLegalizer().legalize(p_placement);

0 commit comments

Comments
 (0)