| /*===================================================================*/ |
| // |
| // place_partition.c |
| // |
| // Aaron P. Hurst, 2003-2007 |
| // ahurst@eecs.berkeley.edu |
| // |
| /*===================================================================*/ |
| |
| #include <stdlib.h> |
| #include <math.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <limits.h> |
| #include <assert.h> |
| //#include <sys/stat.h> |
| //#include <unistd.h> |
| |
| #include "place_base.h" |
| #include "place_gordian.h" |
| |
| #if !defined(NO_HMETIS) |
| #include "libhmetis.h" |
| |
| ABC_NAMESPACE_IMPL_START |
| |
| #endif |
| |
| // -------------------------------------------------------------------- |
| // Global variables |
| // |
| // -------------------------------------------------------------------- |
| |
| Partition *g_place_rootPartition = NULL; |
| ConcreteNet **allNetsR2 = NULL, |
| **allNetsL2 = NULL, |
| **allNetsB2 = NULL, |
| **allNetsT2 = NULL; |
| |
| |
| // -------------------------------------------------------------------- |
| // Function prototypes and local data structures |
| // |
| // -------------------------------------------------------------------- |
| |
| typedef struct FM_cell { |
| int loc; |
| int gain; |
| ConcreteCell *cell; |
| struct FM_cell *next, *prev; |
| bool locked; |
| } FM_cell; |
| |
| void FM_updateGains(ConcreteNet *net, int partition, int inc, |
| FM_cell target [], FM_cell *bin [], |
| int count_1 [], int count_2 []); |
| |
| |
| // -------------------------------------------------------------------- |
| // initPartitioning() |
| // |
| /// \brief Initializes data structures necessary for partitioning. |
| // |
| /// Creates a valid g_place_rootPartition. |
| /// |
| // -------------------------------------------------------------------- |
| void initPartitioning() { |
| int i; |
| float area; |
| |
| // create root partition |
| g_place_numPartitions = 1; |
| if (g_place_rootPartition) free(g_place_rootPartition); |
| g_place_rootPartition = malloc(sizeof(Partition)); |
| g_place_rootPartition->m_level = 0; |
| g_place_rootPartition->m_area = 0; |
| g_place_rootPartition->m_bounds = g_place_coreBounds; |
| g_place_rootPartition->m_vertical = false; |
| g_place_rootPartition->m_done = false; |
| g_place_rootPartition->m_leaf = true; |
| |
| // add all of the cells to this partition |
| g_place_rootPartition->m_members = malloc(sizeof(ConcreteCell*)*g_place_numCells); |
| g_place_rootPartition->m_numMembers = 0; |
| for (i=0; i<g_place_numCells; i++) |
| if (g_place_concreteCells[i]) { |
| if (!g_place_concreteCells[i]->m_fixed) { |
| area = getCellArea(g_place_concreteCells[i]); |
| g_place_rootPartition->m_members[g_place_rootPartition->m_numMembers++] = |
| g_place_concreteCells[i]; |
| g_place_rootPartition->m_area += area; |
| } |
| } |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // presortNets() |
| // |
| /// \brief Sorts nets by corner positions. |
| // |
| /// Allocates allNetsX2 structures. |
| /// |
| // -------------------------------------------------------------------- |
| void presortNets() { |
| allNetsL2 = (ConcreteNet**)realloc(allNetsL2, sizeof(ConcreteNet*)*g_place_numNets); |
| allNetsR2 = (ConcreteNet**)realloc(allNetsR2, sizeof(ConcreteNet*)*g_place_numNets); |
| allNetsB2 = (ConcreteNet**)realloc(allNetsB2, sizeof(ConcreteNet*)*g_place_numNets); |
| allNetsT2 = (ConcreteNet**)realloc(allNetsT2, sizeof(ConcreteNet*)*g_place_numNets); |
| memcpy(allNetsL2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets); |
| memcpy(allNetsR2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets); |
| memcpy(allNetsB2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets); |
| memcpy(allNetsT2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets); |
| qsort(allNetsL2, g_place_numNets, sizeof(ConcreteNet*), netSortByL); |
| qsort(allNetsR2, g_place_numNets, sizeof(ConcreteNet*), netSortByR); |
| qsort(allNetsB2, g_place_numNets, sizeof(ConcreteNet*), netSortByB); |
| qsort(allNetsT2, g_place_numNets, sizeof(ConcreteNet*), netSortByT); |
| } |
| |
| // -------------------------------------------------------------------- |
| // refinePartitions() |
| // |
| /// \brief Splits large leaf partitions. |
| // |
| // -------------------------------------------------------------------- |
| bool refinePartitions() { |
| |
| return refinePartition(g_place_rootPartition); |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // reallocPartitions() |
| // |
| /// \brief Reallocates the partitions based on placement information. |
| // |
| // -------------------------------------------------------------------- |
| void reallocPartitions() { |
| |
| reallocPartition(g_place_rootPartition); |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // refinePartition() |
| // |
| /// \brief Splits any large leaves within a partition. |
| // |
| // -------------------------------------------------------------------- |
| bool refinePartition(Partition *p) { |
| bool degenerate = false; |
| int nonzeroCount = 0; |
| int i; |
| |
| assert(p); |
| |
| // is this partition completed? |
| if (p->m_done) return true; |
| |
| // is this partition a non-leaf node? |
| if (!p->m_leaf) { |
| p->m_done = refinePartition(p->m_sub1); |
| p->m_done &= refinePartition(p->m_sub2); |
| return p->m_done; |
| } |
| |
| // leaf... |
| // create two new subpartitions |
| g_place_numPartitions++; |
| p->m_sub1 = malloc(sizeof(Partition)); |
| p->m_sub1->m_level = p->m_level+1; |
| p->m_sub1->m_leaf = true; |
| p->m_sub1->m_done = false; |
| p->m_sub1->m_area = 0; |
| p->m_sub1->m_vertical = !p->m_vertical; |
| p->m_sub1->m_numMembers = 0; |
| p->m_sub1->m_members = NULL; |
| p->m_sub2 = malloc(sizeof(Partition)); |
| p->m_sub2->m_level = p->m_level+1; |
| p->m_sub2->m_leaf = true; |
| p->m_sub2->m_done = false; |
| p->m_sub2->m_area = 0; |
| p->m_sub2->m_vertical = !p->m_vertical; |
| p->m_sub2->m_numMembers = 0; |
| p->m_sub2->m_members = NULL; |
| p->m_leaf = false; |
| |
| // --- INITIAL PARTITION |
| |
| if (PARTITION_AREA_ONLY) |
| partitionEqualArea(p); |
| else |
| partitionScanlineMincut(p); |
| |
| resizePartition(p); |
| |
| // --- PARTITION IMPROVEMENT |
| |
| if (p->m_level < REPARTITION_LEVEL_DEPTH) { |
| if (REPARTITION_FM) |
| repartitionFM(p); |
| else if (REPARTITION_HMETIS) |
| repartitionHMetis(p); |
| } |
| |
| resizePartition(p); |
| |
| // fix imbalances due to zero-area cells |
| for(i=0; i<p->m_sub1->m_numMembers; i++) |
| if (p->m_sub1->m_members[i]) |
| if (getCellArea(p->m_sub1->m_members[i]) > 0) { |
| nonzeroCount++; |
| } |
| |
| // is this leaf now done? |
| if (nonzeroCount <= LARGEST_FINAL_SIZE) |
| p->m_sub1->m_done = true; |
| if (nonzeroCount == 0) |
| degenerate = true; |
| |
| nonzeroCount = 0; |
| for(i=0; i<p->m_sub2->m_numMembers; i++) |
| if (p->m_sub2->m_members[i]) |
| if (getCellArea(p->m_sub2->m_members[i]) > 0) { |
| nonzeroCount++; |
| } |
| |
| // is this leaf now done? |
| if (nonzeroCount <= LARGEST_FINAL_SIZE) |
| p->m_sub2->m_done = true; |
| if (nonzeroCount == 0) |
| degenerate = true; |
| |
| // have we found a degenerate partitioning? |
| if (degenerate) { |
| printf("QPART-35 : WARNING: degenerate partition generated\n"); |
| partitionEqualArea(p); |
| resizePartition(p); |
| p->m_sub1->m_done = true; |
| p->m_sub2->m_done = true; |
| } |
| |
| // is this parent now finished? |
| if (p->m_sub1->m_done && p->m_sub2->m_done) p->m_done = true; |
| |
| return p->m_done; |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // repartitionHMetis() |
| // |
| /// \brief Repartitions the two subpartitions using the hMetis min-cut library. |
| /// |
| /// The number of cut nets between the two partitions will be minimized. |
| // |
| // -------------------------------------------------------------------- |
| void repartitionHMetis(Partition *parent) { |
| #if defined(NO_HMETIS) |
| printf("QPAR_02 : \t\tERROR: hMetis not available. Ignoring.\n"); |
| #else |
| |
| int n,c,t, i; |
| float area; |
| int *edgeConnections = NULL; |
| int *partitionAssignment = (int *)calloc(g_place_numCells, sizeof(int)); |
| int *vertexWeights = (int *)calloc(g_place_numCells, sizeof(int)); |
| int *edgeDegree = (int *)malloc(sizeof(int)*(g_place_numNets+1)); |
| int numConnections = 0; |
| int numEdges = 0; |
| float initial_cut; |
| int targets = 0; |
| ConcreteCell *cell = NULL; |
| int options[9]; |
| int afterCuts = 0; |
| |
| assert(parent); |
| assert(parent->m_sub1); |
| assert(parent->m_sub2); |
| |
| printf("QPAR-02 : \t\trepartitioning with hMetis\n"); |
| |
| // count edges |
| edgeDegree[0] = 0; |
| for(n=0; n<g_place_numNets; n++) if (g_place_concreteNets[n]) |
| if (g_place_concreteNets[n]->m_numTerms > 1) { |
| numConnections += g_place_concreteNets[n]->m_numTerms; |
| edgeDegree[++numEdges] = numConnections; |
| } |
| |
| if (parent->m_vertical) { |
| // vertical |
| initial_cut = parent->m_sub2->m_bounds.x; |
| |
| // initialize all cells |
| for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) { |
| if (g_place_concreteCells[c]->m_x < initial_cut) |
| partitionAssignment[c] = 0; |
| else |
| partitionAssignment[c] = 1; |
| } |
| |
| // initialize cells in partition 1 |
| for(t=0; t<parent->m_sub1->m_numMembers; t++) if (parent->m_sub1->m_members[t]) { |
| cell = parent->m_sub1->m_members[t]; |
| vertexWeights[cell->m_id] = getCellArea(cell); |
| // pay attention to cells that are close to the cut |
| if (abs(cell->m_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) { |
| targets++; |
| partitionAssignment[cell->m_id] = -1; |
| } |
| } |
| |
| // initialize cells in partition 2 |
| for(t=0; t<parent->m_sub2->m_numMembers; t++) if (parent->m_sub2->m_members[t]) { |
| cell = parent->m_sub2->m_members[t]; |
| vertexWeights[cell->m_id] = getCellArea(cell); |
| // pay attention to cells that are close to the cut |
| if (abs(cell->m_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) { |
| targets++; |
| partitionAssignment[cell->m_id] = -1; |
| } |
| } |
| |
| } else { |
| // horizontal |
| initial_cut = parent->m_sub2->m_bounds.y; |
| |
| // initialize all cells |
| for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) { |
| if (g_place_concreteCells[c]->m_y < initial_cut) |
| partitionAssignment[c] = 0; |
| else |
| partitionAssignment[c] = 1; |
| } |
| |
| // initialize cells in partition 1 |
| for(t=0; t<parent->m_sub1->m_numMembers; t++) if (parent->m_sub1->m_members[t]) { |
| cell = parent->m_sub1->m_members[t]; |
| vertexWeights[cell->m_id] = getCellArea(cell); |
| // pay attention to cells that are close to the cut |
| if (abs(cell->m_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) { |
| targets++; |
| partitionAssignment[cell->m_id] = -1; |
| } |
| } |
| |
| // initialize cells in partition 2 |
| for(t=0; t<parent->m_sub2->m_numMembers; t++) if (parent->m_sub2->m_members[t]) { |
| cell = parent->m_sub2->m_members[t]; |
| vertexWeights[cell->m_id] = getCellArea(cell); |
| // pay attention to cells that are close to the cut |
| if (abs(cell->m_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) { |
| targets++; |
| partitionAssignment[cell->m_id] = -1; |
| } |
| } |
| } |
| |
| options[0] = 1; // any non-default values? |
| options[1] = 3; // num bisections |
| options[2] = 1; // grouping scheme |
| options[3] = 1; // refinement scheme |
| options[4] = 1; // cycle refinement scheme |
| options[5] = 0; // reconstruction scheme |
| options[6] = 0; // fixed assignments? |
| options[7] = 12261980; // random seed |
| options[8] = 0; // debugging level |
| |
| edgeConnections = (int *)malloc(sizeof(int)*numConnections); |
| |
| i = 0; |
| for(n=0; n<g_place_numNets; n++) if (g_place_concreteNets[n]) { |
| if (g_place_concreteNets[n]->m_numTerms > 1) |
| for(t=0; t<g_place_concreteNets[n]->m_numTerms; t++) |
| edgeConnections[i++] = g_place_concreteNets[n]->m_terms[t]->m_id; |
| } |
| |
| HMETIS_PartRecursive(g_place_numCells, numEdges, vertexWeights, |
| edgeDegree, edgeConnections, NULL, |
| 2, (int)(100*MAX_PARTITION_NONSYMMETRY), |
| options, partitionAssignment, &afterCuts); |
| |
| /* |
| printf("HMET-20 : \t\t\tbalance before %d / %d ... ", parent->m_sub1->m_numMembers, |
| parent->m_sub2->m_numMembers); |
| */ |
| |
| // reassign members to subpartitions |
| parent->m_sub1->m_numMembers = 0; |
| parent->m_sub1->m_area = 0; |
| parent->m_sub2->m_numMembers = 0; |
| parent->m_sub2->m_area = 0; |
| parent->m_sub1->m_members = (ConcreteCell**)realloc(parent->m_sub1->m_members, |
| sizeof(ConcreteCell*)*parent->m_numMembers); |
| parent->m_sub2->m_members = (ConcreteCell**)realloc(parent->m_sub2->m_members, |
| sizeof(ConcreteCell*)*parent->m_numMembers); |
| |
| for(t=0; t<parent->m_numMembers; t++) if (parent->m_members[t]) { |
| cell = parent->m_members[t]; |
| area = getCellArea(cell); |
| if (partitionAssignment[cell->m_id] == 0) { |
| parent->m_sub1->m_members[parent->m_sub1->m_numMembers++] = cell; |
| parent->m_sub1->m_area += area; |
| } |
| else { |
| parent->m_sub2->m_members[parent->m_sub2->m_numMembers++] = cell; |
| parent->m_sub2->m_area += area; |
| } |
| } |
| /* |
| printf("after %d / %d\n", parent->m_sub1->m_numMembers, |
| parent->m_sub2->m_numMembers); |
| */ |
| |
| // cout << "HMET-21 : \t\t\tloc: " << initial_cut << " targetting: " << targets*100/parent->m_members.length() << "%" << endl; |
| // cout << "HMET-22 : \t\t\tstarting cuts= " << beforeCuts << " final cuts= " << afterCuts << endl; |
| |
| free(edgeConnections); |
| free(vertexWeights); |
| free(edgeDegree); |
| free(partitionAssignment); |
| #endif |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // repartitionFM() |
| // |
| /// \brief Fiduccia-Matheyses mincut partitioning algorithm. |
| // |
| /// UNIMPLEMENTED (well, un-C-ified) |
| // |
| // -------------------------------------------------------------------- |
| void repartitionFM(Partition *parent) { |
| #if 0 |
| assert(!parent->leaf && parent->m_sub1->leaf && parent->m_sub2->leaf); |
| |
| // count of each net's number of cells in each bipartition |
| int count_1[m_design->nets.length()]; |
| memset(count_1, 0, sizeof(int)*m_design->nets.length()); |
| int count_2[m_design->nets.length()]; |
| memset(count_2, 0, sizeof(int)*m_design->nets.length()); |
| |
| FM_cell target[m_design->cells.length()]; |
| memset(target, 0, sizeof(FM_cell)*m_design->cells.length()); |
| FM_cell *bin[FM_MAX_BIN+1]; |
| FM_cell *locked = 0; |
| memset(bin, 0, sizeof(FM_cell *)*(FM_MAX_BIN+1)); |
| |
| int max_gain = 0; |
| int before_cuts = 0, current_cuts = 0; |
| double initial_cut; |
| int targets = 0; |
| long cell_id; |
| double halfArea = parent->m_area / 2.0; |
| double areaFlexibility = parent->m_area * MAX_PARTITION_NONSYMMETRY; |
| ConcreteNet *net; |
| |
| // INITIALIZATION |
| // select cells to partition |
| |
| if (parent->vertical) { |
| // vertical |
| |
| initial_cut = parent->m_sub2->m_bounds.x; |
| |
| // initialize all cells |
| for(h::list<ConcreteCell *>::iterator it = rootPartition->m_members.begin(); !it; it++) { |
| cell_id = (*it)->getID(); |
| if ((*it)->temp_x < initial_cut) |
| target[cell_id].loc = -1; |
| else |
| target[cell_id].loc = -2; |
| target[cell_id].cell = *it; |
| target[cell_id].gain = 0; |
| } |
| |
| // initialize cells in partition 1 |
| for(h::list<ConcreteCell *>::iterator it = parent->m_sub1->m_members.begin(); !it; it++) { |
| cell_id = (*it)->getID(); |
| // pay attention to cells that are close to the cut |
| if (abs((*it)->temp_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) { |
| targets++; |
| target[cell_id].loc = 1; |
| } |
| } |
| |
| // initialize cells in partition 2 |
| for(h::list<ConcreteCell *>::iterator it = parent->m_sub2->m_members.begin(); !it; it++) { |
| cell_id = (*it)->getID(); |
| // pay attention to cells that are close to the cut |
| if (abs((*it)->temp_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) { |
| targets++; |
| target[cell_id].loc = 2; |
| } |
| } |
| |
| // count the number of cells on each side of the partition for every net |
| for(h::hash_map<ConcreteNet *>::iterator n_it = m_design->nets.begin(); !n_it; n_it++) { |
| for(ConcretePinList::iterator p_it = (net = *n_it)->getPins().begin(); !p_it; p_it++) |
| if (abs(target[(*p_it)->getCell()->getID()].loc) == 1) |
| count_1[net->getID()]++; |
| else if (abs(target[(*p_it)->getCell()->getID()].loc) == 2) |
| count_2[net->getID()]++; |
| else if ((*p_it)->getCell()->temp_x < initial_cut) |
| count_1[net->getID()]++; |
| else |
| count_2[net->getID()]++; |
| if (count_1[net->getID()] > 0 && count_2[net->getID()] > 0) before_cuts++; |
| } |
| |
| } else { |
| // horizontal |
| |
| initial_cut = parent->m_sub2->m_bounds.y; |
| |
| // initialize all cells |
| for(h::list<ConcreteCell *>::iterator it = rootPartition->m_members.begin(); !it; it++) { |
| cell_id = (*it)->getID(); |
| if ((*it)->temp_y < initial_cut) |
| target[cell_id].loc = -1; |
| else |
| target[cell_id].loc = -2; |
| target[cell_id].cell = *it; |
| target[cell_id].gain = 0; |
| } |
| |
| // initialize cells in partition 1 |
| for(h::list<ConcreteCell *>::iterator it = parent->m_sub1->m_members.begin(); !it; it++) { |
| cell_id = (*it)->getID(); |
| // pay attention to cells that are close to the cut |
| if (abs((*it)->temp_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) { |
| targets++; |
| target[cell_id].loc = 1; |
| } |
| } |
| |
| // initialize cells in partition 2 |
| for(h::list<ConcreteCell *>::iterator it = parent->m_sub2->m_members.begin(); !it; it++) { |
| cell_id = (*it)->getID(); |
| // pay attention to cells that are close to the cut |
| if (abs((*it)->temp_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) { |
| targets++; |
| target[cell_id].loc = 2; |
| } |
| } |
| |
| // count the number of cells on each side of the partition for every net |
| for(h::hash_map<ConcreteNet *>::iterator n_it = m_design->nets.begin(); !n_it; n_it++) { |
| for(ConcretePinList::iterator p_it = (net = *n_it)->getPins().begin(); !p_it; p_it++) |
| if (abs(target[(*p_it)->getCell()->getID()].loc) == 1) |
| count_1[net->getID()]++; |
| else if (abs(target[(*p_it)->getCell()->getID()].loc) == 2) |
| count_2[net->getID()]++; |
| else if ((*p_it)->getCell()->temp_y < initial_cut) |
| count_1[net->getID()]++; |
| else |
| count_2[net->getID()]++; |
| if (count_1[net->getID()] > 0 && count_2[net->getID()] > 0) before_cuts++; |
| } |
| } |
| |
| // INITIAL GAIN CALCULATION |
| for(long id=0; id < m_design->cells.length(); id++) |
| if (target[id].loc > 0) { |
| assert(target[id].cell != 0); |
| assert(target[id].gain == 0); |
| |
| // examine counts for the net on each pin |
| for(ConcretePinMap::iterator p_it = target[id].cell->getPins().begin(); !p_it; p_it++) |
| if ((*p_it)->isAttached()) { |
| int n_id = (*p_it)->getNet()->getID(); |
| if (target[id].loc == 1 && count_1[n_id] == 1) target[id].gain++; |
| if (target[id].loc == 1 && count_2[n_id] == 0) target[id].gain--; |
| if (target[id].loc == 2 && count_1[n_id] == 0) target[id].gain--; |
| if (target[id].loc == 2 && count_2[n_id] == 1) target[id].gain++; |
| } |
| |
| assert(target[id].cell->getPins().length() >= abs(target[id].gain)); |
| |
| // add it to a bin |
| int bin_num = min(max(0, target[id].gain),FM_MAX_BIN); |
| max_gain = max(max_gain, bin_num); |
| |
| assert(bin_num >= 0 && bin_num <= FM_MAX_BIN); |
| target[id].next = bin[bin_num]; |
| target[id].prev = 0; |
| if (bin[bin_num] != 0) |
| bin[bin_num]->prev = &target[id]; |
| bin[bin_num] = &target[id]; |
| } |
| |
| // OUTER F-M LOOP |
| current_cuts = before_cuts; |
| int num_moves = 1; |
| int pass = 0; |
| while(num_moves > 0 && pass < FM_MAX_PASSES) { |
| pass++; |
| num_moves = 0; |
| |
| // check_list(bin, locked, targets); // DEBUG |
| |
| // move all locked cells back |
| int moved_back = 0; |
| while(locked != 0) { |
| FM_cell *current = locked; |
| current->locked = false; |
| |
| int bin_num = min(max(0, current->gain),FM_MAX_BIN); |
| max_gain = max(max_gain, bin_num); |
| |
| locked = current->next; |
| if (locked != 0) |
| locked->prev = 0; |
| |
| if (bin[bin_num] != 0) |
| bin[bin_num]->prev = current; |
| current->next = bin[bin_num]; |
| bin[bin_num] = current; |
| |
| moved_back++; |
| } |
| // cout << "\tmoved back: " << moved_back << endl; |
| // check_list(bin, locked, targets); // DEBUG |
| |
| max_gain = FM_MAX_BIN; |
| while(bin[max_gain] == 0 && max_gain > 0) max_gain--; |
| |
| // INNER F-M LOOP (single pass) |
| while(1) { |
| |
| int bin_num = FM_MAX_BIN; |
| FM_cell *current = bin[bin_num]; |
| |
| // look for next cell to move |
| while (bin_num > 0 && (current == 0 || |
| (current->loc==1 && current->cell->getArea()+parent->m_sub2->m_area > halfArea+areaFlexibility) || |
| (current->loc==2 && current->cell->getArea()+parent->m_sub1->m_area > halfArea+areaFlexibility))) { |
| |
| if (current == 0) current = bin[--bin_num]; else current = current->next; |
| } |
| if (bin_num == 0) |
| break; |
| |
| num_moves++; |
| current->locked = true; |
| // cout << "moving cell " << current->cell->getID() << " gain=" << current->gain << " pins= " << current->cell->getPins().length() << " from " << current->loc; |
| |
| // change partition marking and areas |
| if (current->loc == 1) { |
| current->loc = 2; |
| parent->m_sub1->m_area -= current->cell->getArea(); |
| parent->m_sub2->m_area += current->cell->getArea(); |
| |
| // update partition counts on all nets attached to this cell |
| for(ConcretePinMap::iterator p_it = current->cell->getPins().begin(); |
| !p_it; p_it++) { |
| |
| if (!(*p_it)->isAttached()) // ignore unattached pins |
| continue; |
| net = (*p_it)->getNet(); |
| |
| count_1[net->getID()]--; |
| count_2[net->getID()]++; |
| |
| // cout << "\tnet " << net->getID() << " was " << count_1[net->getID()]+1 << "/" << count_2[net->getID()]-1 << " now " << count_1[net->getID()] << "/" << count_2[net->getID()] << endl; |
| |
| // if net becomes critical, update gains on attached cells and resort bins |
| if (count_1[net->getID()] == 0) { current_cuts--; FM_updateGains(net, 2, -1, target, bin, count_1, count_2); } |
| if (count_2[net->getID()] == 1) { current_cuts++; FM_updateGains(net, 1, -1, target, bin, count_1, count_2); } |
| |
| // check_list(bin, locked, targets); // DEBUG |
| } |
| |
| } else { |
| current->loc = 1; |
| parent->m_sub2->m_area -= current->cell->getArea(); |
| parent->m_sub1->m_area += current->cell->getArea(); |
| |
| // update gains on all nets attached to this cell |
| for(ConcretePinMap::iterator p_it = current->cell->getPins().begin(); |
| !p_it; p_it++) { |
| |
| if (!(*p_it)->isAttached()) // ignore unattached pins |
| continue; |
| net = (*p_it)->getNet(); |
| count_2[net->getID()]--; |
| count_1[net->getID()]++; |
| |
| // cout << "\tnet " << net->getID() << " was " << count_1[net->getID()]-1 << "/" << count_2[net->getID()]+1 << " now " << count_1[net->getID()] << "/" << count_2[net->getID()] << endl; |
| |
| if (count_2[net->getID()] == 0) { current_cuts--; FM_updateGains(net, 2, -1, target, bin, count_1, count_2); } |
| if (count_1[net->getID()] == 1) { current_cuts++; FM_updateGains(net, 1, -1, target, bin, count_1, count_2); } |
| |
| // check_list(bin, locked, targets); // DEBUG |
| } |
| } |
| |
| //cout << " cuts=" << current_cuts << endl; |
| |
| // move current to locked |
| |
| /* |
| cout << "b=" << bin[bin_num] << " "; |
| cout << current->prev << "-> "; |
| if (current->prev == 0) |
| cout << "X"; |
| else cout << current->prev->next; |
| cout << "=" << current << "="; |
| if (current->next == 0) |
| cout << "X"; |
| else |
| cout << current->next->prev; |
| cout << " ->" << current->next << endl; |
| */ |
| |
| if (bin[bin_num] == current) |
| bin[bin_num] = current->next; |
| if (current->prev != 0) |
| current->prev->next = current->next; |
| if (current->next != 0) |
| current->next->prev = current->prev; |
| |
| /* |
| cout << "b=" << bin[bin_num] << " "; |
| cout << current->prev << "-> "; |
| if (current->prev == 0) |
| cout << "X"; |
| else cout << current->prev->next; |
| cout << "=" << current << "="; |
| if (current->next == 0) |
| cout << "X"; |
| else |
| cout << current->next->prev; |
| cout << " ->" << current->next << endl; |
| */ |
| |
| current->prev = 0; |
| current->next = locked; |
| if (locked != 0) |
| locked->prev = current; |
| locked = current; |
| |
| // check_list(bin, locked, targets); // DEBUG |
| |
| // update max_gain |
| max_gain = FM_MAX_BIN; |
| while(bin[max_gain] == 0 && max_gain > 0) max_gain--; |
| } |
| |
| // cout << "\tcurrent cuts= " << current_cuts << " moves= " << num_moves << endl; |
| } |
| |
| // reassign members to subpartitions |
| cout << "FIDM-20 : \tbalance before " << parent->m_sub1->m_members.length() << "/" |
| << parent->m_sub2->m_members.length() << " "; |
| parent->m_sub1->m_members.clear(); |
| parent->m_sub1->m_area = 0; |
| parent->m_sub2->m_members.clear(); |
| parent->m_sub2->m_area = 0; |
| for(h::list<ConcreteCell *>::iterator it = parent->m_members.begin(); !it; it++) { |
| if (target[(*it)->getID()].loc == 1 || target[(*it)->getID()].loc == -1) { |
| parent->m_sub1->m_members.push_back(*it); |
| parent->m_sub1->m_area += (*it)->getArea(); |
| } |
| else { |
| parent->m_sub2->m_members.push_back(*it); |
| parent->m_sub2->m_area += (*it)->getArea(); |
| } |
| } |
| cout << " after " << parent->m_sub1->m_members.length() << "/" |
| << parent->m_sub2->m_members.length() << endl; |
| |
| |
| cout << "FIDM-21 : \tloc: " << initial_cut << " targetting: " << targets*100/parent->m_members.length() << "%" << endl; |
| cout << "FIDM-22 : \tstarting cuts= " << before_cuts << " final cuts= " << current_cuts << endl; |
| #endif |
| } |
| |
| // ----- FM_updateGains() |
| // moves a cell between bins |
| #if 0 |
| void FM_updateGains(ConcreteNet *net, int partition, int inc, |
| FM_cell target [], FM_cell *bin [], |
| int count_1 [], int count_2 []) { |
| |
| for(ConcretePinList::iterator it = net->getPins().begin(); !it; it++) { |
| FM_cell *current = &(target[(*it)->getCell()->getID()]); |
| assert(current->cell != 0); |
| |
| int old_gain = current->gain; |
| current->gain = 0; |
| |
| // examine counts for the net on each pin |
| for(ConcretePinMap::iterator p_it = current->cell->getPins().begin(); !p_it; p_it++) |
| if ((*p_it)->isAttached()) { |
| int n_id = (*p_it)->getNet()->getID(); |
| if (current->loc == 1 && count_1[n_id] == 1) current->gain++; |
| if (current->loc == 1 && count_2[n_id] == 0) current->gain--; |
| if (current->loc == 2 && count_1[n_id] == 0) current->gain--; |
| if (current->loc == 2 && count_2[n_id] == 1) current->gain++; |
| } |
| |
| if (!current->locked) { |
| // remove cell from existing bin |
| int bin_num = min(max(0, old_gain),FM_MAX_BIN); |
| if (bin[bin_num] == current) |
| bin[bin_num] = current->next; |
| if (current->prev != 0) |
| current->prev->next = current->next; |
| if (current->next != 0) |
| current->next->prev = current->prev; |
| // add cell to correct bin |
| bin_num = min(max(0, current->gain),FM_MAX_BIN); |
| current->prev = 0; |
| current->next = bin[bin_num]; |
| if (bin[bin_num] != 0) |
| bin[bin_num]->prev = current; |
| bin[bin_num] = current; |
| } |
| } |
| |
| } |
| #endif |
| |
| |
| // -------------------------------------------------------------------- |
| // partitionEqualArea() |
| // |
| /// \brief Splits a partition into two halves of equal area. |
| // |
| // -------------------------------------------------------------------- |
| void partitionEqualArea(Partition *parent) { |
| float halfArea, area; |
| int i=0; |
| |
| // which way to sort? |
| if (parent->m_vertical) |
| // sort by X position |
| qsort(parent->m_members, parent->m_numMembers, sizeof(ConcreteCell*), cellSortByX); |
| else |
| // sort by Y position |
| qsort(parent->m_members, parent->m_numMembers, sizeof(ConcreteCell*), cellSortByY); |
| |
| // split the list |
| halfArea = parent->m_area*0.5; |
| parent->m_sub1->m_area = 0.0; |
| parent->m_sub1->m_numMembers = 0; |
| parent->m_sub1->m_members = (ConcreteCell**)realloc(parent->m_sub1->m_members, |
| sizeof(ConcreteCell*)*parent->m_numMembers); |
| parent->m_sub2->m_area = 0.0; |
| parent->m_sub2->m_numMembers = 0; |
| parent->m_sub2->m_members = (ConcreteCell**)realloc(parent->m_sub2->m_members, |
| sizeof(ConcreteCell*)*parent->m_numMembers); |
| |
| for(; parent->m_sub1->m_area < halfArea; i++) |
| if (parent->m_members[i]) { |
| area = getCellArea(parent->m_members[i]); |
| parent->m_sub1->m_members[parent->m_sub1->m_numMembers++] = parent->m_members[i]; |
| parent->m_sub1->m_area += area; |
| } |
| for(; i<parent->m_numMembers; i++) |
| if (parent->m_members[i]) { |
| area = getCellArea(parent->m_members[i]); |
| parent->m_sub2->m_members[parent->m_sub2->m_numMembers++] = parent->m_members[i]; |
| parent->m_sub2->m_area += area; |
| } |
| |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // partitionScanlineMincut() |
| // |
| /// \brief Scans the cells within a partition from left to right and chooses the min-cut. |
| // |
| // -------------------------------------------------------------------- |
| void partitionScanlineMincut(Partition *parent) { |
| #if 0 |
| int current_cuts = 0; |
| int minimum_cuts = INT_MAX; |
| ConcreteCell *minimum_location = NULL; |
| double currentArea = 0, halfArea = parent->m_area * 0.5; |
| double areaFlexibility = parent->m_area * MAX_PARTITION_NONSYMMETRY; |
| double newLine, oldLine = -DBL_MAX; |
| |
| for(ConcreteNetList::iterator n_it = m_design->nets.begin(); !n_it; n_it++) |
| (*n_it)->m_mark = 0; |
| for(h::list<ConcreteCell *>::iterator i = parent->m_members.begin(); |
| !i.isDone(); i++) { |
| assert(*i); |
| for(ConcretePinMap::iterator j = (*i)->getPins().begin(); |
| !j.isDone(); j++) { |
| assert(*j); |
| if((*j)->isAttached()) { |
| (*j)->getNet()->m_mark = 1; |
| } |
| } |
| } |
| |
| if (parent->vertical) { |
| parent->m_members.sort(sortByX); |
| int all1 = 0, all2 = 0; |
| h::list<ConcreteCell *>::iterator local = parent->m_members.begin(); |
| for(; !local.isDone(); local++) { |
| currentArea += (*local)->getArea(); |
| if (currentArea < halfArea-areaFlexibility) |
| continue; |
| if (currentArea > halfArea+areaFlexibility) |
| break; |
| newLine = (*local)->temp_x; |
| while(all1 < g_place_numNets && allNetsL2[all1]->getBoundingBox().left() <= newLine) { |
| if(allNetsL2[all1]->m_mark) { |
| current_cuts++; |
| } |
| all1++; |
| } |
| while(all2 < g_place_numNets && allNetsR2[all2]->getBoundingBox().right() <= newLine) { |
| if(allNetsR2[all2]->m_mark) { |
| current_cuts--; |
| } |
| all2++; |
| } |
| if (current_cuts < minimum_cuts) { |
| minimum_cuts = current_cuts; |
| minimum_location = *local; |
| } |
| oldLine = newLine; |
| } |
| } |
| else { |
| parent->m_members.sort(sortByY); |
| int all1 = 0, all2 = 0; |
| h::list<ConcreteCell *>::iterator local = parent->m_members.begin(); |
| for(; !local.isDone(); local++) { |
| currentArea += (*local)->getArea(); |
| if (currentArea < halfArea-areaFlexibility) |
| continue; |
| if (currentArea > halfArea+areaFlexibility) |
| break; |
| newLine = (*local)->temp_y; |
| while(all1 < g_place_numNets && allNetsB2[all1]->getBoundingBox().top() <= newLine) { |
| if(allNetsB2[all1]->m_mark) { |
| current_cuts++; |
| } |
| all1++; |
| } |
| while(all2 < g_place_numNets && allNetsT2[all2]->getBoundingBox().bottom() <= newLine) { |
| if(allNetsT2[all2]->m_mark) { |
| current_cuts--; |
| } |
| all2++; |
| } |
| if (current_cuts < minimum_cuts) { |
| minimum_cuts = current_cuts; |
| minimum_location = *local; |
| } |
| oldLine = newLine; |
| } |
| } |
| if (minimum_location == NULL) { |
| return partitionEqualArea(parent); |
| } |
| h::list<ConcreteCell *>::iterator it = parent->m_members.begin(); |
| parent->m_sub1->m_members.clear(); |
| parent->m_sub1->m_area = 0; |
| for(; *it != minimum_location; it++) { |
| parent->m_sub1->m_members.push_front(*it); |
| parent->m_sub1->m_area += (*it)->getArea(); |
| } |
| parent->m_sub2->m_members.clear(); |
| parent->m_sub2->m_area = 0; |
| for(; !it; it++) { |
| parent->m_sub2->m_members.push_front(*it); |
| parent->m_sub2->m_area += (*it)->getArea(); |
| } |
| #endif |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // reallocPartition() |
| // |
| /// \brief Reallocates a partition and all of its children. |
| // |
| // -------------------------------------------------------------------- |
| void reallocPartition(Partition *p) { |
| |
| if (p->m_leaf) { |
| return; |
| } |
| |
| // --- INITIAL PARTITION |
| |
| if (PARTITION_AREA_ONLY) |
| partitionEqualArea(p); |
| else |
| partitionScanlineMincut(p); |
| |
| resizePartition(p); |
| |
| // --- PARTITION IMPROVEMENT |
| if (p->m_level < REPARTITION_LEVEL_DEPTH) { |
| if (REPARTITION_HMETIS) |
| repartitionHMetis(p); |
| |
| resizePartition(p); |
| } |
| |
| reallocPartition(p->m_sub1); |
| reallocPartition(p->m_sub2); |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // resizePartition() |
| // |
| /// \brief Recomputes the bounding boxes of the child partitions based on their relative areas. |
| // |
| // -------------------------------------------------------------------- |
| void resizePartition(Partition *p) { |
| // compute the new bounding box |
| p->m_sub1->m_bounds.x = p->m_bounds.x; |
| p->m_sub1->m_bounds.y = p->m_bounds.y; |
| if (p->m_vertical) { |
| p->m_sub1->m_bounds.w = p->m_bounds.w*(p->m_sub1->m_area/p->m_area); |
| p->m_sub1->m_bounds.h = p->m_bounds.h; |
| p->m_sub2->m_bounds.x = p->m_bounds.x + p->m_sub1->m_bounds.w; |
| p->m_sub2->m_bounds.w = p->m_bounds.w*(p->m_sub2->m_area/p->m_area); |
| p->m_sub2->m_bounds.y = p->m_bounds.y; |
| p->m_sub2->m_bounds.h = p->m_bounds.h; |
| } else { |
| p->m_sub1->m_bounds.h = p->m_bounds.h*(p->m_sub1->m_area/p->m_area); |
| p->m_sub1->m_bounds.w = p->m_bounds.w; |
| p->m_sub2->m_bounds.y = p->m_bounds.y + p->m_sub1->m_bounds.h; |
| p->m_sub2->m_bounds.h = p->m_bounds.h*(p->m_sub2->m_area/p->m_area); |
| p->m_sub2->m_bounds.x = p->m_bounds.x; |
| p->m_sub2->m_bounds.w = p->m_bounds.w; |
| } |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // incrementalSubpartition() |
| // |
| /// \brief Adds new cells to an existing partition. Partition sizes/locations are unchanged. |
| /// |
| /// The function recurses, adding new cells to appropriate subpartitions. |
| // |
| // -------------------------------------------------------------------- |
| void incrementalSubpartition(Partition *p, ConcreteCell *newCells [], const int numNewCells) { |
| int c; |
| ConcreteCell **newCells1 = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*numNewCells), |
| **newCells2 = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*numNewCells); |
| int numNewCells1 = 0, numNewCells2 = 0; |
| float cut_loc; |
| |
| assert(p); |
| |
| // add new cells to partition list |
| p->m_members = (ConcreteCell**)realloc(p->m_members, |
| sizeof(ConcreteCell*)*(p->m_numMembers+numNewCells)); |
| memcpy(&(p->m_members[p->m_numMembers]), newCells, sizeof(ConcreteCell*)*numNewCells); |
| p->m_numMembers += numNewCells; |
| |
| // if is a leaf partition, finished |
| if (p->m_leaf) return; |
| |
| // split new cells into sub-partitions based on location |
| if (p->m_vertical) { |
| cut_loc = p->m_sub2->m_bounds.x; |
| for(c=0; c<numNewCells; c++) |
| if (newCells[c]->m_x < cut_loc) |
| newCells1[numNewCells1++] = newCells[c]; |
| else |
| newCells2[numNewCells2++] = newCells[c]; |
| } else { |
| cut_loc = p->m_sub2->m_bounds.y; |
| for(c=0; c<numNewCells; c++) |
| if (newCells[c]->m_y < cut_loc) |
| newCells1[numNewCells1++] = newCells[c]; |
| else |
| newCells2[numNewCells2++] = newCells[c]; |
| } |
| |
| if (numNewCells1 > 0) incrementalSubpartition(p->m_sub1, newCells1, numNewCells1); |
| if (numNewCells2 > 0) incrementalSubpartition(p->m_sub2, newCells2, numNewCells2); |
| |
| free(newCells1); |
| free(newCells2); |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // incrementalPartition() |
| // |
| /// \brief Adds new cells to an existing partition. Partition sizes/locations are unchanged. |
| /// |
| /// The function recurses, adding new cells to appropriate subpartitions. |
| // |
| // -------------------------------------------------------------------- |
| void incrementalPartition() { |
| int c = 0, c2 = 0; |
| int numNewCells = 0; |
| ConcreteCell **allCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells), |
| **newCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells); |
| |
| assert(g_place_rootPartition); |
| |
| // update cell list of root partition |
| memcpy(allCells, g_place_concreteCells, sizeof(ConcreteCell*)*g_place_numCells); |
| qsort(allCells, g_place_numCells, sizeof(ConcreteCell*), cellSortByID); |
| qsort(g_place_rootPartition->m_members, g_place_rootPartition->m_numMembers, |
| sizeof(ConcreteCell*), cellSortByID); |
| |
| // scan sorted lists and collect cells not in partitions |
| while(!allCells[c++]); |
| while(!g_place_rootPartition->m_members[c2++]); |
| |
| for(; c<g_place_numCells; c++, c2++) { |
| while(c2 < g_place_rootPartition->m_numMembers && |
| allCells[c]->m_id > g_place_rootPartition->m_members[c2]->m_id) c2++; |
| while(c < g_place_numCells && |
| (c2 >= g_place_rootPartition->m_numMembers || |
| allCells[c]->m_id < g_place_rootPartition->m_members[c2]->m_id)) { |
| // a new cell! |
| newCells[numNewCells++] = allCells[c]; |
| c++; |
| } |
| } |
| |
| printf("QPRT-50 : \tincremental partitioning with %d new cells\n", numNewCells); |
| if (numNewCells>0) incrementalSubpartition(g_place_rootPartition, newCells, numNewCells); |
| |
| free(allCells); |
| free(newCells); |
| } |
| ABC_NAMESPACE_IMPL_END |
| |