They measure the performance using the ICCAD’19 global routing contest evaluation metric of detailed routing solution:

MetricWeight
Length of Wire0.5
Number of vias4
Length of wrong-way wire1
Number of off-track vias1
Length of off-track wires0.5
Length of out-of-guide wires1
Number of out-of-guide vias1
Number of min-area violations500
Number of spacing violations500
Number of short violations500
Short metal area / metal2 pitch500

Notation:

  • Capacity of edge is number of tracks going through the edge
  • Capacity of G-cell is given by the average capacity of its two abutting wire edges
  • Number of wires going through the edge (usage)
  • Number of vias in G-cell :
  • Demand of edge :
  • Demand of G-cell :

where and are the two G-cells adjacent to in the preferred routing direction (Q: What about G-cells on the edges?)

  • Resource of an edge
  • Resource of a G-cell

Their Algorithm:

They do all phases on the 3D grid graph. Compute the cost of a net’s path by

where

and , are constants. This is a slight variation on the cost given by FastRoute 1.0. They use a similar cost for vias:

where is another constant (unit via cost).

Their initial routing (pattern routing) extends -pattern routing to 3D to search through all possible 3D L-pattern routes to find the route with minimal . They make use of dynamic programming to efficiently search through the -search space.

They break maze routing into two levels: planning and fine-grained maze routing.

For the planning stage, they compress blocks of G-cells into a coarsened cell . Denote by

  • the resource of cell given by the average across all
  • Two adjacent cells on the same layer are given an edge cost of
  • Two adjacent cells on different layers are given an edge cost of

They perform maze routing on the coarsened grid graph, , to get an initial planning. They then re-perform maze routing using the routes on to narrow the search space down.


They say they ran the benchmarks using 8 threads, but they give no mention as to how they parallelize CUGR. The project repository doesn’t list any parallel implementation libraries except for the -lpthread linker flag (they appear to just use pthreads, see runJobsMT in src/db/Database.cpp).

Looking at the src/multi_net/Scheduler.cpp code, they appear to assign “routers” (SingleNetRouter) to a list of batches. Each batch appears to contain a list of routers (each net is assigned to a router) which do not work in the same regions.

int lastUnroute = 0;
while (lastUnroute < routerIds.size()) {
	// create a new batch from a seed
	batches.emplace_back();
	initSet({}); // Initializes an r-tree
	vector<int> &batch = batches.back();
	for (int i = lastUnroute; i < routerIds.size(); ++i) {
		int routerId = routerIds[i];
 
		if (!assigned[routerId] && !hasConflict(routerId)) {
			batch.push_back(routerId);
			assigned[routerId] = true;
			updateSet(routerId); // Add's routers[routerId]'s "guides" to r-tree
		}
	}
	// find the next seed
	while (lastUnroute < routerIds.size() && assigned[routerIds[lastUnroute]]) {
		++lastUnroute;
	}
}

They make use of an R-tree to accelerate the conflict search:

bool Scheduler::hasConflict(int jobIdx) {
    for (const auto &guide : routers[jobIdx].guides) {
        boostBox box(boostPoint(guide[X].low, guide[Y].low), boostPoint(guide[X].high, guide[Y].high));
 
        std::vector<std::pair<boostBox, int>> results;
        rtrees[guide.layerIdx].query(bgi::intersects(box), std::back_inserter(results));
 
        for (const auto &result : results) {
            if (result.second != jobIdx) {
                return true;
            }
        }
    }
    return false;
}

Each router has a list of “guides” indicating the search region. Each guide is represented by a box, I am unsure how the boxes are formed (relevant code is in Router::getBatches() and SingleNetRouter::planMazeRoute()).

The batches are constructed before any nets are actually routed. It also appears they do not run the fine-grained maze routing in parallel. The initial pattern routing does appear to be run in parallel.

for (const vector<int>& batch : batches) {
   runJobsMT(batch.size(), [&](int jobIdx) {
   	auto& router = routers[batch[jobIdx]];
   	router.planMazeRoute(congMap);
   });
 
   for (auto jobIdx : batch) {
   	auto& router = routers[jobIdx];
   	router.mazeRoute();
   	router.finish();
 
   	int netIdx = netsToRoute[jobIdx];
   	congMap.update(grDatabase.nets[netIdx]);
   	allNetStatus[netIdx] = router.status;
   }
}