Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RWRoute] Add --lutRoutethru option #932

Merged
merged 37 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
880e651
Move assertion in case no site pin
eddieh-xlnx Dec 15, 2023
621affc
RouteNodeInfo.getType() to return WIRE for NODE_PINFEED
eddieh-xlnx Dec 15, 2023
92435bf
Update comment
eddieh-xlnx Dec 15, 2023
e2f9e29
Move assertion for twin method
eddieh-xlnx Dec 15, 2023
e5b6583
Add --lutRoutethru option to RWRouteConfig
eddieh-xlnx Dec 15, 2023
10079bc
Preliminary LUT routethru support
eddieh-xlnx Dec 15, 2023
045ca05
Changes to RouteNodeGraph constructor
eddieh-xlnx Dec 15, 2023
e5223f9
Add testNonTimingDrivenFullRoutingWithLutRoutethru()
eddieh-xlnx Dec 15, 2023
bcf4380
Do not block INT_NODE_IMUX_ accessibility when LUT routethrus
eddieh-xlnx Dec 16, 2023
46999c2
Better method names, add comment, print LUT pin swap/routethru flag
eddieh-xlnx Dec 16, 2023
c17988e
[RWRoute] Analyze a tile below the topmost arbitrary one
eddieh-xlnx Dec 16, 2023
c2a74f3
Remove unused import
eddieh-xlnx Dec 16, 2023
b4e0d11
Use wireIndex not wireName to block *MUX routethrus
eddieh-xlnx Dec 16, 2023
da94aed
Fix assertions
eddieh-xlnx Dec 16, 2023
59cf442
Move global-net fixup before fixRoutes()
eddieh-xlnx Dec 16, 2023
7058c8c
Remove debug
eddieh-xlnx Dec 18, 2023
6ec4437
Enumerate all CLB tiles for *MUX wires
eddieh-xlnx Dec 18, 2023
e84941f
Add Utils.getLagunaTileTypes() and use
eddieh-xlnx Dec 18, 2023
fbebb76
Fix comment
eddieh-xlnx Dec 18, 2023
b8c753a
Remove unused import
eddieh-xlnx Dec 18, 2023
26c3c51
[RWRoute] Do not pin swap dist. RAMs on the 'H' BELs
eddieh-xlnx Dec 19, 2023
6a1d87f
LUTTools.swapMultipleLutPins() to handle LUT routethrus
eddieh-xlnx Dec 19, 2023
c742283
Add TestLUTTools.testUpdateLutPinSwapsFromPIPsWithRWRouteAndLutRoutet…
eddieh-xlnx Dec 19, 2023
b049714
RouteThruHelper.isRouteThruPIPAvailable() to check out pin conns too
eddieh-xlnx Dec 19, 2023
888651b
Fix test
eddieh-xlnx Dec 19, 2023
ba5cb07
[PhysNetlistWriter] Recognize static source BELPins (e.g. LUT outputs)
eddieh-xlnx Dec 19, 2023
30106a7
Revert "RouteThruHelper.isRouteThruPIPAvailable() to check out pin co…
eddieh-xlnx Dec 19, 2023
79fcad4
Expand TestRouteThruHelper
eddieh-xlnx Dec 19, 2023
79ad0e4
Add TestPhysNetlistWriter.testStaticSourceBELPin()
eddieh-xlnx Dec 19, 2023
ba9f017
Tidy up
eddieh-xlnx Dec 19, 2023
dd44e18
Merge remote-tracking branch 'upstream/master' into rwroute_lutrt
eddieh-xlnx Dec 19, 2023
03afd1e
Merge remote-tracking branch 'upstream/master' into rwroute_lutrt
eddieh-xlnx Jan 4, 2024
d035795
Merge remote-tracking branch 'upstream/master' into rwroute_lutrt
eddieh-xlnx Jan 7, 2024
5bae2e0
Merge remote-tracking branch 'upstream/master' into rwroute_lutrt
eddieh-xlnx Jan 8, 2024
d6a2ab4
Address review comments
eddieh-xlnx Jan 12, 2024
a2ecc9f
Merge remote-tracking branch 'upstream/master' into rwroute_lutrt
eddieh-xlnx Jan 12, 2024
cbd8156
Update src/com/xilinx/rapidwright/rwroute/RWRoute.java
eddieh-xlnx Jan 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions src/com/xilinx/rapidwright/design/tools/LUTTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
Expand Down Expand Up @@ -675,6 +676,7 @@ public static int swapLutPinsFromPIPs(Design design) {
Map<SitePinInst, String> oldPinToNewPins = new HashMap<>();
Map<Site, List<SitePinInst>> siteToLutSpis = new HashMap<>();
List<SitePin> unmatchedSitePins = new ArrayList<>();
Set<SitePin> routethruSitePins = new HashSet<>();
for (Net net : design.getNets()) {
if (net.isClockNet()) {
continue;
Expand All @@ -701,17 +703,30 @@ public static int swapLutPinsFromPIPs(Design design) {
}

for (PIP pip : net.getPIPs()) {
if (pip.isRouteThru()) {
Node startNode = pip.getStartNode();
SitePin newSitePin = startNode.getSitePin();
assert(newSitePin != null);
routethruSitePins.add(newSitePin);
continue;
}

Node endNode = pip.getEndNode();
SitePin newSitePin = (endNode != null) ? endNode.getSitePin() : null;
if (newSitePin == null) {
continue;
}

Site site = newSitePin.getSite();
SiteInst si = design.getSiteInstFromSite(site);
if (si == null) {
List<SitePinInst> lutSpis = siteToLutSpis.get(site);
if (lutSpis == null) {
// No sink pins from this net exist on this site
// (e.g. this pin is used as routethru)
continue;
}
SiteInst si = design.getSiteInstFromSite(site);
assert(si != null);

String newSitePinName = newSitePin.getPinName();
if (!SitePinInst.isLUTInputPin(si, newSitePinName)) {
continue;
Expand All @@ -722,12 +737,17 @@ public static int swapLutPinsFromPIPs(Design design) {
System.out.println("WARNING: SitePin " + newSitePin + " visited by PIP " + pip +
" is not a SitePinInst on net " + net + ". Ignoring.");
} else if (!spis.remove(newSpi)) {
// spi is not already on this net
// spi is not already on this net -- could require pin swapping,
// or could be a routethru
unmatchedSitePins.add(newSitePin);
}
}

for (SitePin newSitePin : unmatchedSitePins) {
if (routethruSitePins.contains(newSitePin)) {
// Pin is part of a routethru, ignore it
continue;
}
Site site = newSitePin.getSite();
List<SitePinInst> unmatchedSpis = siteToLutSpis.get(site);
Iterator<SitePinInst> it = unmatchedSpis.iterator();
Expand All @@ -752,6 +772,7 @@ public static int swapLutPinsFromPIPs(Design design) {

siteToLutSpis.clear();
unmatchedSitePins.clear();
routethruSitePins.clear();
}

return swapMultipleLutPins(oldPinToNewPins);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ private static boolean isNodeUsableStaticSource(Node node, NetType type, Design
// before we stop:
// (1) GND_WIRE
// (2) VCC_WIRE
// (3) Unused LUT Outputs (A_0, B_0,...,H_0)
// (3) Unused LUT Outputs ([A-H]_O, [A-H]MUX)
if ((type == NetType.VCC && node.isTiedToVcc()) ||
(type == NetType.GND && node.isTiedToGnd())) {
return true;
Expand Down
15 changes: 9 additions & 6 deletions src/com/xilinx/rapidwright/rwroute/PartialRouter.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ public class PartialRouter extends RWRoute {

protected class RouteNodeGraphPartial extends RouteNodeGraph {

public RouteNodeGraphPartial(RuntimeTracker setChildrenTimer, Design design) {
super(setChildrenTimer, design);
public RouteNodeGraphPartial(RuntimeTracker setChildrenTimer, Design design, RWRouteConfig config) {
super(setChildrenTimer, design, config);
}

@Override
Expand All @@ -87,8 +87,11 @@ protected boolean isExcluded(Node parent, Node child) {
}

protected class RouteNodeGraphPartialTimingDriven extends RouteNodeGraphTimingDriven {
public RouteNodeGraphPartialTimingDriven(RuntimeTracker rnodesTimer, Design design, DelayEstimatorBase delayEstimator, boolean maskNodesCrossRCLK) {
super(rnodesTimer, design, delayEstimator, maskNodesCrossRCLK);
public RouteNodeGraphPartialTimingDriven(RuntimeTracker rnodesTimer,
Design design,
RWRouteConfig config,
DelayEstimatorBase delayEstimator) {
super(rnodesTimer, design, config, delayEstimator);
}

@Override
Expand Down Expand Up @@ -173,9 +176,9 @@ protected RouteNodeGraph createRouteNodeGraph() {
if (config.isTimingDriven()) {
/* An instantiated delay estimator that is used to calculate delay of routing resources */
DelayEstimatorBase estimator = new DelayEstimatorBase(design.getDevice(), new InterconnectInfo(), config.isUseUTurnNodes(), 0);
return new RouteNodeGraphPartialTimingDriven(rnodesTimer, design, estimator, config.isMaskNodesCrossRCLK());
return new RouteNodeGraphPartialTimingDriven(rnodesTimer, design, config, estimator);
} else {
return new RouteNodeGraphPartial(rnodesTimer, design);
return new RouteNodeGraphPartial(rnodesTimer, design, config);
}
}

Expand Down
205 changes: 130 additions & 75 deletions src/com/xilinx/rapidwright/rwroute/RWRoute.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ public class RWRoute{
private float oneMinusTimingWeight;
/** Flag for whether LUT pin swaps are to be considered */
protected boolean lutPinSwapping;
/** Flag for whether LUT routethrus are to be considered */
protected boolean lutRoutethru;

/** The current routing iteration */
protected int routeIteration;
Expand Down Expand Up @@ -227,7 +229,8 @@ protected void initialize() {
rnodesCreatedThisIteration = 0;
routethruHelper = new RouteThruHelper(design.getDevice());
presentCongestionFactor = config.getInitialPresentCongestionFactor();
lutPinSwapping = config.getLutPinSwapping();
lutPinSwapping = config.isLutPinSwapping();
lutRoutethru = config.isLutRoutethru();

routerTimer.createRuntimeTracker("determine route targets", "Initialization").start();
determineRoutingTargets();
Expand Down Expand Up @@ -267,9 +270,9 @@ protected RouteNodeGraph createRouteNodeGraph() {
if (config.isTimingDriven()) {
/* An instantiated delay estimator that is used to calculate delay of routing resources */
DelayEstimatorBase estimator = new DelayEstimatorBase(design.getDevice(), new InterconnectInfo(), config.isUseUTurnNodes(), 0);
return new RouteNodeGraphTimingDriven(rnodesTimer, design, estimator, config.isMaskNodesCrossRCLK());
return new RouteNodeGraphTimingDriven(rnodesTimer, design, config, estimator);
} else {
return new RouteNodeGraph(rnodesTimer, design);
return new RouteNodeGraph(rnodesTimer, design, config);
}
}

Expand Down Expand Up @@ -908,96 +911,148 @@ private List<Connection> getCongestedConnections() {
* Assigns a list of nodes to each connection and fix net routes if there are cycles and / or multi-driver nodes.
*/
protected void postRouteProcess() {
if (routeIteration <= config.getMaxIterations()) {
// perform LUT pin mapping updates
if (lutPinSwapping &&
!Boolean.getBoolean("rapidwright.rwroute.lutPinSwapping.deferIntraSiteRoutingUpdates")) {
Map<SitePinInst, String> pinSwaps = new HashMap<>();
for (Connection connection: indirectConnections) {
SitePinInst oldSinkSpi = connection.getSink();
if (!oldSinkSpi.isLUTInputPin() || !oldSinkSpi.isRouted()) {
if (routeIteration > config.getMaxIterations()) {
return;
}

// perform LUT pin mapping updates
if (lutPinSwapping &&
!Boolean.getBoolean("rapidwright.rwroute.lutPinSwapping.deferIntraSiteRoutingUpdates")) {
Map<SitePinInst, String> pinSwaps = new HashMap<>();
for (Connection connection: indirectConnections) {
SitePinInst oldSinkSpi = connection.getSink();
if (!oldSinkSpi.isLUTInputPin() || !oldSinkSpi.isRouted()) {
continue;
}

List<RouteNode> rnodes = connection.getRnodes();
RouteNode newSinkRnode = rnodes.get(0);
if (newSinkRnode == connection.getSinkRnode()) {
continue;
}
connection.setSinkRnode(newSinkRnode);

SitePin newSitePin = newSinkRnode.getNode().getSitePin();
String existing = pinSwaps.put(oldSinkSpi, newSitePin.getPinName());
assert(existing == null);
}
LUTTools.swapMultipleLutPins(pinSwaps);
}

if (lutRoutethru) {
// When LUT routethru-s are considered, examine both static nets to find
// any cases where the *MUX output pin is used as a static source alongside
// the *_O output being used as a routethru. In such cases, configure the
// OUTMUX* site PIP to source from the 5LUT rather than the default 6LUT
// so that no conflict occurs
eddieh-xlnx marked this conversation as resolved.
Show resolved Hide resolved
for (Net staticNet : Arrays.asList(design.getGndNet(), design.getVccNet())) {
for (SitePinInst spi : staticNet.getPins()) {
if (!spi.isOutPin()) {
continue;
}
SiteInst si = spi.getSiteInst();
if (!Utils.isSLICE(si)) {
continue;
}

String pinName = spi.getName();
if (!pinName.endsWith("MUX")) {
continue;
}

List<RouteNode> rnodes = connection.getRnodes();
RouteNode newSinkRnode = rnodes.get(0);
if (newSinkRnode == connection.getSinkRnode()) {
Node muxNode = spi.getConnectedNode();
assert(routingGraph.getPreservedNet(muxNode) == staticNet);

Site site = si.getSite();
char lutLetter = pinName.charAt(0);
Node oNode = site.getConnectedNode(lutLetter + "_O");
RouteNode rnode = routingGraph.getNode(oNode);
if (rnode == null || rnode.getOccupancy() == 0) {
// No LUT6 routethru, nothing to be done
continue;
}
connection.setSinkRnode(newSinkRnode);

SitePin newSitePin = newSinkRnode.getNode().getSitePin();
String existing = pinSwaps.put(oldSinkSpi, newSitePin.getPinName());
assert(existing == null);
// Only expect GND net to use SLICE outputs
eddieh-xlnx marked this conversation as resolved.
Show resolved Hide resolved
assert(staticNet.getType() == NetType.GND);

// Perform intra-site routing back to the LUT5 to not conflict with LUT6 routethru
BEL outmux = si.getBEL("OUTMUX" + lutLetter);
si.routeIntraSiteNet(staticNet, outmux.getPin("D5"), outmux.getPin("OUT"));

if (si.getDesign() == null) {
// Rename SiteInst (away from "STATIC_SOURCE_<siteName>") and
// attach it to the design so that intra-site routing updates take effect
si.setName(site.getName());
design.addSiteInst(si);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you demonstrate that this doesn't cause an error when importing the designs back into Vivado? Unless a placed cell exists (which a route thru may suffice), my understanding is that this could lead to an issue.

Copy link
Collaborator Author

@eddieh-xlnx eddieh-xlnx Jan 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm seeing a breakpoint hit this line in the newly added testtestNonTimingDrivenFullRoutingWithLutRoutethru with bnn.dcp for SLICE_X79Y201, and for optical-flow.dcp for SLICE_X66Y252 and 10 others.

For bnn.dcp, even after RWRoute is done, that SiteInst has no placed cells (recall that thru-site PIPs do not show up as routethru cells), yet Vivado 2022.1 seems to accept it just fine and reports a clean routing result.

image
(highlighted net is GLOBAL_LOGIC0; rest of slice is completely empty aside from these two routethrus)

Vivado 2022.1 also accepts this optical-flow.dcp routed result.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need to upgrade our terminology as there are "routethrus" that terminate at a cell in the site and
other "routethrus" that don't (being specified by a PIP).

}
}
LUTTools.swapMultipleLutPins(pinSwaps);
}
}

assignNodesToConnections();
assignNodesToConnections();

// fix routes with cycles and / or multi-driver nodes
Set<NetWrapper> routes = fixRoutes();
if (config.isTimingDriven()) updateTimingAfterFixingRoutes(routes);
// fix routes with cycles and / or multi-driver nodes
Set<NetWrapper> routes = fixRoutes();
if (config.isTimingDriven()) updateTimingAfterFixingRoutes(routes);

// Unset the routed state of all source pins
for (Map.Entry<Net, NetWrapper> e : nets.entrySet()) {
Net net = e.getKey();
SitePinInst source = net.getSource();
SitePinInst altSource = net.getAlternateSource();
SiteInst si = source.getSiteInst();
boolean altSourcePreviouslyRouted = altSource != null ? altSource.isRouted() : false;
for (SitePinInst spi : Arrays.asList(source, altSource)) {
if (spi != null) {
spi.setRouted(false);
assert(spi.getSiteInst() == si);
}
// Unset the routed state of all source pins
for (Map.Entry<Net, NetWrapper> e : nets.entrySet()) {
Net net = e.getKey();
SitePinInst source = net.getSource();
SitePinInst altSource = net.getAlternateSource();
SiteInst si = source.getSiteInst();
boolean altSourcePreviouslyRouted = altSource != null ? altSource.isRouted() : false;
for (SitePinInst spi : Arrays.asList(source, altSource)) {
if (spi != null) {
spi.setRouted(false);
assert(spi.getSiteInst() == si);
}
}

// Set the routed state on those source pins that were actually used
NetWrapper netWrapper = e.getValue();
for (Connection connection : netWrapper.getConnections()) {
// Examine getNodes() because connection.getRnodes() is empty for direct connections
List<Node> nodes = connection.getNodes();
if (nodes.isEmpty()) {
// Unroutable connection
continue;
}
// Set the routed state on those source pins that were actually used
NetWrapper netWrapper = e.getValue();
for (Connection connection : netWrapper.getConnections()) {
// Examine getNodes() because connection.getRnodes() is empty for direct connections
List<Node> nodes = connection.getNodes();
if (nodes.isEmpty()) {
// Unroutable connection
continue;
}

// Set the routed state of the used source node
// and if used and not already present, add it to the SiteInst
Node sourceNode = nodes.get(nodes.size() - 1);
SitePinInst usedSpi = null;
for (SitePinInst spi : Arrays.asList(source, altSource)) {
if (spi != null && sourceNode.equals(spi.getConnectedNode())) {
usedSpi = spi;
}
}
if (usedSpi == null) {
throw new RuntimeException("ERROR: Unknown source node " + sourceNode + " on net " + net.getName());
// Set the routed state of the used source node
// and if used and not already present, add it to the SiteInst
Node sourceNode = nodes.get(nodes.size() - 1);
SitePinInst usedSpi = null;
for (SitePinInst spi : Arrays.asList(source, altSource)) {
if (spi != null && sourceNode.equals(spi.getConnectedNode())) {
usedSpi = spi;
}
}
if (usedSpi == null) {
throw new RuntimeException("ERROR: Unknown source node " + sourceNode + " on net " + net.getName());
}

// Now that we know this SitePinInst is used, make sure it exists in
// the SiteInst
usedSpi.setRouted(true);
if (si.getSitePinInst(usedSpi.getName()) == null) {
si.addPin(usedSpi);
}
// Now that we know this SitePinInst is used, make sure it exists in
// the SiteInst
usedSpi.setRouted(true);
if (si.getSitePinInst(usedSpi.getName()) == null) {
si.addPin(usedSpi);
}

if (source.isRouted() && (altSource == null || altSource.isRouted())) {
// Break if all sources have been set to be routed
break;
}
if (source.isRouted() && (altSource == null || altSource.isRouted())) {
// Break if all sources have been set to be routed
break;
}
// If the alt source was previously routed, and is no longer, let's remove it
if (altSource != null && altSourcePreviouslyRouted && !altSource.isRouted()) {
boolean sourceRouted = source.isRouted();
altSource.getSiteInst().removePin(altSource);
net.removePin(altSource);
source.setRouted(sourceRouted);
if (altSource.getName().endsWith("_O") && source.getName().endsWith("MUX") && source.isRouted()) {
// Add site routing back if we are keeping the MUX pin
source.getSiteInst().routeIntraSiteNet(net, altSource.getBELPin(), altSource.getBELPin());
}
}
// If the alt source was previously routed, and is no longer, let's remove it
if (altSource != null && altSourcePreviouslyRouted && !altSource.isRouted()) {
boolean sourceRouted = source.isRouted();
altSource.getSiteInst().removePin(altSource);
net.removePin(altSource);
source.setRouted(sourceRouted);
if (altSource.getName().endsWith("_O") && source.getName().endsWith("MUX") && source.isRouted()) {
// Add site routing back if we are keeping the MUX pin
source.getSiteInst().routeIntraSiteNet(net, altSource.getBELPin(), altSource.getBELPin());
}
}
}
Expand Down
Loading
Loading