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

[NetTools] Add getNodeTrees() method and NodeTree class #1089

Merged
merged 6 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 10 additions & 32 deletions src/com/xilinx/rapidwright/design/DesignTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -4317,49 +4317,27 @@ public static void updatePinsIsRouted(Net net) {
return;
}

Queue<Node> queue = new ArrayDeque<>();
Map<Node, List<Node>> node2fanout = new HashMap<>();
for (PIP pip : net.getPIPs()) {
boolean isReversed = pip.isReversed();
Node startNode = isReversed ? pip.getEndNode() : pip.getStartNode();
Node endNode = isReversed ? pip.getStartNode() : pip.getEndNode();
node2fanout.computeIfAbsent(startNode, k -> new ArrayList<>())
.add(endNode);
if (pip.isBidirectional()) {
node2fanout.computeIfAbsent(endNode, k -> new ArrayList<>())
.add(startNode);
}

if ((net.getType() == NetType.GND && startNode.isTiedToGnd()) ||
(net.getType() == NetType.VCC && startNode.isTiedToVcc())) {
queue.add(startNode);
}
}

Map<Node, SitePinInst> node2spi = new HashMap<>();
for (SitePinInst spi : net.getPins()) {
Node node = spi.getConnectedNode();
if (spi.isOutPin()) {
if (node2fanout.get(node) == null) {
// Skip source pins with no fanout
continue;
}
queue.add(node);
}
node2spi.put(node, spi);
}

Queue<NetTools.NodeTree> queue = new ArrayDeque<>();
for (NetTools.NodeTree node : NetTools.getNodeTrees(net)) {
if (node.fanouts.isEmpty()) {
// Skip source pins with no fanout
continue;
}
queue.add(node);
}
while (!queue.isEmpty()) {
Node node = queue.poll();
NetTools.NodeTree node = queue.poll();
SitePinInst spi = node2spi.get(node);
if (spi != null) {
spi.setRouted(true);
}

List<Node> fanouts = node2fanout.remove(node);
if (fanouts != null) {
queue.addAll(fanouts);
}
queue.addAll(node.fanouts);
}
}

Expand Down
98 changes: 98 additions & 0 deletions src/com/xilinx/rapidwright/design/NetTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,16 @@

package com.xilinx.rapidwright.design;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.xilinx.rapidwright.device.Node;
import com.xilinx.rapidwright.device.PIP;
import com.xilinx.rapidwright.device.SiteTypeEnum;

