diff --git a/src/com/xilinx/rapidwright/rwroute/CUFR.java b/src/com/xilinx/rapidwright/rwroute/CUFR.java index 85805cb40..208776f7b 100644 --- a/src/com/xilinx/rapidwright/rwroute/CUFR.java +++ b/src/com/xilinx/rapidwright/rwroute/CUFR.java @@ -82,7 +82,7 @@ public CUFR(Design design, RWRouteConfig config) { public static class RouteNodeGraphCUFR extends RouteNodeGraph { public RouteNodeGraphCUFR(Design design, RWRouteConfig config) { - super(design, config, new ConcurrentHashMap<>()); + super(design, config); } // Do not track createRnodeTime since it is meaningless when multithreading @@ -92,7 +92,7 @@ protected void addCreateRnodeTime(long time) {} public static class RouteNodeGraphCUFRTimingDriven extends RouteNodeGraphTimingDriven { public RouteNodeGraphCUFRTimingDriven(Design design, RWRouteConfig config, DelayEstimatorBase delayEstimator) { - super(design, config, delayEstimator, new ConcurrentHashMap<>()); + super(design, config, delayEstimator); } // Do not track createRnodeTime since it is meaningless when multithreading diff --git a/src/com/xilinx/rapidwright/rwroute/Connection.java b/src/com/xilinx/rapidwright/rwroute/Connection.java index 95a7f87e0..42a395845 100644 --- a/src/com/xilinx/rapidwright/rwroute/Connection.java +++ b/src/com/xilinx/rapidwright/rwroute/Connection.java @@ -215,19 +215,6 @@ public boolean isCongested() { return false; } - /** - * Checks if a connection is routed through any rnodes that have multiple drivers. - * @return - */ - public boolean useRnodesWithMultiDrivers() { - for (RouteNode rn : getRnodes()) { - if (rn.hasMultiDrivers()) { - return true; - } - } - return false; - } - /** * Add the give RouteNode to the list of those used by this Connection. * Expand the bounding box accordingly, since this node could describe an @@ -299,13 +286,17 @@ public List getAltSinkRnodes() { return altSinkRnodes == null ? Collections.emptyList() : altSinkRnodes; } + public boolean hasAltSinks() { + return altSinkRnodes != null && !altSinkRnodes.isEmpty(); + } + public void addAltSinkRnode(RouteNode sinkRnode) { if (altSinkRnodes == null) { altSinkRnodes = new ArrayList<>(1); } else { assert(!altSinkRnodes.contains(sinkRnode)); } - assert(sinkRnode.getType().isExclusiveSink() || + assert(sinkRnode.getType().isAnyExclusiveSink() || // Can be a WIRE if node is not exclusive a sink sinkRnode.getType() == RouteNodeType.NON_LOCAL); altSinkRnodes.add(sinkRnode); @@ -351,6 +342,10 @@ public NetWrapper getNetWrapper() { return this.netWrapper; } + public Net getNet() { + return netWrapper.getNet(); + } + public SitePinInst getSource() { return source; } @@ -469,7 +464,7 @@ public String toString() { } public void setAllTargets(RWRoute.ConnectionState state) { - if (sinkRnode.countConnectionsOfUser(netWrapper) == 0 || + if (sinkRnode.countConnectionsOfUser(netWrapper) == 1 || sinkRnode.getIntentCode() == IntentCode.NODE_PINBOUNCE) { // Since this connection will have been ripped up, only mark a node // as a target if it's not already used by this net. @@ -487,7 +482,7 @@ public void setAllTargets(RWRoute.ConnectionState state) { // where the same physical pin services more than one logical pin if (rnode.countConnectionsOfUser(netWrapper) == 0 || // Except if it is not an EXCLUSIVE_SINK - rnode.getType().isExclusiveSink()) { + !rnode.getType().isAnyExclusiveSink()) { assert(rnode.getIntentCode() != IntentCode.NODE_PINBOUNCE); rnode.markTarget(state); } @@ -515,4 +510,12 @@ protected Pair getOrCreateAlternateSource(RouteNodeGraph assert(altSourceRnode != null); return new Pair<>(altSource, altSourceRnode); } + + public boolean isRouted() { + return sink.isRouted(); + } + + public void setRouted(boolean isRouted) { + sink.setRouted(isRouted); + } } diff --git a/src/com/xilinx/rapidwright/rwroute/GlobalSignalRouting.java b/src/com/xilinx/rapidwright/rwroute/GlobalSignalRouting.java index e2bdd88a9..49e8823c4 100644 --- a/src/com/xilinx/rapidwright/rwroute/GlobalSignalRouting.java +++ b/src/com/xilinx/rapidwright/rwroute/GlobalSignalRouting.java @@ -331,22 +331,20 @@ private static ClockRegion findCentroid(Net clk, Device device) { } /** - * Routes a static net (GND or VCC). - * @param currNet The current static net to be routed. + * Routes pins from a static net (GND or VCC). + * @param pins A list of static pins to be routed (must all be on the same net). * @param getNodeState Lambda to get a node's status (available, unavailable, already in-use). * @param design The {@link Design} instance to use. * @param routeThruHelper The {@link RouteThruHelper} instance to use. */ - public static void routeStaticNet(Net currNet, + public static void routeStaticNet(List pins, Function getNodeState, Design design, RouteThruHelper routeThruHelper) { - NetType netType = currNet.getType(); - Set netPIPs = new HashSet<>(currNet.getPIPs()); Queue q = new ArrayDeque<>(); Set usedRoutingNodes = new HashSet<>(); Map prevNode = new HashMap<>(); List pathNodes = new ArrayList<>(); - Set sitePinsToCreate = new HashSet<>(); + List sitePinsToCreate = new ArrayList<>(); final Node INVALID_NODE = new Node(null, Integer.MAX_VALUE); assert(INVALID_NODE.isInvalidNode()); @@ -381,12 +379,25 @@ public static void routeStaticNet(Net currNet, } // Collect all node-sink pairs to be routed + Net currNet = null; Map nodeToRouteToSink = new HashMap<>(); - for (SitePinInst sink : currNet.getPins()) { - if (sink.isRouted() || sink.isOutPin()) { + for (SitePinInst sink : pins) { + if (currNet == null) { + currNet = sink.getNet(); + assert(currNet != null); + } else { + assert(currNet == sink.getNet()); + } + assert(!sink.isOutPin()); + if (sink.isRouted()) { continue; } - nodeToRouteToSink.put(sink.getConnectedNode(), sink); + + Node node = sink.getConnectedNode(); + if (getNodeState.apply(node) != NodeStatus.INUSE) { + throw new RuntimeException("ERROR: Site pin " + sink + " is not available for net " + currNet); + } + nodeToRouteToSink.put(node, sink); } // Sort them by their tile, ensuring that sinks in the same tile are routed in sequence @@ -394,6 +405,7 @@ public static void routeStaticNet(Net currNet, List nodesToRoute = new ArrayList<>(nodeToRouteToSink.keySet()); nodesToRoute.sort(Comparator.comparing((n) -> n.getTile().getUniqueAddress())); + NetType netType = currNet.getType(); for (Node node : nodesToRoute) { int watchdog = 10000; SitePinInst sink = nodeToRouteToSink.get(node); @@ -520,18 +532,6 @@ public static void routeStaticNet(Net currNet, node = uphillNode; break search; } - - // uphillNode must be a sink to be routed; preserve only the current routing, clear the queue, - // and restart routing from this new sink to encourage reuse - assert(!uphillSink.isRouted()); - do { - usedRoutingNodes.add(node); - node = prevNode.get(node); - } while (node != INVALID_NODE); - prevNode.keySet().removeIf((n) -> !usedRoutingNodes.contains(n) && !n.equals(uphillNode)); - q.clear(); - q.add(uphillNode); - continue search; } q.add(uphillNode); @@ -553,7 +553,9 @@ public static void routeStaticNet(Net currNet, // Note that the static net router goes backward from sinks to sources, // requiring the srcToSinkOrder parameter to be set to true below - netPIPs.addAll(RouterHelper.getPIPsFromNodes(pathNodes, true)); + for (PIP pip : RouterHelper.getPIPsFromNodes(pathNodes, true)) { + currNet.addPIP(pip); + } pathNodes.clear(); sink.setRouted(true); @@ -564,6 +566,7 @@ public static void routeStaticNet(Net currNet, } } + assert(sitePinsToCreate.stream().distinct().count() == sitePinsToCreate.size()); for (SitePin sitePin : sitePinsToCreate) { Site site = sitePin.getSite(); SiteInst si = design.getSiteInstFromSite(site); @@ -591,8 +594,6 @@ public static void routeStaticNet(Net currNet, currNet.addPin(spi, updateSiteRouting); spi.setRouted(true); } - - currNet.setPIPs(netPIPs); } /** diff --git a/src/com/xilinx/rapidwright/rwroute/NetWrapper.java b/src/com/xilinx/rapidwright/rwroute/NetWrapper.java index 05dc3eff8..278a223e1 100644 --- a/src/com/xilinx/rapidwright/rwroute/NetWrapper.java +++ b/src/com/xilinx/rapidwright/rwroute/NetWrapper.java @@ -180,4 +180,34 @@ public SitePinInst getOrCreateAlternateSource(RouteNodeGraph routingGraph) { public RouteNode getAltSourceRnode() { return altSourceRnode; } + + public boolean hasMultipleDrivers(int sequence) { + for (Connection connection : connections) { + List rnodes = connection.getRnodes(); + if (rnodes.isEmpty()) { + continue; + } + + RouteNode driver = rnodes.get(rnodes.size() - 2); + for (int i = rnodes.size() - 1; i >= 0; i--) { + assert(driver != null); + RouteNode rnode = rnodes.get(i); + if (rnode.isVisited(sequence)) { + // Rnode has already been visited by a prior connection; + // check if driver is same as this connection + RouteNode prev = rnode.getPrev(); + if (prev != driver) { + return true; + } + } else { + // Rnode has not been visited by this net yet, + // set initial prev + rnode.setVisited(sequence); + rnode.setPrev(driver); + } + driver = rnode; + } + } + return false; + } } diff --git a/src/com/xilinx/rapidwright/rwroute/PartialCUFR.java b/src/com/xilinx/rapidwright/rwroute/PartialCUFR.java index 2eace5070..0d70a3965 100644 --- a/src/com/xilinx/rapidwright/rwroute/PartialCUFR.java +++ b/src/com/xilinx/rapidwright/rwroute/PartialCUFR.java @@ -62,7 +62,7 @@ public PartialCUFR(Design design, RWRouteConfig config, Collection public static class RouteNodeGraphPartialCUFR extends RouteNodeGraphPartial { public RouteNodeGraphPartialCUFR(Design design, RWRouteConfig config) { - super(design, config, new ConcurrentHashMap<>()); + super(design, config); } // Do not track createRnodeTime since it is meaningless when multithreading @@ -72,7 +72,7 @@ protected void addCreateRnodeTime(long time) {} public static class RouteNodeGraphPartialCUFRTimingDriven extends RouteNodeGraphPartialTimingDriven { public RouteNodeGraphPartialCUFRTimingDriven(Design design, RWRouteConfig config, DelayEstimatorBase delayEstimator) { - super(design, config, delayEstimator, new ConcurrentHashMap<>()); + super(design, config, delayEstimator); } // Do not track createRnodeTime since it is meaningless when multithreading @@ -137,9 +137,10 @@ protected void routeIndirectConnections(Collection connections) { } @Override - protected void unpreserveNet(Net net) { - super.unpreserveNet(net); + protected NetWrapper unpreserveNet(Net net) { + NetWrapper netWrapper = super.unpreserveNet(net); needsRepartitioning = true; + return netWrapper; } @Override diff --git a/src/com/xilinx/rapidwright/rwroute/PartialRouter.java b/src/com/xilinx/rapidwright/rwroute/PartialRouter.java index 90435b794..ba9c00c8f 100644 --- a/src/com/xilinx/rapidwright/rwroute/PartialRouter.java +++ b/src/com/xilinx/rapidwright/rwroute/PartialRouter.java @@ -28,7 +28,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -44,7 +43,6 @@ import com.xilinx.rapidwright.device.Node; import com.xilinx.rapidwright.device.PIP; import com.xilinx.rapidwright.device.Series; -import com.xilinx.rapidwright.device.Tile; import com.xilinx.rapidwright.router.UltraScaleClockRouting; import com.xilinx.rapidwright.tests.CodePerfTracker; import com.xilinx.rapidwright.timing.ClkRouteTiming; @@ -71,12 +69,8 @@ public class PartialRouter extends RWRoute { protected static class RouteNodeGraphPartial extends RouteNodeGraph { - public RouteNodeGraphPartial(Design design, RWRouteConfig config, Map nodesMap) { - super(design, config, nodesMap); - } - public RouteNodeGraphPartial(Design design, RWRouteConfig config) { - this(design, config, new HashMap<>()); + super(design, config); } @Override @@ -90,17 +84,10 @@ protected boolean isExcluded(RouteNode parent, Node child) { } protected static class RouteNodeGraphPartialTimingDriven extends RouteNodeGraphTimingDriven { - public RouteNodeGraphPartialTimingDriven(Design design, - RWRouteConfig config, - DelayEstimatorBase delayEstimator, - Map nodesMap) { - super(design, config, delayEstimator, nodesMap); - } - public RouteNodeGraphPartialTimingDriven(Design design, RWRouteConfig config, DelayEstimatorBase delayEstimator) { - this(design, config, delayEstimator, new HashMap<>()); + super(design, config, delayEstimator); } @Override @@ -168,7 +155,7 @@ protected static boolean isPartOfExistingRoute(RouteNodeGraph routingGraph, Rout return false; } - if (prev.equals(start) && routingGraph.isPreserved(end)) { + if (prev == start && routingGraph.isPreserved(end)) { // Arc matches start node and end node is preserved // This implies that both start and end nodes must be preserved for the same net // (which assumedly is the net we're currently routing, and is asserted upstream) @@ -202,7 +189,7 @@ protected TimingManager createTimingManager(ClkRouteTiming clkTiming, Collection protected int getNumIndirectConnectionPins() { int totalSitePins = 0; for (Connection connection : indirectConnections) { - totalSitePins += (connection.getSink().isRouted() && !connection.isCongested()) ? 0 : 1; + totalSitePins += (connection.isRouted() && !connection.isCongested()) ? 0 : 1; } return totalSitePins; } @@ -211,7 +198,7 @@ protected int getNumIndirectConnectionPins() { protected int getNumConnectionsCrossingSLRs() { int numCrossingSLRs = 0; for (Connection c : indirectConnections) { - numCrossingSLRs += (!c.isCrossSLR() || (c.getSink().isRouted() && !c.isCongested())) ? 0 : 1; + numCrossingSLRs += (!c.isCrossSLR() || (c.isRouted() && !c.isCongested())) ? 0 : 1; } return numCrossingSLRs; } @@ -249,23 +236,45 @@ protected void determineRoutingTargets() { // if so, unpreserve that blocking net Set unpreserveNets = new HashSet<>(); for (Connection connection : indirectConnections) { - Net net = connection.getNetWrapper().getNet(); + Net net = connection.getNet(); Net preservedNet; assert((preservedNet = routingGraph.getPreservedNet(connection.getSourceRnode())) == null || preservedNet == net); RouteNode sinkRnode = connection.getSinkRnode(); preservedNet = routingGraph.getPreservedNet(sinkRnode); if (preservedNet != null && preservedNet != net) { unpreserveNets.add(preservedNet); - assert(sinkRnode.getType().isExclusiveSink()); + assert(sinkRnode.getType().isAnyExclusiveSink()); } } if (!unpreserveNets.isEmpty()) { - System.out.println("INFO: Unpreserving " + unpreserveNets.size() + " nets to improve sink routability"); + System.out.println("INFO: Unpreserving " + unpreserveNets.size() + " nets to ensure sink routability"); for (Net net : unpreserveNets) { System.out.println("\t" + net); assert(!net.isStaticNet()); - unpreserveNet(net); + NetWrapper netWrapper = unpreserveNet(net); + for (Connection connection : netWrapper.getConnections()) { + List rnodes = connection.getRnodes(); + if (rnodes.size() < 3) { + continue; + } + // Look for overused exclusive sinks within + // this connection's used nodes (except for the first and last used node, + // corresponding to source and sink) + for (RouteNode rnode : rnodes.subList(1, rnodes.size() - 1)) { + if (!rnode.getType().isAnyExclusiveSink() || !rnode.isOverUsed()) { + continue; + } + + // If an overused exclusive sink is found -- it must also be used + // by the net to which that sink belongs to, so rip up this connection's + // routing + ripUp(connection); + connection.resetRoute(); + connection.setRouted(false); + break; + } + } } } } @@ -290,8 +299,17 @@ protected void addGlobalClkRoutingTargets(Net clk) { @Override protected void addStaticNetRoutingTargets(Net staticNet) { - preserveNet(staticNet, true); if (staticNet.hasPIPs()) { + // Preserve only the static net's PIPs and its output pins; its input pins will be + // preserved by routeStaticNets() + List outputPins = new ArrayList<>(); + for (SitePinInst spi : staticNet.getPins()) { + if (!spi.isOutPin()) { + continue; + } + outputPins.add(spi); + } + routingGraph.preserveAsync(staticNet, outputPins); numPreservedStaticNets++; } @@ -310,8 +328,12 @@ protected void addStaticNetRoutingTargets(Net staticNet) { @Override protected void preserveNet(Net net, boolean async) { - List pinsToRoute = netToPins.get(net); - // Only preserve those pins that are not to be routed + List pinsToRoute = null; + if (!net.isStaticNet()) { + // For signal nets, only preserve those pins that are not to be routed + // All sink pins must be preserved for static nets since the static router does not resolve conflicts + pinsToRoute = netToPins.get(net); + } List pinsToPreserve; if (pinsToRoute == null) { pinsToPreserve = net.getPins(); @@ -394,13 +416,6 @@ protected void addNetConnectionToRoutingTargets(Net net) { continue; } - // Even though this connection is not expected to have any routing yet, - // perform a rip up anyway in order to release any exclusive sinks - // ahead of finishRouteConnection() - assert(connection.getRnodes().isEmpty()); - connection.getSink().setRouted(false); - ripUp(connection); - RouteNode sinkRnode = connection.getSinkRnode(); finishRouteConnection(connection, sinkRnode); } @@ -431,7 +446,7 @@ protected boolean saveRouting(Connection connection, RouteNode rnode) { assert(rnodes.size() > 1); // Check if alternate source exists (without creating one if it doesn't) - if (connection.getNetWrapper().getNet().getAlternateSource() != null) { + if (connection.getNet().getAlternateSource() != null) { Pair altSourceAndRnode = connection.getOrCreateAlternateSource(routingGraph); assert(altSourceAndRnode != null); RouteNode altSourceRnode = altSourceAndRnode.getSecond(); @@ -450,13 +465,8 @@ protected boolean saveRouting(Connection connection, RouteNode rnode) { protected void finishRouteConnection(Connection connection, RouteNode rnode) { super.finishRouteConnection(connection, rnode); - if (!connection.getSink().isRouted()) { + if (!connection.isRouted()) { connection.resetRoute(); - if (connection.getAltSinkRnodes().isEmpty()) { - // Undo what ripUp() would have done for this connection which has a single exclusive sink - rnode.incrementUser(connection.getNetWrapper()); - rnode.updatePresentCongestionCost(presentCongestionFactor); - } } } @@ -524,7 +534,7 @@ protected int unpreserveNetsAndReleaseResources(Connection connection) { return unpreserveNets.size(); } - protected void unpreserveNet(Net net) { + protected NetWrapper unpreserveNet(Net net) { assert(!net.getName().equals(Net.Z_NET)); Set rnodes = new HashSet<>(); @@ -599,14 +609,7 @@ protected void unpreserveNet(Net net) { RouteNode sourceRnode = connection.getSourceRnode(); RouteNode sinkRnode = connection.getSinkRnode(); assert(sourceRnode.getType() == RouteNodeType.EXCLUSIVE_SOURCE); - assert(sinkRnode.getType().isExclusiveSink()); - - // Even though this connection is not expected to have any routing yet, - // perform a rip up anyway in order to release any exclusive sinks - // ahead of finishRouteConnection() - assert(connection.getRnodes().isEmpty()); - connection.getSink().setRouted(false); - ripUp(connection); + assert(sinkRnode.getType().isAnyExclusiveSink()); finishRouteConnection(connection, sinkRnode); } @@ -642,6 +645,7 @@ protected void unpreserveNet(Net net) { numPreservedWire--; numPreservedRoutableNets--; + return netWrapper; } @Override @@ -652,9 +656,9 @@ protected boolean handleUnroutableConnection(Connection connection) { } if (softPreserve && ( // First iteration, without alternate source - (routeIteration == 1 && connection.getNetWrapper().getNet().getAlternateSource() == null) || + (routeIteration == 1 && connection.getNet().getAlternateSource() == null) || // Second iteration, with alternate source - (routeIteration == 2 && connection.getNetWrapper().getNet().getAlternateSource() != null)) + (routeIteration == 2 && connection.getNet().getAlternateSource() != null)) ) { int netsUnpreserved = unpreserveNetsAndReleaseResources(connection); if (netsUnpreserved > 0) { diff --git a/src/com/xilinx/rapidwright/rwroute/RWRoute.java b/src/com/xilinx/rapidwright/rwroute/RWRoute.java index e7c3723d5..c533c14af 100644 --- a/src/com/xilinx/rapidwright/rwroute/RWRoute.java +++ b/src/com/xilinx/rapidwright/rwroute/RWRoute.java @@ -56,10 +56,10 @@ import com.xilinx.rapidwright.util.Pair; import com.xilinx.rapidwright.util.RuntimeTracker; import com.xilinx.rapidwright.util.RuntimeTrackerTree; +import com.xilinx.rapidwright.util.Utils; import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; @@ -67,6 +67,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -320,11 +321,37 @@ protected void determineRoutingTargets() { // an alternate sink for one net later becomes an exclusive sink for another net. // Examine for all connections for this case and remove such alternate sinks. for (Connection connection : indirectConnections) { - connection.getAltSinkRnodes().removeIf((rnode) -> rnode.getOccupancy() > 0); + connection.getAltSinkRnodes().removeIf(RouteNode::isUsed); } // Wait for all outstanding RouteNodeGraph.preserveAsync() calls to complete routingGraph.awaitPreserve(); + + // On Versal only, reserve all uphills of NODE_(CLE|INTF)_CTRL sinks since + // their [BC]NODEs can also be used to reach NODE_INODEs --- not applying this + // heuristic can lead to avoidable congestion + if (routingGraph.isVersal) { + for (Connection connection : indirectConnections) { + RouteNode sinkRnode = connection.getSinkRnode(); + if (sinkRnode.getType() == RouteNodeType.EXCLUSIVE_SINK_BOTH) { + for (Node uphill : sinkRnode.getAllUphillNodes()) { + if (uphill.isTiedToVcc()) { + continue; + } + Net preservedNet = routingGraph.getPreservedNet(uphill); + if (preservedNet != null && preservedNet != connection.getNet()) { + continue; + } + assert((sinkRnode.getIntentCode() == IntentCode.NODE_CLE_CTRL && + (uphill.getIntentCode() == IntentCode.NODE_CLE_CNODE || uphill.getIntentCode() == IntentCode.NODE_CLE_BNODE)) || + (sinkRnode.getIntentCode() == IntentCode.NODE_INTF_CTRL && + (uphill.getIntentCode() == IntentCode.NODE_INTF_CNODE || uphill.getIntentCode() == IntentCode.NODE_INTF_BNODE))); + RouteNode rnode = routingGraph.getOrCreate(uphill, RouteNodeType.LOCAL_RESERVED); + rnode.setType(RouteNodeType.LOCAL_RESERVED); + } + } + } + } } private void categorizeNets() { @@ -493,11 +520,9 @@ protected void addStaticNetRoutingTargets(Net staticNet) { if (sinks.size() > 0) { staticNet.unroute(); // Remove all output pins from unrouted net as those used will be repopulated - staticNet.getPins().removeIf(SitePinInst::isOutPin); + staticNet.setPins(sinks); - // Preserve all pins (e.g. in case of BOUNCE nodes that may serve as a site pin) - preserveNet(staticNet, true); - staticNetAndRoutingTargets.put(staticNet, sinks); + staticNetAndRoutingTargets.put(staticNet, new ArrayList<>(sinks)); } else { numNotNeedingRoutingNets++; } @@ -507,20 +532,64 @@ protected void addStaticNetRoutingTargets(Net staticNet) { * Routes static nets. */ protected void routeStaticNets() { - if (staticNetAndRoutingTargets.isEmpty()) - return; + Net vccNet = design.getVccNet(); + Net gndNet = design.getGndNet(); + + boolean noStaticRouting = staticNetAndRoutingTargets.isEmpty(); + if (!noStaticRouting) { + List gndPins = staticNetAndRoutingTargets.get(gndNet); + if (gndPins != null) { + boolean invertGndToVccForLutInputs = config.isInvertGndToVccForLutInputs(); + Set newVccPins = RouterHelper.invertPossibleGndPinsToVccPins(design, gndPins, invertGndToVccForLutInputs); + if (!newVccPins.isEmpty()) { + gndPins.removeAll(newVccPins); + staticNetAndRoutingTargets.computeIfAbsent(vccNet, (net) -> new ArrayList<>()) + .addAll(newVccPins); + } + } + + Iterator>> it = staticNetAndRoutingTargets.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry> e = it.next(); + Net staticNet = e.getKey(); + List pins = e.getValue(); + // For some encrypted designs, it's possible that RapidWright cannot infer all SitePinInst-s leading to + // some site pins (e.g. CKEN) defaulting those to static nets. Detect such cases -- when signal nets are + // already routed to and preserved at those uninferrable SitePinInst-s -- and remove them from being a + // static net sink + pins.removeIf(spi -> { + Net preservedNet = routingGraph.getPreservedNet(spi.getConnectedNode()); + if (preservedNet == null) { + // This sink is not preserved by any net, allow + return false; + } + // Sink preserved by another net, abandon; check that it cannot have been preserved by this static net + assert(preservedNet != staticNet); + return true; + }); + + // Remove from map if empty + if (pins.isEmpty()) { + it.remove(); + } + } + } - List gndPins = staticNetAndRoutingTargets.get(design.getGndNet()); - if (gndPins != null) { - boolean invertGndToVccForLutInputs = config.isInvertGndToVccForLutInputs(); - Set newVccPins = RouterHelper.invertPossibleGndPinsToVccPins(design, gndPins, invertGndToVccForLutInputs); - if (!newVccPins.isEmpty()) { - gndPins.removeAll(newVccPins); - staticNetAndRoutingTargets.computeIfAbsent(design.getVccNet(), (net) -> new ArrayList<>()) - .addAll(newVccPins); + // Preserve all static nets' sink pins regardless of whether any routing is necessary + for (Net staticNet : Arrays.asList(vccNet, gndNet)) { + for (SitePinInst spi : staticNet.getPins()) { + if (spi.isOutPin()) { + continue; + } + routingGraph.preserve(spi.getConnectedNode(), staticNet); } } + if (noStaticRouting) { + // Now that all static nets have been fully preserved, return if no work to be done + return; + } + for (Map.Entry> e : staticNetAndRoutingTargets.entrySet()) { Net staticNet = e.getKey(); List pins = e.getValue(); @@ -528,7 +597,7 @@ protected void routeStaticNets() { System.out.println("INFO: Routing " + pins.size() + " pins of " + staticNet); Function gns = (node) -> getGlobalRoutingNodeStatus(staticNet, node); - GlobalSignalRouting.routeStaticNet(staticNet, gns, design, routethruHelper); + GlobalSignalRouting.routeStaticNet(pins, gns, design, routethruHelper); preserveNet(staticNet, false); } @@ -601,21 +670,31 @@ protected NetWrapper createNetWrapperAndConnections(Net net) { } indirectConnections.add(connection); - BitSet[] eastWestWires = (routingGraph.eastWestWires == null) ? null : - routingGraph.eastWestWires.get(sinkINTNode.getTile().getTileTypeEnum()); - RouteNode sinkRnode; - if (eastWestWires != null && eastWestWires[0].get(sinkINTNode.getWireIndex())) { - sinkRnode = routingGraph.getOrCreate(sinkINTNode, RouteNodeType.EXCLUSIVE_SINK_EAST); - sinkRnode.setType(RouteNodeType.EXCLUSIVE_SINK_EAST); - } else if (eastWestWires != null && eastWestWires[1].get(sinkINTNode.getWireIndex())) { - sinkRnode = routingGraph.getOrCreate(sinkINTNode, RouteNodeType.EXCLUSIVE_SINK_WEST); - sinkRnode.setType(RouteNodeType.EXCLUSIVE_SINK_WEST); - } else { - sinkRnode = routingGraph.getOrCreate(sinkINTNode, RouteNodeType.EXCLUSIVE_SINK); - sinkRnode.setType(RouteNodeType.EXCLUSIVE_SINK); - } + + // Inform RouteNodeInfo.getType() that this a sink to prevent it from returning + // RouteNodeType.LAGUNA_PINFEED and instead return LOCAL_{EAST,WEST} + boolean forceSink = true; + RouteNodeType sinkType = RouteNodeInfo.getType(sinkINTNode, null, routingGraph, forceSink); + assert(sinkType.isAnyLocal()); + sinkType = sinkType == RouteNodeType.LOCAL_EAST ? RouteNodeType.EXCLUSIVE_SINK_EAST : + sinkType == RouteNodeType.LOCAL_WEST ? RouteNodeType.EXCLUSIVE_SINK_WEST : + sinkType == RouteNodeType.LOCAL_BOTH ? RouteNodeType.EXCLUSIVE_SINK_BOTH : + null; + assert(sinkType != null); + RouteNode sinkRnode = routingGraph.getOrCreate(sinkINTNode, sinkType); + sinkRnode.setType(sinkType); connection.setSinkRnode(sinkRnode); + if (sinkINTNode.getTile() != sink.getTile()) { + TileTypeEnum sinkTileType = sink.getTile().getTileTypeEnum(); + if (Utils.isLaguna(sinkTileType)) { + // Sinks in Laguna tiles must be Laguna registers (but will be projected into the INT tile) + // however, it's possible for another net to use the sink node as a bounce -- prevent that here + assert(sinkINTNode.getTile().getTileTypeEnum() == TileTypeEnum.INT); + routingGraph.preserve(sink.getConnectedNode(), net); + } + } + // Where appropriate, allow all 6 LUT pins to be swapped to begin with char lutLetter = sink.getName().charAt(0); int numberOfSwappablePins = (lutPinSwapping && sink.isLUTInputPin()) @@ -665,15 +744,14 @@ protected NetWrapper createNetWrapperAndConnections(Net net) { continue; } RouteNode altSinkRnode = routingGraph.getOrCreate(node, sinkRnode.getType()); - assert(altSinkRnode.getType().isExclusiveSink()); + assert(altSinkRnode.getType().isAnyExclusiveSink()); connection.addAltSinkRnode(altSinkRnode); } - if (connection.getAltSinkRnodes().isEmpty()) { + if (!connection.hasAltSinks()) { // Since this connection only has a single sink target, increment // its usage here immediately sinkRnode.incrementUser(netWrapper); - sinkRnode.updatePresentCongestionCost(presentCongestionFactor); } connection.setDirect(false); @@ -830,7 +908,7 @@ private void routeDirectConnections() { System.out.println("\nINFO: Route " + directConnections.size() + " direct connections "); for (Connection connection : directConnections) { boolean success = RouterHelper.routeDirectConnection(connection); - connection.getSink().setRouted(success); + connection.setRouted(success); // no need to update route delay of direct connection, because it would not be changed if (!success) System.err.println("ERROR: Failed to route direct connection " + connection); } @@ -860,6 +938,7 @@ public void routeIndirectConnectionsIteratively() { if (config.isTimingDriven()) { setRerouteCriticality(); } + routingGraph.updatePresentCongestionCosts(presentCongestionFactor); routeIndirectConnections(sortedIndirectConnections); rnodesTimer.setTime(routingGraph.getCreateRnodeTime()); @@ -923,7 +1002,7 @@ public void routeIndirectConnectionsIteratively() { private List getUnroutableConnections() { List unroutedConnections = new ArrayList<>(); for (Connection connection : sortedIndirectConnections) { - if (!connection.getSink().isRouted()) { + if (!connection.isRouted()) { unroutedConnections.add(connection); } } @@ -948,7 +1027,7 @@ protected void postRouteProcess() { return; } - // perform LUT pin mapping updates + // Perform LUT pin mapping updates if (lutPinSwapping && !Boolean.getBoolean("rapidwright.rwroute.lutPinSwapping.deferIntraSiteRoutingUpdates")) { Map pinSwaps = new HashMap<>(); @@ -965,7 +1044,7 @@ protected void postRouteProcess() { } connection.setSinkRnode(newSinkRnode); - SitePin newSitePin = newSinkRnode.getNode().getSitePin(); + SitePin newSitePin = newSinkRnode.getSitePin(); String existing = pinSwaps.put(oldSinkSpi, newSitePin.getPinName()); assert(existing == null); } @@ -975,8 +1054,10 @@ protected void postRouteProcess() { assignNodesToConnections(); // fix routes with cycles and / or multi-driver nodes - Set routes = fixRoutes(); - if (config.isTimingDriven()) updateTimingAfterFixingRoutes(routes); + List fixedRoutes = fixRoutes(); + if (config.isTimingDriven()) { + updateTimingAfterFixingRoutes(fixedRoutes); + } // Unset the routed state of all source pins for (Map.Entry e : nets.entrySet()) { @@ -1073,14 +1154,14 @@ protected boolean shouldRoute(Connection connection) { } } - if (connection.getAltSinkRnodes().isEmpty()) { + if (!connection.hasAltSinks()) { // Check that this connection's exclusive sink node is used but never overused RouteNode sinkRnode = connection.getSinkRnode(); assert(sinkRnode.countConnectionsOfUser(connection.getNetWrapper()) > 0); assert(!sinkRnode.isOverUsed()); } - return !connection.getSink().isRouted() || connection.isCongested(); + return !connection.isRouted() || connection.isCongested(); } /** @@ -1119,10 +1200,10 @@ private void updateTiming() { /** * Updates timing after fixing routes of nets. - * @param netsWithIllegalRoutes A set of nets whose routes have been fixed. + * @param fixedRoutes A set of nets whose routes have been fixed. */ - private void updateTimingAfterFixingRoutes(Set netsWithIllegalRoutes) { - timingManager.updateIllegalNetsDelays(netsWithIllegalRoutes, nodesDelays); + private void updateTimingAfterFixingRoutes(List fixedRoutes) { + timingManager.updateIllegalNetsDelays(fixedRoutes, nodesDelays); timingManager.patchUpDelayOfConnections(sortedIndirectConnections); updateTiming(); } @@ -1150,7 +1231,7 @@ protected void assignNodesToConnections() { } } else { // Routing must go to an alternate sink - assert(!connection.getAltSinkRnodes().isEmpty()); + assert(connection.hasAltSinks()); // Assume that it doesn't need unprojecting back to the sink pin // since the sink node is a site pin @@ -1158,7 +1239,7 @@ protected void assignNodesToConnections() { } for (RouteNode rnode : rnodes) { - nodes.add(rnode.getNode()); + nodes.add(rnode); } List sourceToSwitchBox = RouterHelper.findPathBetweenNodes(connection.getSource().getConnectedNode(), connection.getSourceRnode()); @@ -1249,16 +1330,10 @@ private void updateCostFactors() { private void updateCost() { overUsedRnodes.clear(); for (RouteNode rnode : routingGraph.getRnodes()) { - int overuse=rnode.getOccupancy() - RouteNode.capacity; - if (overuse == 0) { - rnode.setPresentCongestionCost(1 + presentCongestionFactor); - } else if (overuse > 0) { + int overuse = rnode.getOccupancy() - RouteNode.capacity; + if (overuse > 0) { overUsedRnodes.add(rnode); - rnode.setPresentCongestionCost(1 + (overuse + 1) * presentCongestionFactor); rnode.setHistoricalCongestionCost(rnode.getHistoricalCongestionCost() + overuse * historicalCongestionFactor); - } else { - assert(overuse < 0); - assert(rnode.getPresentCongestionCost() == 1); } } } @@ -1380,60 +1455,29 @@ private void computesNodeUsageAndTotalWirelength() { /** * Fixes routes of nets with routing path cycles and multi-driver nodes. - * @return A set of nets that have been fixed. - */ - private Set fixRoutes() { - Set illegalRoutes = findIllegalRoutes(); - // fix routes with cycles and / or multi-driver nodes - for (NetWrapper route:illegalRoutes) { - for (Connection connection : route.getConnections()) { - try { - if (!connection.isDirect()) ripUp(connection); - } catch (Exception e) { - e.printStackTrace(); - } - } - RouteFixer graphHelper = new RouteFixer(route, routingGraph); - graphHelper.finalizeRoutesOfConnections(); - } - return illegalRoutes; - } - - /** - * Finds nets that have illegal routes by checking its connections' routes. - * @return A set of routed {@link NetWrapper} instances whose should be fixed. */ - private Set findIllegalRoutes() { - Set illegalRoutes = new HashSet<>(); + private List fixRoutes() { + List fixedRoutes = new ArrayList<>(); + int sequence = connectionsRouted.get() + 1; for (Entry e : nets.entrySet()) { NetWrapper netWrapper = e.getValue(); - buildDriverCountsOfRnodes(netWrapper); - for (Connection connection : netWrapper.getConnections()) { - if (shouldMergePath(connection)) { - illegalRoutes.add(netWrapper); - if (config.isTimingDriven()) addNodesDelays(netWrapper); - break; + if (netWrapper.hasMultipleDrivers(sequence)) { + fixedRoutes.add(netWrapper); + if (config.isTimingDriven()) { + addNodesDelays(netWrapper); } - } - } - return illegalRoutes; - } - - /** - * Builds the driversCounts map of each {@link RouteNode} instance that is used by a net. - * @param netWrapper A NetWrapper instance that represents a net. - */ - private void buildDriverCountsOfRnodes(NetWrapper netWrapper) { - for (Connection connection : netWrapper.getConnections()) { - RouteNode driver = null; - for (int i = connection.getRnodes().size() - 1; i >= 0; i--) { - RouteNode rnode = connection.getRnodes().get(i); - if (driver != null) { - rnode.incrementDriver(driver); + for (Connection connection : netWrapper.getConnections()) { + if (connection.isDirect()) { + continue; + } + ripUp(connection); } - driver = rnode; + RouteFixer graphHelper = new RouteFixer(netWrapper, routingGraph); + graphHelper.finalizeRoutesOfConnections(); } + sequence++; } + return fixedRoutes; } /** @@ -1448,15 +1492,6 @@ private void addNodesDelays(NetWrapper net) { } } - /** - * Checks if a connection has multi-driver nodes. - * @param connection The connection in question. - * @return true, if the connection has multi-driver nodes. - */ - private boolean shouldMergePath(Connection connection) { - return connection.useRnodesWithMultiDrivers(); - } - /** * Rips up a connection. * @param connection The connection to be ripped up. @@ -1464,18 +1499,28 @@ private boolean shouldMergePath(Connection connection) { protected void ripUp(Connection connection) { List rnodes = connection.getRnodes(); if (rnodes.isEmpty()) { - assert(!connection.getSink().isRouted()); - if (connection.getAltSinkRnodes().isEmpty()) { - // If there is no alternate sink, decrement this one-and-only sink node - RouteNode sinkRnode = connection.getSinkRnode(); - rnodes = Collections.singletonList(sinkRnode); + assert(!connection.isRouted()); + return; + } + + RouteNode sinkRnode = rnodes.get(0); + if (sinkRnode == connection.getSinkRnode()) { + if (!connection.hasAltSinks()) { + // Sink is exclusive -- do not rip up + rnodes = rnodes.subList(1, rnodes.size() - 1); } + } else { + // Sink is not exclusive + assert(connection.getAltSinkRnodes().contains(sinkRnode)); } + NetWrapper netWrapper = connection.getNetWrapper(); for (RouteNode rnode : rnodes) { - rnode.decrementUser(connection.getNetWrapper()); - rnode.updatePresentCongestionCost(presentCongestionFactor); + rnode.decrementUser(netWrapper); } + + assert(sinkRnode.countConnectionsOfUser(netWrapper) > 0 || + (sinkRnode.countConnectionsOfUser(netWrapper) == 0 && connection.hasAltSinks())); } /** @@ -1483,10 +1528,35 @@ protected void ripUp(Connection connection) { * @param connection The routed connection. */ private void updateUsersAndPresentCongestionCost(Connection connection) { - for (RouteNode rnode : connection.getRnodes()) { - rnode.incrementUser(connection.getNetWrapper()); - rnode.updatePresentCongestionCost(presentCongestionFactor); + List rnodes = connection.getRnodes(); + if (rnodes.isEmpty()) { + assert(!connection.isRouted()); + return; + } + + RouteNode sinkRnode = rnodes.get(0); + if (sinkRnode == connection.getSinkRnode()) { + if (!connection.hasAltSinks()) { + // Sink is exclusive -- do not increment + rnodes = rnodes.subList(1, rnodes.size() - 1); + } + } else { + // Sink is not exclusive + assert(connection.getAltSinkRnodes().contains(sinkRnode)); + } + + NetWrapper netWrapper = connection.getNetWrapper(); + for (RouteNode rnode : rnodes) { + rnode.incrementUser(netWrapper); } + assert(sinkRnode.countConnectionsOfUser(netWrapper) == 1 || + (sinkRnode.countConnectionsOfUser(netWrapper) > 1 && + // An alternate sink (e.g. LUT routethru to FF) that serves more than one FF + (sinkRnode != connection.getSinkRnode() || + // A bounce node that is this sink and also used to serve a different sink + sinkRnode.getIntentCode() == IntentCode.NODE_PINBOUNCE) + ) + ); } /** @@ -1626,26 +1696,21 @@ protected void routeIndirectConnection(Connection connection) { if (rnode != null) { queue.clear(); finishRouteConnection(connection, rnode); - if (!connection.getSink().isRouted()) { + if (!connection.isRouted()) { List rnodes = connection.getRnodes(); throw new RuntimeException("ERROR: Unable to save routing for connection " + connection + "\n" + " Backtracking terminated at " + rnodes.get(rnodes.size() -1)); } - if (config.isTimingDriven()) connection.updateRouteDelay(); - assert(connection.getSink().isRouted()); + if (config.isTimingDriven()) { + connection.updateRouteDelay(); + } + assert(connection.isRouted()); } else { assert(queue.isEmpty()); // Clears previous route of the connection connection.resetRoute(); - connection.getSink().setRouted(false); + connection.setRouted(false); assert(connection.getRnodes().isEmpty()); - - if (connection.getAltSinkRnodes().isEmpty()) { - // Undo what ripUp() did for this connection which has a single exclusive sink - RouteNode sinkRnode = connection.getSinkRnode(); - sinkRnode.incrementUser(connection.getNetWrapper()); - sinkRnode.updatePresentCongestionCost(presentCongestionFactor); - } } // Reset the nodes marked as this connection's target(s) @@ -1667,9 +1732,9 @@ protected void enlargeBoundingBox(Connection connection) { protected void abandonConnectionIfUnroutable(Connection connection) { if (!config.isUseBoundingBox() || config.isEnlargeBoundingBox()) { return; - } + } - System.out.println(" " + "Abandoning"); + System.out.println("INFO: Abandoning\n"); // Since bounding box is never enlarged there is no hope of routing this connection so abandon it indirectConnections.remove(connection); @@ -1713,7 +1778,7 @@ protected boolean swapOutputPin(Connection connection) { RouteNode altSourceRnode = altSourceAndRnode.getSecond(); connection.setSourceRnode(altSourceRnode); - connection.getSink().setRouted(false); + connection.setRouted(false); return true; } @@ -1723,7 +1788,7 @@ protected boolean swapOutputPin(Connection connection) { */ protected void finishRouteConnection(Connection connection, RouteNode rnode) { boolean routed = saveRouting(connection, rnode); - connection.getSink().setRouted(routed); + connection.setRouted(routed); if (routed) { updateUsersAndPresentCongestionCost(connection); } @@ -1741,7 +1806,7 @@ protected boolean saveRouting(Connection connection, RouteNode rnode) { if (rnode != sinkRnode && !altSinkRnodes.contains(rnode)) { List prevRouting = connection.getRnodes(); // Check that this is the sink path marked by prepareRouteConnection() - if (!connection.getSink().isRouted() || prevRouting.isEmpty() || !rnode.isTarget()) { + if (!connection.isRouted() || prevRouting.isEmpty() || !rnode.isTarget()) { throw new RuntimeException("Unexpected rnode to backtrack from: " + rnode); } // Backtrack from the sink used on that sink path @@ -1770,14 +1835,14 @@ private void exploreAndExpand(ConnectionState state, RouteNode rnode) { final Connection connection = state.connection; final int sequence = state.sequence; final PriorityQueue queue = state.queue; - for (RouteNode childRNode:rnode.getChildren(routingGraph)) { + for (RouteNode childRNode : rnode.getChildren(routingGraph)) { // Targets that are visited more than once must be overused assert(!childRNode.isTarget() || !childRNode.isVisited(sequence) || childRNode.willOverUse(connection.getNetWrapper())); // If childRnode is preserved, then it must be preserved for the current net we're routing Net preservedNet; assert((preservedNet = routingGraph.getPreservedNet(childRNode)) == null || - preservedNet == connection.getNetWrapper().getNet()); + preservedNet == connection.getNet()); if (childRNode.isVisited(sequence)) { // Node must be in queue already. @@ -1789,11 +1854,11 @@ private void exploreAndExpand(ConnectionState state, RouteNode rnode) { } if (childRNode.isTarget()) { - boolean earlyTermination = false; - if (childRNode == connection.getSinkRnode() && connection.getAltSinkRnodes().isEmpty()) { + boolean earlyTermination; + if (childRNode == connection.getSinkRnode() && !connection.hasAltSinks()) { // This sink must be exclusively reserved for this connection already - assert(childRNode.getOccupancy() == 0 || - childRNode.getIntentCode() == IntentCode.NODE_PINBOUNCE); + assert(childRNode.getOccupancy() == 1 || + childRNode.getIntentCode() == IntentCode.NODE_PINBOUNCE); earlyTermination = true; } else { // Target is not an exclusive sink, only early terminate if this net will not @@ -1811,22 +1876,24 @@ private void exploreAndExpand(ConnectionState state, RouteNode rnode) { continue; } switch (childRNode.getType()) { - case LOCAL: + case LOCAL_BOTH: case LOCAL_EAST: case LOCAL_WEST: + case LOCAL_RESERVED: if (!routingGraph.isAccessible(childRNode, connection)) { continue; } - // Verify invariant that east/west wires stay east/west - assert(rnode.getType() != RouteNodeType.LOCAL_EAST || childRNode.getType() == RouteNodeType.LOCAL_EAST); - assert(rnode.getType() != RouteNodeType.LOCAL_WEST || childRNode.getType() == RouteNodeType.LOCAL_WEST); + // Verify invariant that east/west wires stay east/west ... + assert(rnode.getType() != RouteNodeType.LOCAL_EAST || childRNode.getType() == RouteNodeType.LOCAL_EAST || + // ... unless it's an exclusive sink using a LOCAL_RESERVED node + (childRNode.getType() == RouteNodeType.LOCAL_RESERVED && connection.getSinkRnode().getType() == RouteNodeType.EXCLUSIVE_SINK_BOTH)); + assert(rnode.getType() != RouteNodeType.LOCAL_WEST || childRNode.getType() == RouteNodeType.LOCAL_WEST || + (childRNode.getType() == RouteNodeType.LOCAL_RESERVED && connection.getSinkRnode().getType() == RouteNodeType.EXCLUSIVE_SINK_BOTH)); break; case NON_LOCAL: // LOCALs cannot connect to NON_LOCALs except via a LUT routethru - assert(!rnode.getType().isLocal() || - routingGraph.lutRoutethru && rnode.getIntentCode() == IntentCode.NODE_PINFEED || - // FIXME: - design.getSeries() == Series.Versal); + assert(!rnode.getType().isAnyLocal() || + routingGraph.lutRoutethru && rnode.getIntentCode() == IntentCode.NODE_PINFEED); if (!routingGraph.isAccessible(childRNode, connection)) { continue; @@ -1837,14 +1904,16 @@ private void exploreAndExpand(ConnectionState state, RouteNode rnode) { continue; } break; - case EXCLUSIVE_SINK: + case EXCLUSIVE_SINK_BOTH: case EXCLUSIVE_SINK_EAST: case EXCLUSIVE_SINK_WEST: assert(childRNode.getType() != RouteNodeType.EXCLUSIVE_SINK_EAST || rnode.getType() == RouteNodeType.LOCAL_EAST); assert(childRNode.getType() != RouteNodeType.EXCLUSIVE_SINK_WEST || rnode.getType() == RouteNodeType.LOCAL_WEST); - assert(childRNode.getType() != RouteNodeType.EXCLUSIVE_SINK || rnode.getType() == RouteNodeType.LOCAL || - // FIXME: - design.getSeries() == Series.Versal); + assert(childRNode.getType() != RouteNodeType.EXCLUSIVE_SINK_BOTH || rnode.getType() == RouteNodeType.LOCAL_BOTH || + // [BC]NODEs are LOCAL_{EAST,WEST} since they connect to INODEs, but also service CTRL sinks + (routingGraph.isVersal && EnumSet.of(IntentCode.NODE_CLE_BNODE, IntentCode.NODE_CLE_CNODE, + IntentCode.NODE_INTF_BNODE, IntentCode.NODE_INTF_CNODE) + .contains(rnode.getIntentCode()))); if (!isAccessibleSink(childRNode, connection)) { continue; } @@ -1889,7 +1958,7 @@ protected boolean isAccessibleSink(RouteNode child, Connection connection) { } protected boolean isAccessibleSink(RouteNode child, Connection connection, boolean assertOnOveruse) { - assert(child.getType().isExclusiveSink()); + assert(child.getType().isAnyExclusiveSink()); assert(!assertOnOveruse || !child.isOverUsed()); if (child.isTarget()) { @@ -1952,25 +2021,18 @@ protected void evaluateCostAndPush(ConnectionState state, RouteNode rnode, boole // Account for any detours that must be taken to get to and back from the closest Laguna column int nextLagunaColumn = routingGraph.nextLagunaColumn[childX]; int prevLagunaColumn = routingGraph.prevLagunaColumn[childX]; - int nextLagunaColumnDist = Math.abs(nextLagunaColumn - childX); - int prevLagunaColumnDist = Math.abs(prevLagunaColumn - childX); - if (sinkX >= childX) { - if (nextLagunaColumnDist <= prevLagunaColumnDist || prevLagunaColumn == Integer.MIN_VALUE) { - assert (nextLagunaColumn != Integer.MAX_VALUE); - deltaX = Math.abs(nextLagunaColumn - childX) + Math.abs(nextLagunaColumn - sinkX); - } else { - deltaX = Math.abs(childX - prevLagunaColumn) + Math.abs(sinkX - prevLagunaColumn); - } - } else { // childX > sinkX - if (prevLagunaColumnDist <= nextLagunaColumnDist) { - assert (prevLagunaColumn != Integer.MIN_VALUE); - deltaX = Math.abs(childX - prevLagunaColumn) + Math.abs(sinkX - prevLagunaColumn); - } else { - deltaX = Math.abs(nextLagunaColumn - childX) + Math.abs(nextLagunaColumn - sinkX); - } + int nextLagunaColumnDeltaX = (nextLagunaColumn == Integer.MAX_VALUE) ? Integer.MAX_VALUE : + Math.abs(nextLagunaColumn - childX) + Math.abs(sinkX - nextLagunaColumn); + int prevLagunaColumnDeltaX = (prevLagunaColumn == Integer.MIN_VALUE) ? Integer.MAX_VALUE : + Math.abs(prevLagunaColumn - childX) + Math.abs(sinkX - prevLagunaColumn); + if (nextLagunaColumnDeltaX <= prevLagunaColumnDeltaX) { + assert(deltaX <= nextLagunaColumnDeltaX); + deltaX = nextLagunaColumnDeltaX; + } else { + assert(deltaX <= prevLagunaColumnDeltaX); + deltaX = prevLagunaColumnDeltaX; } - - assert(deltaX >= 0); + assert(deltaX >= 0 && deltaX < Integer.MAX_VALUE); } } @@ -1992,25 +2054,27 @@ protected void evaluateCostAndPush(ConnectionState state, RouteNode rnode, boole * @return The sum of the congestion cost and the bias cost of rnode. */ private float getNodeCost(RouteNode rnode, Connection connection, int countSameSourceUsers, float sharingFactor) { - boolean hasSameSourceUsers = countSameSourceUsers!= 0; + boolean hasSameSourceUsers = (countSameSourceUsers != 0); float presentCongestionCost; if (hasSameSourceUsers) {// the rnode is used by other connection(s) from the same net - int overoccupancy = rnode.getOccupancy() - RouteNode.capacity; + int occupancyWithoutThisNet = rnode.getOccupancy() - 1; // make the congestion cost less for the current connection - presentCongestionCost = 1 + overoccupancy * presentCongestionFactor; + presentCongestionCost = routingGraph.getPresentCongestionCost(occupancyWithoutThisNet); } else { - presentCongestionCost = rnode.getPresentCongestionCost(); + presentCongestionCost = rnode.getPresentCongestionCost(routingGraph); } + float baseCost = rnode.getBaseCost(); float biasCost = 0; if (!rnode.isTarget() && rnode.getType() != RouteNodeType.SUPER_LONG_LINE) { NetWrapper net = connection.getNetWrapper(); - biasCost = rnode.getBaseCost() / net.getConnections().size() * - (Math.abs(rnode.getEndTileXCoordinate() - net.getXCenter()) + Math.abs(rnode.getEndTileYCoordinate() - net.getYCenter())) / net.getDoubleHpwl(); + float distToCenter = Math.abs(rnode.getEndTileXCoordinate() - net.getXCenter()) + + Math.abs(rnode.getEndTileYCoordinate() - net.getYCenter()); + biasCost = baseCost / net.getConnections().size() * distToCenter / net.getDoubleHpwl(); } - return rnode.getBaseCost() * rnode.getHistoricalCongestionCost() * presentCongestionCost / sharingFactor + biasCost; + return baseCost * rnode.getHistoricalCongestionCost() * presentCongestionCost / sharingFactor + biasCost; } /** @@ -2217,7 +2281,7 @@ public static Design routeDesignWithUserDefinedArguments(Design design, String[] } private static Design routeDesign(Design design, RWRouteConfig config) { - if (!config.isMaskNodesCrossRCLK()) { + if (config.isTimingDriven() && !config.isMaskNodesCrossRCLK()) { System.out.println("WARNING: Not masking nodes across RCLK could result in delay optimism."); } diff --git a/src/com/xilinx/rapidwright/rwroute/RouteNode.java b/src/com/xilinx/rapidwright/rwroute/RouteNode.java index 8f8f78d12..a5fb5af8c 100644 --- a/src/com/xilinx/rapidwright/rwroute/RouteNode.java +++ b/src/com/xilinx/rapidwright/rwroute/RouteNode.java @@ -33,6 +33,7 @@ import com.xilinx.rapidwright.util.RuntimeTracker; import java.util.ArrayList; +import java.util.EnumSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; @@ -51,7 +52,7 @@ public class RouteNode extends Node implements Comparable { public static final int initialHistoricalCongestionCost = 1; /** The type of a rnode*/ - private RouteNodeType type; + private byte type; /** The tileXCoordinate and tileYCoordinate of the INT tile that the associated node stops at */ private final short endTileXCoordinate; private final short endTileYCoordinate; @@ -64,8 +65,6 @@ public class RouteNode extends Node implements Comparable { /** The children (downhill rnodes) of this rnode */ protected RouteNode[] children; - /** Present congestion cost */ - private float presentCongestionCost; /** Historical congestion cost */ private float historicalCongestionCost; /** Upstream path cost */ @@ -84,26 +83,18 @@ public class RouteNode extends Node implements Comparable { * The number is used for the sharing mechanism of RWRoute. */ private Map usersConnectionCounts; - /** - * A map that records all the driver rnodes of a rnode based on all routed connections. - * It is possible that a rnode are driven by different rnodes after routing of all connections of a net. - * We count the drivers of a rnode to facilitate the route fixer at the end of routing. - */ - private Map driversCounts; protected RouteNode(RouteNodeGraph routingGraph, Node node, RouteNodeType type) { super(node); RouteNodeInfo nodeInfo = RouteNodeInfo.get(node, routingGraph); - this.type = (type == null) ? nodeInfo.type : type; + this.type = (byte) ((type == null) ? nodeInfo.type : type).ordinal(); endTileXCoordinate = nodeInfo.endTileXCoordinate; endTileYCoordinate = nodeInfo.endTileYCoordinate; length = nodeInfo.length; children = null; setBaseCost(routingGraph.design.getSeries()); - presentCongestionCost = initialPresentCongestionCost; historicalCongestionCost = initialHistoricalCongestionCost; usersConnectionCounts = null; - driversCounts = null; visited = 0; assert(prev == null); assert(!isTarget); @@ -118,27 +109,29 @@ public int compareTo(RouteNode that) { private void setBaseCost(Series series) { baseCost = 0.4f; - switch (type) { + switch (getType()) { case EXCLUSIVE_SOURCE: assert(length == 0 || - (length <= 3 && series == Series.Versal)); + (length <= 3 && (getIntentCode() == IntentCode.NODE_INTF2 || getIntentCode() == IntentCode.NODE_INTF4))); break; - case EXCLUSIVE_SINK: + case EXCLUSIVE_SINK_BOTH: case EXCLUSIVE_SINK_EAST: case EXCLUSIVE_SINK_WEST: assert(length == 0 || (length == 1 && (series == Series.UltraScalePlus || series == Series.UltraScale) && getIntentCode() == IntentCode.NODE_PINBOUNCE)); break; - case LOCAL: + case LOCAL_BOTH: assert(length == 0); break; case LOCAL_EAST: case LOCAL_WEST: + case LOCAL_RESERVED: assert(length == 0 || (length == 1 && ( ((series == Series.UltraScalePlus || series == Series.UltraScale) && getIntentCode() == IntentCode.NODE_PINBOUNCE) || (series == Series.UltraScalePlus && getWireName().matches("INODE_[EW]_\\d+_FT[01]")) || - (series == Series.UltraScale && getWireName().matches("INODE_[12]_[EW]_\\d+_FT[NS]")) + (series == Series.UltraScale && getWireName().matches("INODE_[12]_[EW]_\\d+_FT[NS]")) || + (series == Series.Versal && EnumSet.of(IntentCode.NODE_CLE_BNODE, IntentCode.NODE_CLE_CNODE).contains(getIntentCode())) )) ); break; @@ -148,12 +141,13 @@ private void setBaseCost(Series series) { // (since RWroute.getNodeCost() would return zero) but doing it here should be // okay because this node only leads to a SLL which will have a non-zero base cost baseCost = 0.0f; - break; + return; case SUPER_LONG_LINE: - assert(length == RouteNodeGraph.SUPER_LONG_LINE_LENGTH_IN_TILES); - baseCost = 0.3f * length; + assert(getLength() == RouteNodeGraph.SUPER_LONG_LINE_LENGTH_IN_TILES); + baseCost = 0.3f * RouteNodeGraph.SUPER_LONG_LINE_LENGTH_IN_TILES; break; case NON_LOCAL: + short length = getLength(); // NOTE: IntentCode is device-dependent IntentCode ic = getIntentCode(); switch(ic) { @@ -172,45 +166,76 @@ private void setBaseCost(Series series) { case NODE_VSINGLE: // Versal-only case NODE_HSINGLE: // Versal-only case NODE_SINGLE: // US and US+ - assert(length <= 2); - if (length == 2) baseCost *= length; + if (length <= 1) { + assert(!getAllDownhillPIPs().isEmpty()); + } else { + assert(length == 2); + baseCost *= length; + } break; case NODE_VDOUBLE: // Versal only case NODE_HDOUBLE: // Versal only case NODE_DOUBLE: // US and US+ - if (endTileXCoordinate != getTile().getTileXCoordinate()) { - assert(length <= 2); - // Typically, length = 1 (since tile X is not equal) - // In US, have seen length = 2, e.g. VU440's INT_X171Y827/EE2_E_BEG7. - if (length == 2) baseCost *= length; + if (length == 0) { + assert(!getAllDownhillPIPs().isEmpty()); } else { - // Typically, length = 2 except for horizontal U-turns (length = 0) - // or vertical U-turns (length = 1). - // In US, have seen length = 3, e.g. VU440's INT_X171Y827/NN2_E_BEG7. - assert(length <= 3); + if (getBeginTileXCoordinate() != getEndTileXCoordinate()) { + assert(length <= 2); + // Typically, length = 1 (since tile X is not equal) + // In US, have seen length = 2, e.g. VU440's INT_X171Y827/EE2_E_BEG7. + if (length == 2) { + baseCost *= length; + } + } else { + // Typically, length = 2 except for horizontal U-turns (length = 0) + // or vertical U-turns (length = 1). + // In US, have seen length = 3, e.g. VU440's INT_X171Y827/NN2_E_BEG7. + assert(length <= 3); + } } break; case NODE_HQUAD: - assert (length != 0 || getAllDownhillNodes().isEmpty()); - baseCost = 0.35f * length; + if (length == 0) { + // Since this node has zero length (and by extension no downhill PIPs) + // mark it as being inacccessible so that it will never be queued + assert(getAllDownhillPIPs().isEmpty()); + type = (byte) RouteNodeType.INACCESSIBLE.ordinal(); + } else { + baseCost = 0.35f * length; + } break; case NODE_VQUAD: - // In case of U-turn nodes - if (length != 0) baseCost = 0.15f * length;// VQUADs have length 4 and 5 + if (length == 0) { + assert(!getAllDownhillPIPs().isEmpty()); + } else { + // VQUADs have length 4 and 5 + baseCost = 0.15f * length; + } break; + case NODE_HLONG6: // Versal only case NODE_HLONG10: // Versal only baseCost = 0.15f * (length == 0 ? 1 : length); break; case NODE_HLONG: // US/US+ - assert (length != 0 || getAllDownhillNodes().isEmpty()); - baseCost = 0.15f * length;// HLONGs have length 6 and 7 + if (length == 0) { + // Since this node has zero length (and by extension no downhill PIPs) + // mark it as being inacccessible so that it will never be queued + assert(getAllDownhillPIPs().isEmpty()); + type = (byte) RouteNodeType.INACCESSIBLE.ordinal(); + } else { + // HLONGs have length 6 and 7 + baseCost = 0.15f * length; + } break; case NODE_VLONG7: // Versal only case NODE_VLONG12: // Versal only baseCost = 0.15f * (length == 0 ? 1 : length); break; case NODE_VLONG: // US/US+ + if (length == 0) { + assert(!getAllDownhillPIPs().isEmpty()); + } baseCost = 0.7f; break; @@ -221,27 +246,14 @@ private void setBaseCost(Series series) { // Feedthrough nodes to reach tiles immediately above/below (length == 1 && getWireName().matches("OUT_[NESW]NODE_[EW]_\\d+"))); break; - case NODE_INODE: // INT.INT_NODE_IMUX_ATOM_*_INT_OUT[01] - case NODE_IMUX: // INT.IMUX_B_[EW]* - case NODE_CLE_CTRL: // CLE_BC_CORE*.CTRL_[LR]_B* - case NODE_INTF_CTRL: // INTF_[LR]OCF_[TB][LR]_TILE.INTF_IRI* - assert(length == 0); - break; - case NODE_CLE_BNODE: // CLE_BC_CORE*.BNODE_OUTS_[EW]* - case NODE_CLE_CNODE: // CLE_BC_CORE*.CNODE_OUTS_[EW]* - case NODE_INTF_BNODE: // INTF_[LR]OCF_[TB][LR]_TILE.IF_INT_BNODE_OUTS* - case NODE_INTF_CNODE: // INTF_[LR]OCF_[TB][LR]_TILE.IF_INT_CNODE_OUTS* - // length == 1 because one side of BCNODE-s are shared between CLE_W_CORE_XaYb and CLE_E_CORE_X(a+1)Yb - // or CLE_W_CORE_X(a-1)Yb and CLE_E_CORE_XaYb - assert(length <= 1); - break; default: throw new RuntimeException(ic.toString()); } break; default: - throw new RuntimeException(type.toString()); + throw new RuntimeException(getType().toString()); } + assert(baseCost > 0); } /** @@ -265,33 +277,10 @@ public boolean isUsed() { return getOccupancy() > 0; } - /** - * Checks if a RouteNode Object are illegally driven by multiple drivers. - * @return true, if a RouteNode Object has multiple drivers. - */ - public boolean hasMultiDrivers() { - return RouteNode.capacity < uniqueDriverCount(); - } - public static short getLength(Node node) { return RouteNodeInfo.get(node, null).length; } - /** - * Updates the present congestion cost based on the present congestion penalty factor. - * @param pres_fac The present congestion penalty factor. - */ - public void updatePresentCongestionCost(float pres_fac) { - int occ = getOccupancy(); - int cap = RouteNode.capacity; - - if (occ < cap) { - setPresentCongestionCost(1); - } else { - setPresentCongestionCost(1 + (occ - cap + 1) * pres_fac); - } - } - @Override public String toString() { StringBuilder s = new StringBuilder(); @@ -299,7 +288,7 @@ public String toString() { s.append(", "); s.append("(" + endTileXCoordinate + "," + getEndTileYCoordinate() + ")"); s.append(", "); - s.append(String.format("type = %s", type)); + s.append(String.format("type = %s", getType())); s.append(", "); s.append(String.format("ic = %s", getIntentCode())); s.append(", "); @@ -315,7 +304,8 @@ public String toString() { * @return true, if coordinates of a RouteNode is within the connection's bounding box. */ public boolean isInConnectionBoundingBox(Connection connection) { - return endTileXCoordinate > connection.getXMinBB() && endTileXCoordinate < connection.getXMaxBB() && endTileYCoordinate > connection.getYMinBB() && endTileYCoordinate < connection.getYMaxBB(); + return endTileXCoordinate > connection.getXMinBB() && endTileXCoordinate < connection.getXMaxBB() && + endTileYCoordinate > connection.getYMinBB() && endTileYCoordinate < connection.getYMaxBB(); } /** @@ -343,7 +333,6 @@ public void markTarget(RWRoute.ConnectionState state) { state.targets.add(this); } - /* * Clears the target state on this node. */ @@ -357,7 +346,7 @@ public void clearTarget() { * @return The RouteNodeType of a RouteNode Object. */ public RouteNodeType getType() { - return type; + return RouteNodeType.values[type]; } /** @@ -365,13 +354,16 @@ public RouteNodeType getType() { * @param type New RouteNodeType value. */ public void setType(RouteNodeType type) { - assert(this.type == type || + assert(this.type == type.ordinal() || // Support demotion from EXCLUSIVE_SINK to LOCAL since they have the same base cost - (this.type.isExclusiveSink() && type == RouteNodeType.LOCAL) || + (RouteNodeType.isAnyExclusiveSink(this.type) && type.isAnyLocal()) || // Or promotion from LOCAL to EXCLUSIVE_SINK (by PartialRouter when NODE_PINBOUNCE on // a newly unpreserved net becomes a sink) - (this.type == RouteNodeType.LOCAL && type.isExclusiveSink())); - this.type = type; + (RouteNodeType.isAnyLocal(this.type) && type.isAnyExclusiveSink())) || + // Or promotion for any LOCAL to a LOCAL_RESERVED (by determineRoutingTargets() + // for uphills of CTRL sinks) + (RouteNodeType.isAnyLocal(this.type) && type == RouteNodeType.LOCAL_RESERVED); + this.type = (byte) type.ordinal(); } /** @@ -421,6 +413,7 @@ public short getEndTileYCoordinate() { * @return The base cost of a RouteNode Object. */ public float getBaseCost() { + assert(getType() != RouteNodeType.EXCLUSIVE_SOURCE); return baseCost; } @@ -439,7 +432,9 @@ public RouteNode[] getChildren(RouteNodeGraph routingGraph) { } RouteNode child = routingGraph.getOrCreate(downhill); - childrenList.add(child);//the sink rnode of a target connection has been created up-front + if (child.getType() != RouteNodeType.INACCESSIBLE) { + childrenList.add(child); + } } if (!childrenList.isEmpty()) { children = childrenList.toArray(EMPTY_ARRAY); @@ -560,36 +555,6 @@ public int countConnectionsOfUser(NetWrapper user) { return usersConnectionCounts.getOrDefault(user, 0); } - /** - * Gets the number of unique drivers. - * @return The number of unique drivers of a rnode, i.e., the key set size of the driver map - */ - public int uniqueDriverCount() { - if (driversCounts == null) { - return 0; - } - return driversCounts.size(); - } - - /** - * Adds a driver to the driver map. - * @param parent The driver to be added. - */ - public void incrementDriver(RouteNode parent) { - if (driversCounts == null) { - driversCounts = new IdentityHashMap<>(); - } - driversCounts.merge(parent, 1, Integer::sum); - } - - /** - * Decrements the driver count of a RouteNode instance. - * @param parent The driver that should have its count reduced by 1. - */ - public void decrementDriver(RouteNode parent) { - driversCounts.compute(parent, (k,v) -> (v == 1) ? null : v - 1); - } - /** * Gets the number of users. * @return The number of users. @@ -619,16 +584,8 @@ public void setPrev(RouteNode prev) { * Gets the present congestion cost of a RouteNode Object. * @return The present congestion of a RouteNode Object. */ - public float getPresentCongestionCost() { - return presentCongestionCost; - } - - /** - * Sets the present congestion cost of a RouteNode Object. - * @param presentCongestionCost The present congestion cost to be set. - */ - public void setPresentCongestionCost(float presentCongestionCost) { - this.presentCongestionCost = presentCongestionCost; + public float getPresentCongestionCost(RouteNodeGraph routingGraph) { + return routingGraph.getPresentCongestionCost(getOccupancy()); } /** @@ -674,11 +631,11 @@ public int getVisited() { /** * Mark a RouteNode instance as being visited by a specific integer identifier. - * @param id Integer identifier. + * @param seq Integer identifier. */ - public void setVisited(int id) { - assert(id > 0); - visited = id; + public void setVisited(int seq) { + assert(seq > 0); + visited = seq; } /** diff --git a/src/com/xilinx/rapidwright/rwroute/RouteNodeGraph.java b/src/com/xilinx/rapidwright/rwroute/RouteNodeGraph.java index ece65969e..24d8c9e2b 100644 --- a/src/com/xilinx/rapidwright/rwroute/RouteNodeGraph.java +++ b/src/com/xilinx/rapidwright/rwroute/RouteNodeGraph.java @@ -27,7 +27,6 @@ import java.util.BitSet; import java.util.EnumMap; import java.util.EnumSet; -import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; @@ -35,6 +34,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.function.BiConsumer; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -62,15 +63,17 @@ public class RouteNodeGraph { protected final Design design; /** - * A map of nodes to created rnodes + * A map of nodes to created rnodes. Assume that only a single + * thread will operate on each tile (first dimension) simultaneously + * (so no need for AtomicReferenceArray) */ - protected final Map nodesMap; + protected final RouteNode[][] nodesMap; private final AtomicInteger nodesMapSize; /** * A map of preserved nodes to their nets */ - private final Map preservedMap; + private final AtomicReferenceArray preservedMap; private final AtomicInteger preservedMapSize; /** @@ -88,7 +91,7 @@ public class RouteNodeGraph { public final int[] nextLagunaColumn; public final int[] prevLagunaColumn; - /** + /** * Map indicating which wire indices within a Laguna-adjacent INT tile have * IntentCode.NODE_PINFEED that lead into the Laguna tile. */ @@ -102,28 +105,39 @@ public class RouteNodeGraph { /** Flag for whether LUT routethrus are to be considered */ protected final boolean lutRoutethru; - /** Map indicating (for UltraScale/UltraScale+ only) the wire indices that have a local intent code, - * but is what RWRoute will consider to be non-local, e.g. INT_NODE_SDQ_* + /** Map indicating (for UltraScale/UltraScale+ only) the subset wire indices of a NODE_LOCAL that are + * what RWRoute should assign a LOCAL_* type, e.g. excluding INT_NODE_SDQ_* */ - protected final Map ultraScalesNonLocalWires; + protected final Map ultraScalesLocalWires; /** Map indicating the wire indices corresponding to the east/west side of interconnect tiles */ protected final Map eastWestWires; - public RouteNodeGraph(Design design, RWRouteConfig config) { - this(design, config, new HashMap<>()); + /** Flag for whether design targets the Versal series */ + protected final boolean isVersal; + + protected final Map baseWireCounts; + + protected final static int MAX_OCCUPANCY = 256; + protected final float[] presentCongestionCosts; + + protected static int getTileCount(Design design) { + Device device = design.getDevice(); + return device.getColumns() * device.getRows(); } - protected RouteNodeGraph(Design design, RWRouteConfig config, Map nodesMap) { + + public RouteNodeGraph(Design design, RWRouteConfig config) { this.design = design; lutRoutethru = config.isLutRoutethru(); - this.nodesMap = nodesMap; + this.nodesMap = new RouteNode[getTileCount(design)][]; nodesMapSize = new AtomicInteger(); - preservedMap = new ConcurrentHashMap<>(); + preservedMap = new AtomicReferenceArray<>(getTileCount(design)); preservedMapSize = new AtomicInteger(); asyncPreserveOutstanding = new CountUpDownLatch(); createRnodeTime = 0; + baseWireCounts = new ConcurrentHashMap<>(); Device device = design.getDevice(); intYToSLRIndex = new int[device.getRows()]; @@ -141,30 +155,45 @@ protected RouteNodeGraph(Design design, RWRouteConfig config, Map intTileIntentCodeCareSet; Pattern eastWestPattern; - - Tile intTile = device.getArbitraryTileOfType(TileTypeEnum.INT); - // Device.getArbitraryTileOfType() typically gives you the North-Western-most - // tile (with minimum X, maximum Y). Analyze the tile just below that. - intTile = intTile.getTileXYNeighbor(0, -1); - - BitSet wires = new BitSet(); - BitSet eastWires = new BitSet(); - BitSet westWires = new BitSet(); + eastWestWires = new EnumMap<>(TileTypeEnum.class); + BitSet localWires = new BitSet(); if (isUltraScale || isUltraScalePlus) { - ultraScalesNonLocalWires = new EnumMap<>(TileTypeEnum.class); - ultraScalesNonLocalWires.put(intTile.getTileTypeEnum(), wires); - eastWestPattern = Pattern.compile("(((BOUNCE|BYPASS|IMUX|INODE(_[12])?)_([EW]))|INT_NODE_IMUX_(\\d+)_).*"); - - eastWestWires = new EnumMap<>(TileTypeEnum.class); - eastWestWires.put(intTile.getTileTypeEnum(), new BitSet[]{eastWires, westWires}); + intTile = device.getArbitraryTileOfType(TileTypeEnum.INT); + // Device.getArbitraryTileOfType() typically gives you the North-Western-most + // tile (with minimum X, maximum Y). Analyze the tile just below that. + intTile = intTile.getTileXYNeighbor(0, -1); + intTileIntentCodeCareSet = EnumSet.of( + IntentCode.NODE_PINFEED, + IntentCode.NODE_PINBOUNCE, + IntentCode.NODE_LOCAL); + + ultraScalesLocalWires = new EnumMap<>(TileTypeEnum.class); + ultraScalesLocalWires.put(intTile.getTileTypeEnum(), localWires); + + eastWestPattern = Pattern.compile("(((BOUNCE|BYPASS|IMUX|INODE(_[12])?)_(?[EW]))|INT_NODE_IMUX_(?\\d+)_).*"); } else { - assert(series == Series.Versal); - ultraScalesNonLocalWires = null; - - // FIXME: - eastWestPattern = null; - eastWestWires = null; + assert(isVersal); + + // Find an INT tile adjacent to a CLE_BC_CORE tile since Versal devices may contain AIEs on their northern edge + Tile bcCoreTile = device.getArbitraryTileOfType(TileTypeEnum.CLE_BC_CORE); + // Device.getArbitraryTileOfType() typically gives you the North-Western-most + // tile (with minimum X, maximum Y). Analyze the tile just below that. + intTile = bcCoreTile.getTileNeighbor(2, 0); + assert(intTile.getTileTypeEnum() == TileTypeEnum.INT); + intTileIntentCodeCareSet = EnumSet.of( + IntentCode.NODE_IMUX, + IntentCode.NODE_PINBOUNCE, + IntentCode.NODE_INODE, + IntentCode.NODE_CLE_BNODE, + IntentCode.NODE_CLE_CNODE); + + ultraScalesLocalWires = null; + + eastWestPattern = Pattern.compile("(((BOUNCE|IMUX_B|[BC]NODE_OUTS)_(?[EW]))|INT_NODE_IMUX_ATOM_(?\\d+)_).*"); } for (int wireIndex = 0; wireIndex < intTile.getWireCount(); wireIndex++) { @@ -173,74 +202,118 @@ protected RouteNodeGraph(Design design, RWRouteConfig config, Map new BitSet[]{new BitSet(), new BitSet()}); + BitSet eastWires = eastWestWires[0]; + BitSet westWires = eastWestWires[1]; + String ew = m.group("eastwest"); + String inode; + if (ew != null) { + // [BC]NODEs connect to INODEs opposite to their wire name + if (baseIntentCode == IntentCode.NODE_CLE_BNODE || baseIntentCode == IntentCode.NODE_CLE_CNODE) { + ew = ew.equals("E") ? "W" : "E"; } - } else if (m.group(6) != null) { - int i = Integer.valueOf(m.group(6)); - if (i < 32 || (isUltraScale && (i >= 64 && i < 96))) { + if (ew.equals("E")) { eastWires.set(baseNode.getWireIndex()); } else { - assert(i < 64 || (isUltraScale && (i >= 96 && i < 128))); + assert(ew.equals("W")); westWires.set(baseNode.getWireIndex()); } + } else { + if ((inode = m.group("inode")) != null) { + int i = Integer.valueOf(inode); + if (i < 32 || ((isUltraScale || isVersal) && i >= 64 && i < 96)) { + eastWires.set(baseNode.getWireIndex()); + } else { + assert(i < 64 || (isUltraScale || isVersal && i >= 96 && i < 128)); + westWires.set(baseNode.getWireIndex()); + } + } } } else { - assert(baseWireName.matches("CTRL_[EW](_B)?\\d+|INT_NODE_GLOBAL_\\d+(_INT)?_OUT[01]?") - // FIXME: - || series == Series.Versal); + assert((isUltraScale || isUltraScalePlus) && baseWireName.matches("CTRL_[EW](_B)?\\d+|INT_NODE_GLOBAL_\\d+(_INT)?_OUT[01]?")); } } + if (isVersal) { + // With NODE_CLE_[BC]NODEs being handled as part of the INT tile above, compute east/west wires + // in INTF_* tiles here + BiConsumer, Boolean> lambda = (types, east) -> { + for (TileTypeEnum tte : types) { + Tile intfTile = device.getArbitraryTileOfType(tte); + BitSet eastWestWires = this.eastWestWires.computeIfAbsent(tte, + k -> new BitSet[]{new BitSet(), new BitSet()})[east ? 0 : 1]; + for (int wireIndex = 0; wireIndex < intfTile.getWireCount(); wireIndex++) { + IntentCode baseIntentCode = intfTile.getWireIntentCode(wireIndex); + if (baseIntentCode != IntentCode.NODE_INTF_BNODE && baseIntentCode != IntentCode.NODE_INTF_CNODE) { + continue; + } + assert(Node.getNode(intfTile, wireIndex).getTile() == intfTile); + + eastWestWires.set(wireIndex); + } + } + }; + + lambda.accept(Arrays.asList( + TileTypeEnum.INTF_LOCF_TR_TILE, + TileTypeEnum.INTF_LOCF_BR_TILE, + TileTypeEnum.INTF_ROCF_TR_TILE, + TileTypeEnum.INTF_ROCF_BR_TILE), true); + lambda.accept(Arrays.asList( + TileTypeEnum.INTF_LOCF_TL_TILE, + TileTypeEnum.INTF_LOCF_BL_TILE, + TileTypeEnum.INTF_ROCF_TL_TILE, + TileTypeEnum.INTF_ROCF_BL_TILE), false); + } + if (lutRoutethru) { assert(isUltraScalePlus || isUltraScale); @@ -250,19 +323,19 @@ protected RouteNodeGraph(Design design, RWRouteConfig config, Map { + // Check all wires in tile to find the index of the last base wire + int lastBaseWire = startWireIndex; + for (int i = lastBaseWire + 1; i < tile.getWireCount(); i++) { + Node node = Node.getNode(tile, i); + if (node != null && node.getTile() == tile && node.getWireIndex() == i) { + lastBaseWire = i; + } + } + return lastBaseWire + 1; + }); + } + protected Net preserve(Node node, Net net) { Net oldNet = preserve(node.getTile(), node.getWireIndex(), net); if (oldNet == null) { @@ -351,7 +443,16 @@ protected Net preserve(Node node, Net net) { private Net preserve(Tile tile, int wireIndex, Net net) { // Assumes that tile/wireIndex describes the base wire on the node // No need to synchronize access to 'nets' since collisions are not expected - Net[] nets = preservedMap.computeIfAbsent(tile, (t) -> new Net[t.getWireCount()]); + int tileAddress = tile.getUniqueAddress(); + Net[] nets = preservedMap.get(tileAddress); + if (nets == null) { + int baseWireCount = getBaseWireCount(tile, wireIndex); + nets = new Net[baseWireCount]; + if (!preservedMap.compareAndSet(tileAddress, null, nets)) { + // Another thread must have beat us to a compareAndSet, use that result + nets = preservedMap.get(tileAddress); + } + } Net oldNet = nets[wireIndex]; // Do not clobber the old value if (oldNet == null) { @@ -376,7 +477,7 @@ public void preserve(Net net, List pins) { String pinName = pin.getName(); char lutLetter = pinName.charAt(0); String otherPinName = null; - String otherPinNameSuffix = design.getSeries() == Series.Versal ? "Q" : "MUX"; + String otherPinNameSuffix = isVersal ? "Q" : "MUX"; if (pinName.endsWith(otherPinNameSuffix)) { otherPinName = lutLetter + "_O"; } else if (pinName.endsWith("_O")) { @@ -432,7 +533,7 @@ public boolean unpreserve(Node node) { private boolean unpreserve(Tile tile, int wireIndex) { // Assumes that tile/wireIndex describes the base wire on its node - Net[] nets = preservedMap.get(tile); + Net[] nets = preservedMap.get(tile.getUniqueAddress()); if (nets == null || nets[wireIndex] == null) return false; nets[wireIndex] = null; @@ -442,7 +543,7 @@ private boolean unpreserve(Tile tile, int wireIndex) { public boolean isPreserved(Node node) { Tile tile = node.getTile(); int wireIndex = node.getWireIndex(); - Net[] nets = preservedMap.get(tile); + Net[] nets = preservedMap.get(tile.getUniqueAddress()); return nets != null && nets[wireIndex] != null; } @@ -481,24 +582,37 @@ protected boolean isExcluded(RouteNode parent, Node child) { } } - if (child.getIntentCode() == IntentCode.NODE_PINFEED) { - // PINFEEDs can lead to a site pin, or into a Laguna tile - RouteNode childRnode = getNode(child); - if (childRnode != null) { - assert(childRnode.getType().isExclusiveSink() || - childRnode.getType() == RouteNodeType.LAGUNA_PINFEED || - (lutRoutethru && childRnode.getType().isLocal())); - } else if (!lutRoutethru) { - // child does not already exist in our routing graph, meaning it's not a used site pin - // in our design, but it could be a LAGUNA_I - if (lagunaI == null) { - // No LAGUNA_Is - return true; - } - BitSet bs = lagunaI.get(child.getTile()); - if (bs == null || !bs.get(child.getWireIndex())) { - // Not a LAGUNA_I -- skip it - return true; + IntentCode ic = child.getIntentCode(); + if (isVersal) { + assert(ic != IntentCode.NODE_PINFEED); // This intent code should have been projected away + + if ((!lutRoutethru && ic == IntentCode.NODE_IMUX) || ic == IntentCode.NODE_CLE_CTRL || ic == IntentCode.NODE_INTF_CTRL) { + // Disallow these site pin projections if they aren't already in the routing graph (as a potential sink) + RouteNode childRnode = getNode(child); + return childRnode == null; + } + } else { + assert(design.getSeries() == Series.UltraScale || design.getSeries() == Series.UltraScalePlus); + + if (child.getIntentCode() == IntentCode.NODE_PINFEED) { + // PINFEEDs can lead to a site pin, or into a Laguna tile + RouteNode childRnode = getNode(child); + if (childRnode != null) { + assert(childRnode.getType().isAnyExclusiveSink() || + childRnode.getType() == RouteNodeType.LAGUNA_PINFEED || + (lutRoutethru && childRnode.getType().isAnyLocal())); + } else if (!lutRoutethru) { + // child does not already exist in our routing graph, meaning it's not a used site pin + // in our design, but it could be a LAGUNA_I + if (lagunaI == null) { + // No LAGUNA_Is + return true; + } + BitSet bs = lagunaI.get(child.getTile()); + if (bs == null || !bs.get(child.getWireIndex())) { + // Not a LAGUNA_I -- skip it + return true; + } } } } @@ -520,7 +634,7 @@ public Net getPreservedNet(Node node) { private Net getPreservedNet(Tile tile, int wireIndex) { // Assumes that tile/wireIndex describes the base wire on its node - Net[] nets = preservedMap.get(tile); + Net[] nets = preservedMap.get(tile.getUniqueAddress()); return nets != null ? nets[wireIndex] : null; } @@ -532,45 +646,68 @@ public RouteNode getNode(Node node) { private RouteNode getNode(Tile tile, int wireIndex) { // Assumes that tile/wireIndex describes the base wire on its node - RouteNode[] rnodes = nodesMap.get(tile); + RouteNode[] rnodes = nodesMap[tile.getUniqueAddress()]; return rnodes != null ? rnodes[wireIndex] : null; } public Iterable getRnodes() { return new Iterable() { - final Iterator> it = nodesMap.entrySet().iterator(); - RouteNode[] curr = it.hasNext() ? it.next().getValue() : null; - int index = 0; + int tileAddress = -1; // Start at -1 so that pre-increment advances + int wireIndex; + RouteNode[] curr; + int count = 0; + + private boolean findNextWireInNextTile() { + while(++tileAddress < nodesMap.length) { + curr = nodesMap[tileAddress]; + if (curr == null) { + continue; + } + wireIndex = -1; // Start at -1 so that pre-increment advances + if (findNextWireInSameTile()) { + return true; + } + } + assert(curr == null); + return false; + } + + private boolean findNextWireInSameTile() { + assert(curr != null); + assert(wireIndex < curr.length); + while(++wireIndex < curr.length) { + if (curr[wireIndex] != null) { + return true; + } + } + curr = null; + return false; + } @Override public Iterator iterator() { return new Iterator() { @Override public boolean hasNext() { - if (curr == null) { - return false; + if (curr != null && findNextWireInSameTile()) { + count++; + return true; } - while(true) { - while (index < curr.length) { - if (curr[index] != null) { - return true; - } - index++; - } - if (!it.hasNext()) { - return false; - } - curr = it.next().getValue(); - assert(curr != null); - index = 0; + assert(curr == null); + if (findNextWireInNextTile()) { + count++; + return true; } + assert(count == nodesMapSize.get()); + return false; } @Override public RouteNode next() { - hasNext(); - assert(curr[index] != null); - return curr[index++]; + assert(curr != null); + RouteNode routeNode = curr[wireIndex]; + assert(routeNode != null); + return routeNode; } }; } @@ -592,7 +729,13 @@ public RouteNode getOrCreate(Node node) { public RouteNode getOrCreate(Node node, RouteNodeType type) { Tile tile = node.getTile(); int wireIndex = node.getWireIndex(); - RouteNode[] rnodes = nodesMap.computeIfAbsent(tile, (t) -> new RouteNode[t.getWireCount()]); + int tileAddress = tile.getUniqueAddress(); + RouteNode[] rnodes = nodesMap[tileAddress]; + if (rnodes == null) { + int baseWireCount = getBaseWireCount(tile, wireIndex); + rnodes = new RouteNode[baseWireCount]; + nodesMap[tileAddress] = rnodes; + } RouteNode rnode = rnodes[wireIndex]; if (rnode == null) { rnode = create(node, type); @@ -613,7 +756,8 @@ public int averageChildren() { public boolean isAccessible(RouteNode childRnode, Connection connection) { // Only consider LOCAL nodes when: // (a) considering LUT routethrus - if (!childRnode.getType().isLocal() || lutRoutethru) { + RouteNodeType type = childRnode.getType(); + if (!type.isAnyLocal() || lutRoutethru) { return true; } @@ -628,30 +772,60 @@ public boolean isAccessible(RouteNode childRnode, Connection connection) { } // (c) on the same side as the sink - RouteNodeType type = childRnode.getType(); Tile sinkTile = sinkRnode.getTile(); switch (sinkRnode.getType()) { case EXCLUSIVE_SINK_EAST: - if (type == RouteNodeType.LOCAL_WEST) { + if (type == RouteNodeType.LOCAL_WEST || type == RouteNodeType.LOCAL_RESERVED) { // West wires can never reach an east sink return false; } break; case EXCLUSIVE_SINK_WEST: - if (type == RouteNodeType.LOCAL_EAST) { + if (type == RouteNodeType.LOCAL_EAST || type == RouteNodeType.LOCAL_RESERVED) { // East wires can never reach a west sink return false; } break; - case EXCLUSIVE_SINK: - // Only both-sided wires (e.g. INT_NODE_GLOBAL_*) can reach a both-sided sink (CTRL_*) - if (type != RouteNodeType.LOCAL) { - return false; - } - if (design.getSeries() == Series.UltraScale || design.getSeries() == Series.UltraScalePlus) { - // This must be a CTRL sink; these can only be accessed from the sink tile rather than Y +/- 1 below - return childTile == sinkTile; + case EXCLUSIVE_SINK_BOTH: + // This must be a CTRL sink that can be accessed from both east/west sides + + if (isVersal) { + assert(sinkRnode.getIntentCode() == IntentCode.NODE_CLE_CTRL || sinkRnode.getIntentCode() == IntentCode.NODE_INTF_CTRL); + + if (childTile == sinkTile) { + // CTRL sinks can be only accessed directly from LOCAL_RESERVED nodes in the sink CLE_BC_CORE/INTF_* tile ... + if (type != RouteNodeType.LOCAL_RESERVED) { + return false; + } + } else { + // ... or via LOCAL nodes in the two INT tiles either side + if (childTile.getTileYCoordinate() != sinkTile.getTileYCoordinate() || + Math.abs(childTile.getTileXCoordinate() - sinkTile.getTileXCoordinate()) > 1) { + return false; + } + if (childTile.getTileTypeEnum() != TileTypeEnum.INT) { + // e.g. CLE_BC_CORE_X50Y4 and CLE_BC_CORE_1_X50Y4 on xcvc1502 + return false; + } + // Allow use of INODE + PINBOUNCEs in the two INT tiles on either side of sink + assert(childRnode.getIntentCode() == IntentCode.NODE_INODE || childRnode.getIntentCode() == IntentCode.NODE_PINBOUNCE); + return true; + } + } else { + assert(design.getSeries() == Series.UltraScale || design.getSeries() == Series.UltraScalePlus); + assert(sinkRnode.getWireName().startsWith("CTRL_")); + + // CTRL sinks can only be accessed from LOCAL nodes in the sink tile (rather than Y +/- 1 below) + if (childTile != sinkTile) { + return false; + } + + // Only both-sided wires (e.g. INT_NODE_GLOBAL_*) can reach a both-sided sink (CTRL_*) + if (type != RouteNodeType.LOCAL_BOTH) { + return false; + } } + assert(childTile == sinkTile); break; default: throw new RuntimeException("ERROR: Unexpected sink type " + sinkRnode.getType()); @@ -662,6 +836,41 @@ public boolean isAccessible(RouteNode childRnode, Connection connection) { return true; } + if (isVersal) { + assert(sinkRnode.getType() != RouteNodeType.EXCLUSIVE_SINK_BOTH); + assert(sinkRnode.getIntentCode() == IntentCode.NODE_IMUX || sinkRnode.getIntentCode() == IntentCode.NODE_PINBOUNCE); + + IntentCode childIntentCode = childRnode.getIntentCode(); + switch (childIntentCode) { + case NODE_INODE: + // Block access to all INODEs outside the sink tile, since NODE_INODE -> NODE_IMUX -> NODE_PINFEED (or NODE_INODE -> NODE_PINBOUNCE) + assert(childTile != sinkTile); + return false; + case NODE_CLE_BNODE: + case NODE_INTF_BNODE: + case NODE_CLE_CNODE: + case NODE_INTF_CNODE: + // Only allow [BC]NODEs that reach into the sink tile + return childTile.getTileYCoordinate() == sinkTile.getTileYCoordinate() && + childRnode.getEndTileXCoordinate() == sinkTile.getTileXCoordinate(); + case NODE_PINBOUNCE: + // BOUNCEs are only accessible through INODEs, so transitively this intent code is unreachable + break; + case NODE_IMUX: + // IMUXes that are not our target EXCLUSIVE_SINK will have been isExcluded() from the graph unless LUT routethrus are enabled + assert(lutRoutethru); + break; + case NODE_PINFEED: + // Expected to be projected away + break; + case NODE_CLE_CTRL: + case NODE_INTF_CTRL: + // CTRL pins that are not our target EXCLUSIVE_SINK will have been isExcluded() from the graph + break; + } + throw new RuntimeException("ERROR: Unhandled IntentCode: " + childIntentCode); + } + // (e) when in same X as the sink tile, but Y +/- 1 return childX == sinkTile.getTileXCoordinate() && Math.abs(childTile.getTileYCoordinate() - sinkTile.getTileYCoordinate()) <= 1; @@ -672,6 +881,17 @@ protected boolean allowRoutethru(Node head, Node tail) { return false; } + if (tail.getIntentCode() == IntentCode.NODE_PINFEED) { + assert(isVersal); + assert(!lutRoutethru); + assert(head.getIntentCode() == IntentCode.NODE_IMUX || + head.getIntentCode() == IntentCode.NODE_PINBOUNCE); + return false; + } + + // Should not get to this point unless LUT routethru-s are enabled + assert(lutRoutethru); + if (!RouteThruHelper.isRouteThruPIPAvailable(design, head, tail)) { return false; } @@ -685,4 +905,19 @@ protected boolean allowRoutethru(Node head, Node tail) { } return true; } + + public void updatePresentCongestionCosts(float presentCongestionFactor) { + for (int occupancy = 0; occupancy < presentCongestionCosts.length; occupancy++) { + int overuse = occupancy - RouteNode.capacity; + if (overuse < 0) { + presentCongestionCosts[occupancy] = RouteNode.initialPresentCongestionCost; + } else { + presentCongestionCosts[occupancy] = RouteNode.initialPresentCongestionCost + (overuse + 1) * presentCongestionFactor; + } + } + } + + public float getPresentCongestionCost(int occupancy) { + return presentCongestionCosts[occupancy]; + } } diff --git a/src/com/xilinx/rapidwright/rwroute/RouteNodeGraphTimingDriven.java b/src/com/xilinx/rapidwright/rwroute/RouteNodeGraphTimingDriven.java index 74474a436..4e75d051d 100644 --- a/src/com/xilinx/rapidwright/rwroute/RouteNodeGraphTimingDriven.java +++ b/src/com/xilinx/rapidwright/rwroute/RouteNodeGraphTimingDriven.java @@ -70,17 +70,10 @@ public class RouteNodeGraphTimingDriven extends RouteNodeGraph { }}; } - public RouteNodeGraphTimingDriven(Design design, - RWRouteConfig config, - DelayEstimatorBase delayEstimator) { - this(design, config, delayEstimator, new HashMap<>()); - } - protected RouteNodeGraphTimingDriven(Design design, RWRouteConfig config, - DelayEstimatorBase delayEstimator, - Map nodesMap) { - super(design, config, nodesMap); + DelayEstimatorBase delayEstimator) { + super(design, config); this.delayEstimator = delayEstimator; this.maskNodesCrossRCLK = config.isMaskNodesCrossRCLK(); diff --git a/src/com/xilinx/rapidwright/rwroute/RouteNodeInfo.java b/src/com/xilinx/rapidwright/rwroute/RouteNodeInfo.java index 2387dd745..2b06ce855 100644 --- a/src/com/xilinx/rapidwright/rwroute/RouteNodeInfo.java +++ b/src/com/xilinx/rapidwright/rwroute/RouteNodeInfo.java @@ -24,7 +24,6 @@ import com.xilinx.rapidwright.device.IntentCode; import com.xilinx.rapidwright.device.Node; -import com.xilinx.rapidwright.device.Series; import com.xilinx.rapidwright.device.Tile; import com.xilinx.rapidwright.device.TileTypeEnum; import com.xilinx.rapidwright.device.Wire; @@ -66,7 +65,8 @@ public static RouteNodeInfo get(Node node, RouteNodeGraph routingGraph) { endTile = node.getTile(); } - RouteNodeType type = getType(node, endTile, routingGraph); + boolean forceSink = false; + RouteNodeType type = getType(node, endTile, routingGraph, forceSink); short endTileXCoordinate = getEndTileXCoordinate(node, type, (short) endTile.getTileXCoordinate()); short endTileYCoordinate = (short) endTile.getTileYCoordinate(); short length = getLength(baseTile, type, endTileXCoordinate, endTileYCoordinate); @@ -126,7 +126,7 @@ private static short getEndTileXCoordinate(Node node, RouteNodeType type, short return endTileXCoordinate; } - private static RouteNodeType getType(Node node, Tile endTile, RouteNodeGraph routingGraph) { + public static RouteNodeType getType(Node node, Tile endTile, RouteNodeGraph routingGraph, boolean forceSink) { // NOTE: IntentCode is device-dependent IntentCode ic = node.getIntentCode(); TileTypeEnum tileTypeEnum = node.getTile().getTileTypeEnum(); @@ -134,22 +134,25 @@ private static RouteNodeType getType(Node node, Tile endTile, RouteNodeGraph rou case NODE_LOCAL: { // US/US+ assert(tileTypeEnum == TileTypeEnum.INT); if (routingGraph != null) { - BitSet bs = routingGraph.ultraScalesNonLocalWires.get(tileTypeEnum); + BitSet bs = routingGraph.ultraScalesLocalWires.get(tileTypeEnum); if (!bs.get(node.getWireIndex())) { - BitSet[] eastWestWires = routingGraph.eastWestWires.get(tileTypeEnum); - if (eastWestWires[0].get(node.getWireIndex())) { - return RouteNodeType.LOCAL_EAST; - } else if (eastWestWires[1].get(node.getWireIndex())) { - return RouteNodeType.LOCAL_WEST; - } - return RouteNodeType.LOCAL; + break; } - break; + BitSet[] eastWestWires = routingGraph.eastWestWires.get(tileTypeEnum); + if (eastWestWires[0].get(node.getWireIndex())) { + return RouteNodeType.LOCAL_EAST; + } else if (eastWestWires[1].get(node.getWireIndex())) { + return RouteNodeType.LOCAL_WEST; + } + return RouteNodeType.LOCAL_BOTH; } } case NODE_PINFEED: - if (routingGraph != null && routingGraph.lagunaI != null) { + if (routingGraph == null || routingGraph.isVersal) { + return RouteNodeType.LOCAL_BOTH; + } + if (routingGraph.lagunaI != null && !forceSink) { BitSet bs = routingGraph.lagunaI.get(node.getTile()); if (bs != null && bs.get(node.getWireIndex())) { return RouteNodeType.LAGUNA_PINFEED; @@ -157,18 +160,27 @@ private static RouteNodeType getType(Node node, Tile endTile, RouteNodeGraph rou } // Fall through case NODE_PINBOUNCE: - if (routingGraph != null && routingGraph.eastWestWires != null) { + case NODE_INODE: // INT.INT_NODE_IMUX_ATOM_*_INT_OUT[01] (Versal only) + case NODE_IMUX: // INT.IMUX_B_[EW]* (Versal only) + case NODE_CLE_CNODE: // CLE_BC_CORE*.CNODE_OUTS_[EW]* (Versal only) + case NODE_CLE_BNODE: // CLE_BC_CORE*.BNODE_OUTS_[EW]* (Versal only) + case NODE_INTF_BNODE: // INTF_[LR]OCF_[TB][LR]_TILE.IF_INT_BNODE_OUTS* (Versal only) + case NODE_INTF_CNODE: // INTF_[LR]OCF_[TB][LR]_TILE.IF_INT_CNODE_OUTS* (Versal only) + if (routingGraph != null) { BitSet[] eastWestWires = routingGraph.eastWestWires.get(tileTypeEnum); if (eastWestWires[0].get(node.getWireIndex())) { return RouteNodeType.LOCAL_EAST; } else if (eastWestWires[1].get(node.getWireIndex())) { return RouteNodeType.LOCAL_WEST; } - assert(node.getWireName().startsWith("CTRL_") || - // FIXME: - routingGraph.design.getSeries() == Series.Versal); + assert(!routingGraph.isVersal && node.getWireName().startsWith("CTRL_")); } - return RouteNodeType.LOCAL; + return RouteNodeType.LOCAL_BOTH; + + // Versal only + case NODE_CLE_CTRL: // CLE_BC_CORE*.CTRL_[LR]_B* + case NODE_INTF_CTRL: // INTF_[LR]OCF_[TB][LR]_TILE.INTF_IRI* + return RouteNodeType.LOCAL_BOTH; case NODE_LAGUNA_OUTPUT: // UltraScale+ only assert(tileTypeEnum == TileTypeEnum.LAG_LAG); @@ -179,6 +191,7 @@ private static RouteNodeType getType(Node node, Tile endTile, RouteNodeGraph rou case NODE_LAGUNA_DATA: // UltraScale+ only assert(tileTypeEnum == TileTypeEnum.LAG_LAG); + assert(endTile != null); if (node.getTile() != endTile) { return RouteNodeType.SUPER_LONG_LINE; } @@ -190,6 +203,7 @@ private static RouteNodeType getType(Node node, Tile endTile, RouteNodeGraph rou if (tileTypeEnum == TileTypeEnum.LAGUNA_TILE) { // UltraScale only String wireName = node.getWireName(); if (wireName.startsWith("UBUMP")) { + assert(endTile != null); assert(node.getTile() != endTile); return RouteNodeType.SUPER_LONG_LINE; } else if (wireName.endsWith("_TXOUT")) { diff --git a/src/com/xilinx/rapidwright/rwroute/RouteNodeType.java b/src/com/xilinx/rapidwright/rwroute/RouteNodeType.java index 0c024bb0b..9f5290465 100644 --- a/src/com/xilinx/rapidwright/rwroute/RouteNodeType.java +++ b/src/com/xilinx/rapidwright/rwroute/RouteNodeType.java @@ -28,7 +28,7 @@ public enum RouteNodeType { EXCLUSIVE_SOURCE, - EXCLUSIVE_SINK, + EXCLUSIVE_SINK_BOTH, EXCLUSIVE_SINK_EAST, EXCLUSIVE_SINK_WEST, @@ -46,15 +46,34 @@ public enum RouteNodeType { NON_LOCAL, - LOCAL, + LOCAL_BOTH, LOCAL_EAST, - LOCAL_WEST; + LOCAL_WEST, - public boolean isExclusiveSink() { - return this == EXCLUSIVE_SINK || this == EXCLUSIVE_SINK_EAST || this == EXCLUSIVE_SINK_WEST; + LOCAL_RESERVED, + + /** + * Denotes {@link RouteNode} objects that should be treated as being inaccessible and + * never queued for exploration during routing. Typically, these are routing nodes that + * have already been created but later discovered to not be needed (e.g. is a dead-end node). + */ + INACCESSIBLE; + + public static final RouteNodeType[] values = values(); + + public boolean isAnyExclusiveSink() { + return this == EXCLUSIVE_SINK_BOTH || this == EXCLUSIVE_SINK_EAST || this == EXCLUSIVE_SINK_WEST; + } + + public static boolean isAnyExclusiveSink(int ordinal) { + return ordinal == EXCLUSIVE_SINK_BOTH.ordinal() || ordinal == EXCLUSIVE_SINK_EAST.ordinal() || ordinal == EXCLUSIVE_SINK_WEST.ordinal(); + } + + public boolean isAnyLocal() { + return this == LOCAL_BOTH || this == LOCAL_EAST || this == LOCAL_WEST || this == LOCAL_RESERVED; } - public boolean isLocal() { - return this == LOCAL || this == LOCAL_EAST || this == LOCAL_WEST; + public static boolean isAnyLocal(int ordinal) { + return ordinal == LOCAL_BOTH.ordinal() || ordinal == LOCAL_EAST.ordinal() || ordinal == LOCAL_WEST.ordinal() || ordinal == LOCAL_RESERVED.ordinal(); } } diff --git a/src/com/xilinx/rapidwright/rwroute/RouterHelper.java b/src/com/xilinx/rapidwright/rwroute/RouterHelper.java index 3c21704df..1f61cf225 100644 --- a/src/com/xilinx/rapidwright/rwroute/RouterHelper.java +++ b/src/com/xilinx/rapidwright/rwroute/RouterHelper.java @@ -55,7 +55,6 @@ import com.xilinx.rapidwright.device.Series; import com.xilinx.rapidwright.device.Tile; import com.xilinx.rapidwright.device.TileTypeEnum; -import com.xilinx.rapidwright.device.Wire; import com.xilinx.rapidwright.edif.EDIFHierCellInst; import com.xilinx.rapidwright.timing.TimingEdge; import com.xilinx.rapidwright.timing.TimingManager; @@ -265,7 +264,7 @@ public static List getPIPsFromNodes(List connectionNodes, boolean src for (int i = 0; i < connectionNodes.size() - 1; i++) { Node driver = connectionNodes.get(i + driverOffsetIdx); Node load = connectionNodes.get(i + loadOffsetIdx); - PIP pip = findPIPbetweenNodes(driver, load); + PIP pip = PIP.getArbitraryPIP(driver, load); if (pip != null) { connectionPIPs.add(pip); } else { @@ -275,67 +274,6 @@ public static List getPIPsFromNodes(List connectionNodes, boolean src return connectionPIPs; } - /** - * Finds the {@link PIP} instance that connects two {@link Node} instances. - * @param driver The driver node. - * @param load The load node. - * @return The PIP connecting the two nodes. - */ - public static PIP findPIPbetweenNodes(Node driver, Node load) { - PIP pip = getPIP(load.getTile(), driver.getAllWiresInNode(), load.getWireIndex()); - if (pip == null) { - // for other scenarios regarding bidirectional nodes, such as LAG tile nodes, LAG_LAG_X12Y250/LAG_MUX_ATOM_0_TXOUT to node LAG_LAG_X12Y310/UBUMP0 - pip = getPIP(driver, load); - } - - return pip; - } - - /** - * Gets the {@link PIP} instance based on the {@link Tile} instance of a node, its driver node wires and its base {@link Wire} instance. - * @param loadTile The base tile of the load node. - * @param driverWires All wires in the driver node. - * @param loadWire The wire of the load node. - * @return The PIP that connects one of the wires in the driver node and the wire of the load node. - */ - public static PIP getPIP(Tile loadTile, Wire[] driverWires, int loadWire) { - PIP pip = null; - for (Wire wire : driverWires) { - if (wire.getTile().equals(loadTile)) { - pip = loadTile.getPIP(wire.getWireIndex(), loadWire); - if (pip != null) { - if (pip.isBidirectional() && pip.getStartWireIndex() == loadWire) { - pip.setIsReversed(true); - } - break; - } - } - } - return pip; - } - - /** - * Gets the {@link PIP} instance from a driver {@link Node} instance to a load {@link Node} instance. - * @param driver The driver node. - * @param load The load node. - * @return The PIP from the driver node to the load node. - */ - public static PIP getPIP(Node driver, Node load) { - for (PIP p : driver.getAllDownhillPIPs()) { - if (p.getEndNode().equals(load)) - return p; - } - for (PIP p : driver.getAllUphillPIPs()) { - if (p.getStartNode().equals(load)) { - if (p.isBidirectional()) { - p.setIsReversed(true); - } - return p; - } - } - return null; - } - /** * Gets a (non-unique) collection of {@link Node} instances used by a {@link Net} instance. * Nodes associated with unrouted sink pins on this net will be excluded. @@ -648,7 +586,7 @@ public static List findPathBetweenNodes(Node source, Node sink) { if (!success) { System.err.println("ERROR: Failed to find a path between two nodes: " + source + ", " + sink); - return null; + path.clear(); } return path; } diff --git a/src/com/xilinx/rapidwright/rwroute/TimingAndWirelengthReport.java b/src/com/xilinx/rapidwright/rwroute/TimingAndWirelengthReport.java index c51cc6789..d57747c92 100644 --- a/src/com/xilinx/rapidwright/rwroute/TimingAndWirelengthReport.java +++ b/src/com/xilinx/rapidwright/rwroute/TimingAndWirelengthReport.java @@ -128,7 +128,7 @@ private NetWrapper createNetWrapper(Net net) { if (sinkINTNode == null) { connection.setDirect(true); } else { - connection.setSinkRnode(routingGraph.getOrCreate(sinkINTNode, RouteNodeType.EXCLUSIVE_SINK)); + connection.setSinkRnode(routingGraph.getOrCreate(sinkINTNode, RouteNodeType.EXCLUSIVE_SINK_BOTH)); if (sourceINTNode == null) { sourceINTNode = RouterHelper.projectOutputPinToINTNode(source); } diff --git a/src/com/xilinx/rapidwright/timing/TimingManager.java b/src/com/xilinx/rapidwright/timing/TimingManager.java index 59a101f59..36d6400b2 100644 --- a/src/com/xilinx/rapidwright/timing/TimingManager.java +++ b/src/com/xilinx/rapidwright/timing/TimingManager.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2019-2022, Xilinx, Inc. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. * All rights reserved. * * This file is part of RapidWright. @@ -109,7 +109,7 @@ public TimingManager(Design design, RuntimeTrackerTree timer, RWRouteConfig conf * @param illegalNets {@link NetWrapper} instances in question. * @param nodesDelays Stored nodes and their delay values. */ - public void updateIllegalNetsDelays(Set illegalNets, Map nodesDelays) { + public void updateIllegalNetsDelays(List illegalNets, Map nodesDelays) { for (NetWrapper netWrapper:illegalNets) { for (Connection connection:netWrapper.getConnections()) { float netDelay = 0; diff --git a/test/src/com/xilinx/rapidwright/device/TestNode.java b/test/src/com/xilinx/rapidwright/device/TestNode.java index 175172258..eacef5bd4 100644 --- a/test/src/com/xilinx/rapidwright/device/TestNode.java +++ b/test/src/com/xilinx/rapidwright/device/TestNode.java @@ -228,8 +228,10 @@ public void testNodeReachabilityUltraScale(String partName, String tileName, Str @ParameterizedTest @CsvSource({ - "xcvp1002,INT_X38Y220,NODE_PINBOUNCE,BOUNCE_E.*,true", - "xcvp1002,INT_X38Y220,NODE_PINBOUNCE,BOUNCE_W.*,true", + "xcvp1002,INT_X38Y220,NODE_PINBOUNCE,BOUNCE_E([0-9]|1[0-5]),true", + "xcvp1002,INT_X38Y220,NODE_PINBOUNCE,BOUNCE_E(1[6-9]|2[0-9]|3[01]),true", + "xcvp1002,INT_X38Y220,NODE_PINBOUNCE,BOUNCE_W([0-9]|1[0-5]),true", + "xcvp1002,INT_X38Y220,NODE_PINBOUNCE,BOUNCE_W(1[6-9]|2[0-9]|3[01]),true", "xcvp1002,INT_X38Y220,NODE_INODE,INT_NODE_IMUX_ATOM_([0-9]|1[0-9]|2[0-9]|3[01]|6[4-9]|7[0-9]|8[0-9]|9[0-5])_.*,true", "xcvp1002,INT_X38Y220,NODE_INODE,INT_NODE_IMUX_ATOM_(3[2-9]|4[0-9]|5[0-9]|6[0-3]|9[6-9]|10[0-9]|11[0-9]|12[0-7])_.*,true", "xcvp1002,INT_X38Y220,NODE_IMUX,IMUX_B_E.*,true", diff --git a/test/src/com/xilinx/rapidwright/rwroute/TestGlobalSignalRouting.java b/test/src/com/xilinx/rapidwright/rwroute/TestGlobalSignalRouting.java index cf196000b..ac418bd5c 100644 --- a/test/src/com/xilinx/rapidwright/rwroute/TestGlobalSignalRouting.java +++ b/test/src/com/xilinx/rapidwright/rwroute/TestGlobalSignalRouting.java @@ -30,6 +30,7 @@ import com.xilinx.rapidwright.design.SiteInst; import com.xilinx.rapidwright.design.SitePinInst; import com.xilinx.rapidwright.design.Unisim; +import com.xilinx.rapidwright.device.Device; import com.xilinx.rapidwright.device.Node; import com.xilinx.rapidwright.device.SitePin; import com.xilinx.rapidwright.router.RouteThruHelper; @@ -38,6 +39,7 @@ import com.xilinx.rapidwright.util.ReportRouteStatusResult; import com.xilinx.rapidwright.util.VivadoTools; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.api.io.TempDir; @@ -47,8 +49,8 @@ import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import java.util.function.BiFunction; public class TestGlobalSignalRouting { @ParameterizedTest @@ -124,13 +126,13 @@ public void testRouteStaticNet() { RouteThruHelper routeThruHelper = new RouteThruHelper(design.getDevice()); - GlobalSignalRouting.routeStaticNet(gndNet, (n) -> getNodeState(design, NetType.GND, n), design, routeThruHelper); + GlobalSignalRouting.routeStaticNet(gndPins, (n) -> getNodeState(design, NetType.GND, n), design, routeThruHelper); gndPins = gndNet.getPins(); - Assertions.assertEquals(857, gndPins.stream().filter((spi) -> spi.isOutPin()).count()); + Assertions.assertEquals(737, gndPins.stream().filter((spi) -> spi.isOutPin()).count()); Assertions.assertEquals(19010, gndPins.stream().filter((spi) -> !spi.isOutPin()).count()); - Assertions.assertEquals(33201, gndNet.getPIPs().size()); + Assertions.assertEquals(33429, gndNet.getPIPs().size()); - GlobalSignalRouting.routeStaticNet(vccNet, (n) -> getNodeState(design, NetType.VCC, n), design, routeThruHelper); + GlobalSignalRouting.routeStaticNet(vccPins, (n) -> getNodeState(design, NetType.VCC, n), design, routeThruHelper); vccPins = vccNet.getPins(); Assertions.assertEquals(0, vccPins.stream().filter((spi) -> spi.isOutPin()).count()); Assertions.assertEquals(23099, vccPins.stream().filter((spi) -> !spi.isOutPin()).count()); @@ -209,12 +211,12 @@ public void testRouteStaticNetOnVersalDevice(boolean createStaticPins, @TempDir RouteThruHelper routeThruHelper = new RouteThruHelper(design.getDevice()); Design finalDesign = design; - GlobalSignalRouting.routeStaticNet(gndNet, (n) -> getNodeState(finalDesign, NetType.GND, n), design, routeThruHelper); + GlobalSignalRouting.routeStaticNet(gndPins, (n) -> getNodeState(finalDesign, NetType.GND, n), design, routeThruHelper); Assertions.assertEquals(8, gndPins.stream().filter((spi) -> spi.isOutPin()).count()); Assertions.assertEquals(123, gndPins.stream().filter((spi) -> !spi.isOutPin()).count()); Assertions.assertEquals(436, gndNet.getPIPs().size()); - GlobalSignalRouting.routeStaticNet(vccNet, (n) -> getNodeState(finalDesign, NetType.VCC, n), design, routeThruHelper); + GlobalSignalRouting.routeStaticNet(vccPins, (n) -> getNodeState(finalDesign, NetType.VCC, n), design, routeThruHelper); Assertions.assertEquals(0, vccPins.stream().filter((spi) -> spi.isOutPin()).count()); Assertions.assertEquals(232, vccPins.stream().filter((spi) -> !spi.isOutPin()).count()); Assertions.assertEquals(464, vccNet.getPIPs().size()); @@ -225,4 +227,94 @@ public void testRouteStaticNetOnVersalDevice(boolean createStaticPins, @TempDir Assertions.assertEquals(0, rrs.netsWithRoutingErrors); } } + + // This is a minimized testcase from the result of GlobalSignalRouter + @ParameterizedTest + @CsvSource({ + "false,false", + "true,false", + "false,true" + }) + public void testMuxOutPinAsStaticSourceEvenWithLut6(boolean setCmuxCtag, boolean fullIntraSiteRouting) { + Assumptions.assumeTrue(FileTools.isVivadoOnPath()); + + Design design = RapidWrightDCP.loadDCP("optical-flow.dcp"); + Device device = design.getDevice(); + Net gndNet = design.getGndNet(); + boolean srcToSinkOrder = true; + gndNet.setPIPs(RouterHelper.getPIPsFromNodes(Arrays.asList( + device.getNode("CLEM_X52Y123/CLE_CLE_M_SITE_0_CMUX"), + device.getNode("INT_X52Y123/INT_NODE_SDQ_90_INT_OUT1"), + device.getNode("INT_X52Y123/WW1_W_BEG7"), + device.getNode("INT_X51Y124/INODE_E_1_FT1"), + device.getNode("INT_X51Y123/IMUX_E15") + ), srcToSinkOrder)); + SiteInst si = design.getSiteInstFromSiteName("SLICE_X81Y123"); + Cell lut6 = si.getCell("C6LUT"); + Assertions.assertEquals("LUT6", lut6.getType()); + String const0 = ""; + if (setCmuxCtag) { + if (!fullIntraSiteRouting) { + // Setting just the CMUX cTag + si.routeIntraSiteNet(gndNet, si.getBELPin("CMUX", "CMUX"), si.getBELPin("CMUX", "CMUX")); + } else { + // Full O5 -> OUTMUXC -> CMUX intra-site routing + si.routeIntraSiteNet(gndNet, si.getBELPin("C5LUT", "O5"), si.getBELPin("CMUX", "CMUX")); + } + } + + String status = VivadoTools.reportRouteStatus(design, const0); + if (!setCmuxCtag) { + // Not setting the CMUX cTag correctly causes SLICE_X81Y123 to have invalid site programming + Assertions.assertEquals("CONFLICTS", status); + } else { + // In both cases, SLICE_X81Y123 does not exhibit invalid site programming and thus net appears partially routed + Assertions.assertEquals("PARTIAL", status); + } + } + + // This is a minimized testcase from the result of GlobalSignalRouter + @ParameterizedTest + @CsvSource({ + "false,false", + "true,false", + "false,true" + }) + public void testMuxOutPinAsStaticSourceEvenWithLutRam(boolean setFmuxCtag, boolean fullIntraSiteRouting) { + Assumptions.assumeTrue(FileTools.isVivadoOnPath()); + + Design design = RapidWrightDCP.loadDCP("bnn.dcp"); + Device device = design.getDevice(); + Net gndNet = design.getGndNet(); + boolean srcToSinkOrder = true; + gndNet.setPIPs(RouterHelper.getPIPsFromNodes(Arrays.asList( + device.getNode("CLEM_X52Y218/CLE_CLE_M_SITE_0_FMUX"), + device.getNode("INT_X52Y218/SDQNODE_W_2_FT1"), + device.getNode("INT_X52Y218/EE2_W_BEG0"), + device.getNode("INT_X53Y218/INODE_W_1_FT1"), + device.getNode("INT_X53Y217/IMUX_W46"), + device.getNode("BRAM_X53Y215/BRAM_BRAM_CORE_3_ADDRENAU_PIN") + ), srcToSinkOrder)); + SiteInst si = design.getSiteInstFromSiteName("SLICE_X81Y218"); + Cell lutRam = si.getCell("F6LUT"); + Assertions.assertEquals("RAMS64E", lutRam.getType()); + String const0 = "bd_0_i/hls_inst/inst/"; + if (setFmuxCtag) { + if (!fullIntraSiteRouting) { + // Setting just the FMUX cTag + si.routeIntraSiteNet(gndNet, si.getBELPin("FMUX", "FMUX"), si.getBELPin("FMUX", "FMUX")); + } else { + // Full O5 -> OUTMUXF -> FMUX intra-site routing + si.routeIntraSiteNet(gndNet, si.getBELPin("F5LUT", "O5"), si.getBELPin("FMUX", "FMUX")); + } + } + String status = VivadoTools.reportRouteStatus(design, const0); + if (!setFmuxCtag) { + // Not setting the FMUX cTag correctly causes SLICE_X81Y218 to have invalid site programming + Assertions.assertEquals("CONFLICTS", status); + } else { + // In both cases, SLICE_X81Y218 does not exhibit invalid site programming and thus net appears partially routed + Assertions.assertEquals("PARTIAL", status); + } + } } diff --git a/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java b/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java index 97bb28c56..c4a62f0e2 100644 --- a/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java +++ b/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java @@ -393,33 +393,34 @@ void testSingleConnectionHelper(String partName, Assertions.assertTrue(srcSpi.isRouted()); Assertions.assertTrue(dstSpi.isRouted()); - Assertions.assertTrue(Long.parseLong(System.getProperty("rapidwright.rwroute.nodesPopped")) <= nodesPoppedLimit); + long nodesPopped = Long.parseLong(System.getProperty("rapidwright.rwroute.nodesPopped")); + Assertions.assertTrue(nodesPopped >= (nodesPoppedLimit - 100) && nodesPopped <= nodesPoppedLimit); } @ParameterizedTest @CsvSource({ // One SLR crossing // (Too) Close - "SLICE_X9Y299,SLICE_X9Y300,500", // On Laguna column + "SLICE_X9Y299,SLICE_X9Y300,400", // On Laguna column "SLICE_X9Y300,SLICE_X9Y299,500", "SLICE_X0Y299,SLICE_X0Y300,200", // Far from Laguna column "SLICE_X0Y300,SLICE_X0Y299,200", "SLICE_X53Y299,SLICE_X53Y300,200", // Equidistant from two Laguna columns "SLICE_X53Y300,SLICE_X53Y299,700", // Perfect - "SLICE_X9Y241,SLICE_X9Y300,200", + "SLICE_X9Y241,SLICE_X9Y300,100", "SLICE_X9Y300,SLICE_X9Y241,100", "SLICE_X9Y358,SLICE_X9Y299,100", "SLICE_X9Y299,SLICE_X9Y358,200", "SLICE_X53Y241,SLICE_X69Y300,500", - "SLICE_X53Y358,SLICE_X69Y299,500", + "SLICE_X53Y358,SLICE_X69Y299,400", // Far "SLICE_X9Y240,SLICE_X9Y359,100", // On Laguna "SLICE_X9Y359,SLICE_X9Y240,200", - "SLICE_X162Y240,SLICE_X162Y430,200", + "SLICE_X162Y240,SLICE_X162Y430,100", "SLICE_X162Y430,SLICE_X162Y240,300", - "SLICE_X0Y240,SLICE_X12Y430,400", // Far from Laguna - "SLICE_X0Y430,SLICE_X12Y240,200", + "SLICE_X0Y240,SLICE_X12Y430,300", // Far from Laguna + "SLICE_X0Y430,SLICE_X12Y240,100", // Two SLR crossings "SLICE_X162Y299,SLICE_X162Y599,600", @@ -427,10 +428,10 @@ void testSingleConnectionHelper(String partName, // Three SLR crossings "SLICE_X79Y0,SLICE_X79Y899,200", // Straight up: next to Laguna column - "SLICE_X0Y0,SLICE_X0Y899,600", // Straight up: far from Laguna column + "SLICE_X0Y0,SLICE_X0Y899,500", // Straight up: far from Laguna column "SLICE_X168Y0,SLICE_X168Y899,400", // Straight up: far from Laguna column - "SLICE_X9Y0,SLICE_X162Y899,1000", // Up and right - "SLICE_X168Y162,SLICE_X9Y899,600", // Up and left + "SLICE_X9Y0,SLICE_X162Y899,500", // Up and right + "SLICE_X168Y162,SLICE_X9Y899,1100", // Up and left }) public void testSLRCrossingNonTimingDriven(String srcSiteName, String dstSiteName, long nodesPoppedLimit) { testSingleConnectionHelper(Device.AWS_F1, srcSiteName, "AQ", dstSiteName, "A1", nodesPoppedLimit); @@ -515,10 +516,10 @@ public void testTimingAndWirelengthReport() { "xcvu3p,GTYE4_CHANNEL_X0Y12,TXOUTCLK_INT,BUFG_GT_X0Y78,CLK_IN,0", // (dst pin can be projected to INT but not src pin) // Non-dedicated connections - "xcvu3p,IOB_X0Y47,I,SLICE_X77Y122,FX,600", + "xcvu3p,IOB_X0Y47,I,SLICE_X77Y122,FX,100", // 240 CLB height SLR, no LAG tiles on Y0 (since HBM on bottom edge) - "xcu50,SLICE_X38Y239,AQ,SLICE_X38Y240,A1,500" + "xcu50,SLICE_X38Y239,AQ,SLICE_X38Y240,A1,400" }) public void testSingleConnection(String partName, String srcSiteName, String srcPinName,