public class NetTools {
Expand All @@ -46,4 +53,95 @@ public static boolean isGlobalClock(Net net) {

return clkSrcSiteTypeEnums.contains(srcSpi.getSiteTypeEnum());
}

public static class NodeTree extends Node {
public List<NodeTree> fanouts = Collections.emptyList();
public NodeTree(Node node) {
super(node);
}

public void addFanout(NodeTree node) {
if (fanouts.isEmpty()) {
fanouts = new ArrayList<>(1);
}
fanouts.add(node);
}

private void buildString(StringBuilder sb,
boolean subtreeStart,
boolean branchStart,
boolean branchEndIfNoFanouts,
boolean subTreeEndIfNoFanouts) {
// Adopt the same spacing as Vivado's report_route_status
sb.append(" ");
sb.append(subtreeStart ? "[" : " ");
sb.append(branchStart ? "{" : " ");
sb.append(" ");
boolean branchEnd = branchEndIfNoFanouts && fanouts.isEmpty();
sb.append(branchEnd ? "}" : " ");
boolean subtreeEnd = subTreeEndIfNoFanouts && branchEnd;
sb.append(subtreeEnd ? "]" : " ");
sb.append(String.format(" %30s", super.toString()));
sb.append("\n");

subtreeStart = false;
for (int i = 0; i < fanouts.size(); i++) {
NodeTree fanout = fanouts.get(i);
boolean lastFanout = (i == fanouts.size() - 1);
branchStart = !lastFanout && (fanouts.size() > 1);
branchEndIfNoFanouts = lastFanout || branchStart;
fanout.buildString(sb, subtreeStart, branchStart, branchEndIfNoFanouts,
subTreeEndIfNoFanouts && !branchStart && lastFanout);
}
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
boolean subtreeStart = true;
boolean branchStart = true;
boolean branchEndIfNoFanouts = true;
boolean subTreeEndIfNoFanouts = true;
buildString(sb, branchStart, subtreeStart, branchEndIfNoFanouts, subTreeEndIfNoFanouts);
return sb.toString();
}
}

/**
* Compute the node routing tree of the given Net by examining its PIPs.
* Note that this method: (a) assumes that no loops are present, (b) only discovers subtrees that start at an
* output SitePinInst or a node tied to VCC/GND (i.e. gaps and islands will be ignored).
* @param net Net to analyze
* @return A list of NodeTree objects, corresponding to the root of each subtree.
*/
public static List<NodeTree> getNodeTrees(Net net) {
List<NodeTree> subtrees = new ArrayList<>();
Map<Node, NodeTree> nodeMap = new HashMap<>();
for (PIP pip : net.getPIPs()) {
if (pip.isEndWireNull()) {
continue;
}
boolean isReversed = pip.isReversed();
NodeTree startNode = nodeMap.computeIfAbsent(isReversed ? pip.getEndNode() : pip.getStartNode(), NodeTree::new);
NodeTree endNode = nodeMap.computeIfAbsent(isReversed ? pip.getStartNode() : pip.getEndNode(), NodeTree::new);
startNode.addFanout(endNode);
if (!pip.isBidirectional()) {
if ((net.getType() == NetType.GND && startNode.isTiedToGnd()) ||
(net.getType() == NetType.VCC && startNode.isTiedToVcc())) {
subtrees.add(startNode);
}
}
}

for (SitePinInst spi : net.getPins()) {
if (!spi.isOutPin()) {
continue;
}
Node node = spi.getConnectedNode();
NodeTree nodeTree = nodeMap.computeIfAbsent(node, NodeTree::new);
subtrees.add(nodeTree);
}

return subtrees;
}
}
84 changes: 84 additions & 0 deletions test/src/com/xilinx/rapidwright/design/TestNetTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
package com.xilinx.rapidwright.design;

import java.util.HashSet;
import java.util.List;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

Expand Down Expand Up @@ -110,4 +112,86 @@ public void testisGlobalClock(String pathAndlobalClockNames) {
}
Assertions.assertEquals(globalClockNamesFromVivado,globalClockNamesFromNetTools);
}

@Test
public void testGetRouteTrees() {
Design design = RapidWrightDCP.loadDCP("picoblaze_ooc_X10Y235.dcp");
Net net = design.getNet("processor/data_path_loop[0].output_data.sy_kk_mux_lut/O5");
List<NetTools.NodeTree> trees = NetTools.getNodeTrees(net);
Assertions.assertEquals(1, trees.size());

// Taken directly from Vivado's report_route_status
String[] expected = new String(
" [{ CLEL_R_X10Y236/CLE_CLE_L_SITE_0_AMUX (65535) \n" +
" { INT_X10Y236/INT_NODE_SDQ_27_INT_OUT1 ( 3) INT_X10Y236/INT.LOGIC_OUTS_E21->INT_NODE_SDQ_27_INT_OUT1\n" +
" INT_X10Y236/SS1_E_BEG5 ( 0) INT_X10Y236/INT.INT_NODE_SDQ_27_INT_OUT1->>SS1_E_BEG5\n" +
" { INT_X10Y235/INT_NODE_IMUX_16_INT_OUT0 ( 5) INT_X10Y235/INT.SS1_E_END5->>INT_NODE_IMUX_16_INT_OUT0\n" +
" INT_X10Y235/BYPASS_E10 ( 3) INT_X10Y235/INT.INT_NODE_IMUX_16_INT_OUT0->>BYPASS_E10\n" +
" { INT_X10Y235/INT_NODE_IMUX_9_INT_OUT1 ( 0) INT_X10Y235/INT.BYPASS_E10->>INT_NODE_IMUX_9_INT_OUT1\n" +
" } INT_X10Y235/IMUX_E23 ( 6) INT_X10Y235/INT.INT_NODE_IMUX_9_INT_OUT1->>IMUX_E23\n" +
" INT_X10Y235/INT_NODE_IMUX_8_INT_OUT1 ( 0) INT_X10Y235/INT.BYPASS_E10->>INT_NODE_IMUX_8_INT_OUT1\n" +
" } INT_X10Y235/IMUX_E26 ( 6) INT_X10Y235/INT.INT_NODE_IMUX_8_INT_OUT1->>IMUX_E26\n" +
" { INT_X10Y235/INT_NODE_SDQ_28_INT_OUT0 ( 2) INT_X10Y235/INT.SS1_E_END5->INT_NODE_SDQ_28_INT_OUT0\n" +
" INT_X10Y235/EE1_E_BEG4 ( 3) INT_X10Y235/INT.INT_NODE_SDQ_28_INT_OUT0->>EE1_E_BEG4\n" +
" INT_X11Y235/INT_NODE_IMUX_48_INT_OUT0 ( 1) INT_X11Y235/INT.EE1_E_END4->>INT_NODE_IMUX_48_INT_OUT0\n" +
" } INT_X11Y235/IMUX_W37 ( 3) INT_X11Y235/INT.INT_NODE_IMUX_48_INT_OUT0->>IMUX_W37\n" +
" INT_X10Y235/INT_NODE_IMUX_16_INT_OUT1 ( 5) INT_X10Y235/INT.SS1_E_END5->>INT_NODE_IMUX_16_INT_OUT1\n" +
" { } INT_X10Y235/IMUX_E30 ( 3) INT_X10Y235/INT.INT_NODE_IMUX_16_INT_OUT1->>IMUX_E30\n" +
" } INT_X10Y235/IMUX_E31 ( 3) INT_X10Y235/INT.INT_NODE_IMUX_16_INT_OUT1->>IMUX_E31\n" +
" INT_X10Y236/INT_NODE_SDQ_29_INT_OUT1 ( 1) INT_X10Y236/INT.LOGIC_OUTS_E21->INT_NODE_SDQ_29_INT_OUT1\n" +
" { INT_X10Y236/NN2_E_BEG5 ( 0) INT_X10Y236/INT.INT_NODE_SDQ_29_INT_OUT1->>NN2_E_BEG5\n" +
" { INT_X10Y238/INT_NODE_IMUX_18_INT_OUT1 ( 4) INT_X10Y238/INT.NN2_E_END5->>INT_NODE_IMUX_18_INT_OUT1\n" +
" } INT_X10Y238/IMUX_E10 ( 4) INT_X10Y238/INT.INT_NODE_IMUX_18_INT_OUT1->>IMUX_E10\n" +
" { INT_X10Y238/INT_NODE_SDQ_30_INT_OUT1 ( 2) INT_X10Y238/INT.NN2_E_END5->INT_NODE_SDQ_30_INT_OUT1\n" +
" INT_X10Y238/EE1_E_BEG5 ( 0) INT_X10Y238/INT.INT_NODE_SDQ_30_INT_OUT1->>EE1_E_BEG5\n" +
" { INT_X11Y238/INT_NODE_SDQ_79_INT_OUT0 ( 0) INT_X11Y238/INT.EE1_E_END5->INT_NODE_SDQ_79_INT_OUT0\n" +
" INT_X11Y238/SS1_W_BEG5 ( 2) INT_X11Y238/INT.INT_NODE_SDQ_79_INT_OUT0->>SS1_W_BEG5\n" +
" INT_X11Y237/INT_NODE_IMUX_49_INT_OUT1 ( 4) INT_X11Y237/INT.SS1_W_END5->>INT_NODE_IMUX_49_INT_OUT1\n" +
" INT_X11Y237/BYPASS_W8 ( 5) INT_X11Y237/INT.INT_NODE_IMUX_49_INT_OUT1->>BYPASS_W8\n" +
" { INT_X11Y237/INT_NODE_IMUX_36_INT_OUT0 ( 0) INT_X11Y237/INT.BYPASS_W8->>INT_NODE_IMUX_36_INT_OUT0\n" +
" } INT_X11Y237/IMUX_W6 ( 2) INT_X11Y237/INT.INT_NODE_IMUX_36_INT_OUT0->>IMUX_W6\n" +
" { INT_X11Y237/INT_NODE_IMUX_37_INT_OUT0 ( 0) INT_X11Y237/INT.INT_NODE_IMUX_37_INT_OUT0<<->>BYPASS_W8\n" +
" } INT_X11Y237/IMUX_W7 ( 1) INT_X11Y237/INT.INT_NODE_IMUX_37_INT_OUT0->>IMUX_W7\n" +
" INT_X11Y237/INT_NODE_IMUX_36_INT_OUT1 ( 0) INT_X11Y237/INT.BYPASS_W8->>INT_NODE_IMUX_36_INT_OUT1\n" +
" { } INT_X11Y237/IMUX_W2 ( 4) INT_X11Y237/INT.INT_NODE_IMUX_36_INT_OUT1->>IMUX_W2\n" +
" { } INT_X11Y237/IMUX_W3 ( 2) INT_X11Y237/INT.INT_NODE_IMUX_36_INT_OUT1->>IMUX_W3\n" +
" INT_X11Y237/BOUNCE_W_2_FT1 ( 4) INT_X11Y237/INT.INT_NODE_IMUX_36_INT_OUT1->>BOUNCE_W_2_FT1\n" +
" INT_X11Y236/INODE_W_58_FT0 ( 0) INT_X11Y236/INT.BOUNCE_W_BLS_2_FT0->>INODE_W_58_FT0\n" +
" { } INT_X11Y237/IMUX_W0 ( 4) INT_X11Y237/INT.INODE_W_BLN_58_FT1->>IMUX_W0\n" +
" } INT_X11Y237/IMUX_W1 ( 2) INT_X11Y237/INT.INODE_W_BLN_58_FT1->>IMUX_W1\n" +
" INT_X11Y238/INT_NODE_IMUX_50_INT_OUT1 ( 1) INT_X11Y238/INT.EE1_E_END5->>INT_NODE_IMUX_50_INT_OUT1\n" +
" { INT_X11Y238/BYPASS_W10 ( 4) INT_X11Y238/INT.INT_NODE_IMUX_50_INT_OUT1->>BYPASS_W10\n" +
" INT_X11Y238/INT_NODE_IMUX_40_INT_OUT1 ( 0) INT_X11Y238/INT.BYPASS_W10->>INT_NODE_IMUX_40_INT_OUT1\n" +
" } INT_X11Y238/IMUX_W26 ( 4) INT_X11Y238/INT.INT_NODE_IMUX_40_INT_OUT1->>IMUX_W26\n" +
" } INT_X11Y238/IMUX_W36 ( 4) INT_X11Y238/INT.INT_NODE_IMUX_50_INT_OUT1->>IMUX_W36\n" +
" INT_X10Y238/INT_NODE_SDQ_30_INT_OUT0 ( 3) INT_X10Y238/INT.NN2_E_END5->INT_NODE_SDQ_30_INT_OUT0\n" +
" INT_X10Y238/NN1_E_BEG5 ( 2) INT_X10Y238/INT.INT_NODE_SDQ_30_INT_OUT0->>NN1_E_BEG5\n" +
" INT_X10Y239/INT_NODE_SDQ_30_INT_OUT0 ( 2) INT_X10Y239/INT.NN1_E_END5->INT_NODE_SDQ_30_INT_OUT0\n" +
" INT_X10Y239/EE2_E_BEG5 ( 3) INT_X10Y239/INT.INT_NODE_SDQ_30_INT_OUT0->>EE2_E_BEG5\n" +
" INT_X11Y239/INT_NODE_SDQ_27_INT_OUT0 ( 0) INT_X11Y239/INT.EE2_E_END5->INT_NODE_SDQ_27_INT_OUT0\n" +
" INT_X11Y239/INT_INT_SDQ_74_INT_OUT0 ( 2) INT_X11Y239/INT.INT_NODE_SDQ_27_INT_OUT0->>INT_INT_SDQ_74_INT_OUT0\n" +
" INT_X11Y239/INT_NODE_SDQ_73_INT_OUT0 ( 0) INT_X11Y239/INT.INT_INT_SDQ_74_INT_OUT0->INT_NODE_SDQ_73_INT_OUT0\n" +
" INT_X11Y239/SS1_W_BEG4 ( 3) INT_X11Y239/INT.INT_NODE_SDQ_73_INT_OUT0->>SS1_W_BEG4\n" +
" INT_X11Y238/INT_NODE_SDQ_72_INT_OUT0 ( 2) INT_X11Y238/INT.SS1_W_END4->INT_NODE_SDQ_72_INT_OUT0\n" +
" INT_X11Y238/SS1_W_BEG4 ( 2) INT_X11Y238/INT.INT_NODE_SDQ_72_INT_OUT0->>SS1_W_BEG4\n" +
" INT_X11Y237/INT_NODE_IMUX_46_INT_OUT0 ( 4) INT_X11Y237/INT.SS1_W_END4->>INT_NODE_IMUX_46_INT_OUT0\n" +
" { } INT_X11Y237/IMUX_W10 ( 2) INT_X11Y237/INT.INT_NODE_IMUX_46_INT_OUT0->>IMUX_W10\n" +
" } INT_X11Y237/IMUX_W11 ( 2) INT_X11Y237/INT.INT_NODE_IMUX_46_INT_OUT0->>IMUX_W11\n" +
" INT_X10Y236/INT_INT_SDQ_34_INT_OUT1 ( 0) INT_X10Y236/INT.INT_NODE_SDQ_29_INT_OUT1->>INT_INT_SDQ_34_INT_OUT1\n" +
" INT_X10Y236/INT_NODE_SDQ_82_INT_OUT0 ( 0) INT_X10Y236/INT.INT_INT_SDQ_34_INT_OUT1->INT_NODE_SDQ_82_INT_OUT0\n" +
" INT_X10Y236/INT_INT_SDQ_6_INT_OUT0 ( 2) INT_X10Y236/INT.INT_NODE_SDQ_82_INT_OUT0->>INT_INT_SDQ_6_INT_OUT0\n" +
" { INT_X10Y236/INT_NODE_GLOBAL_6_INT_OUT1 ( 4) INT_X10Y236/INT.INT_INT_SDQ_6_INT_OUT0->>INT_NODE_GLOBAL_6_INT_OUT1\n" +
" INT_X10Y236/INT_NODE_IMUX_9_INT_OUT0 ( 2) INT_X10Y236/INT.INT_NODE_GLOBAL_6_INT_OUT1->>INT_NODE_IMUX_9_INT_OUT0\n" +
" } INT_X10Y236/IMUX_E30 ( 6) INT_X10Y236/INT.INT_NODE_IMUX_9_INT_OUT0->>IMUX_E30\n" +
" INT_X10Y236/INT_NODE_IMUX_18_INT_OUT1 ( 1) INT_X10Y236/INT.INT_INT_SDQ_6_INT_OUT0->>INT_NODE_IMUX_18_INT_OUT1\n" +
" }] INT_X10Y236/IMUX_E35 ( 3) INT_X10Y236/INT.INT_NODE_IMUX_18_INT_OUT1->>IMUX_E35\n").split("\n");
String[] actual = trees.get(0).toString().split("\n");
Assertions.assertEquals(expected.length, actual.length);
for (int i = 0; i < expected.length; i++) {
// Remove all text after the first round bracket
int firstRoundBracket = expected[i].indexOf("(");
String expectedNodeOnly = expected[i].substring(0, firstRoundBracket - 1);
Assertions.assertEquals(expectedNodeOnly, actual[i]);
}
}
}
Loading