diff --git a/src/com/xilinx/rapidwright/MainEntrypoint.java b/src/com/xilinx/rapidwright/MainEntrypoint.java index 12d80c350..6daad6dd1 100644 --- a/src/com/xilinx/rapidwright/MainEntrypoint.java +++ b/src/com/xilinx/rapidwright/MainEntrypoint.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2021-2022, Xilinx, Inc. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. * All rights reserved. * * Author: Jakob Wenzel, Xilinx Research Labs. @@ -103,6 +103,7 @@ import com.xilinx.rapidwright.util.PartPrinter; import com.xilinx.rapidwright.util.PerformanceExplorer; import com.xilinx.rapidwright.util.ReplaceEDIFInDCP; +import com.xilinx.rapidwright.util.ReportRouteStatus; import com.xilinx.rapidwright.util.StringTools; import com.xilinx.rapidwright.util.Unzip; import com.xilinx.rapidwright.util.performance_evaluation.PerformanceEvaluation; @@ -186,6 +187,7 @@ private static void addFunction(String name, MainStyleFunction func) { addFunction("RelocationTools", RelocationTools::main); addFunction("ReplaceEDIFInDCP", ReplaceEDIFInDCP::main); addFunction("ReportDevicePerformance", ReportDevicePerformance::main); + addFunction("ReportRouteStatus", ReportRouteStatus::main); addFunction("ReportTimingExample", ReportTimingExample::main); addFunction("Router", Router::main); addFunction("RouteThruHelper", RouteThruHelper::main); diff --git a/src/com/xilinx/rapidwright/util/ReportRouteStatus.java b/src/com/xilinx/rapidwright/util/ReportRouteStatus.java new file mode 100644 index 000000000..74cf22f43 --- /dev/null +++ b/src/com/xilinx/rapidwright/util/ReportRouteStatus.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Author: Eddie Hung, Advanced Micro Devices, Inc. + * + * This file is part of RapidWright. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.xilinx.rapidwright.util; + +import com.xilinx.rapidwright.design.Design; +import com.xilinx.rapidwright.design.DesignTools; +import com.xilinx.rapidwright.design.Net; +import com.xilinx.rapidwright.design.SitePinInst; +import com.xilinx.rapidwright.device.Node; +import com.xilinx.rapidwright.device.PIP; +import com.xilinx.rapidwright.rwroute.RouterHelper; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class ReportRouteStatus { + /** + * Compute the route status of given Design's physical nets by examining the + * {@link SitePinInst#isRouted()} state of each net's pins, as well as to discovering node conflicts + * between each net's PIPs. + * Freshly loaded designs, as well as designs that are not up-to-date, can call + * {@link DesignTools#updatePinsIsRouted(Design)} for recomputing the SitePinInst.isRouted() state. + * Note that currently this method does not check the Design's logical netlist nor its physical + * placement --- these are assumed to be correct. + * @param design Design to examine. + * @return ReportRouteStatusResult object. + */ + public static ReportRouteStatusResult reportRouteStatus(Design design) { + ReportRouteStatusResult rrs = new ReportRouteStatusResult(); + + Map nodesUsedByDesign = new HashMap<>(); + Set conflictingNets = new HashSet<>(); + + Collection nets = design.getNets(); + for (Net net : nets) { + if (!net.isStaticNet() && !RouterHelper.isRoutableNetWithSourceSinks(net)) { + rrs.netsNotNeedingRouting++; + continue; + } + rrs.routableNets++; + + boolean isFullyRouted = true; + boolean isPartiallyRouted = false; + for (SitePinInst spi : net.getPins()) { + if (spi.isRouted()) { + isPartiallyRouted = true; + continue; + } + isFullyRouted = false; + } + + boolean isConflictFree = true; + for (PIP pip : net.getPIPs()) { + Node endNode = pip.isReversed() ? pip.getStartNode() : pip.getEndNode(); + Net conflictingNet = nodesUsedByDesign.putIfAbsent(endNode, net); + if (conflictingNet != null && conflictingNet != net) { + conflictingNets.add(conflictingNet); + isConflictFree = false; + } + } + + if (!isConflictFree) { + conflictingNets.add(net); + } else if (!isFullyRouted) { + if (isPartiallyRouted) { + rrs.netsWithSomeUnroutedPins++; + } else { + rrs.unroutedNets++; + } + } + } + + rrs.logicalNets = nets.size(); + rrs.netsWithResourceConflicts = conflictingNets.size(); + rrs.netsWithRoutingErrors = rrs.netsWithSomeUnroutedPins + rrs.netsWithResourceConflicts; + rrs.fullyRoutedNets = rrs.routableNets - rrs.unroutedNets - rrs.netsWithRoutingErrors; + return rrs; + } + + public static void main(String[] args) { + if (args.length != 1) { + System.out.println("USAGE: ReportRouteStatus "); + System.exit(1); + } + + Design design = Design.readCheckpoint(args[0]); + + DesignTools.updatePinsIsRouted(design); + + ReportRouteStatusResult rrs = reportRouteStatus(design); + + System.out.println(); + System.out.println(rrs.toString("RapidWright Design Route Status")); + if (!rrs.isFullyRouted()) { + throw new RuntimeException("Design is not fully routed"); + } + } +} diff --git a/src/com/xilinx/rapidwright/util/ReportRouteStatusResult.java b/src/com/xilinx/rapidwright/util/ReportRouteStatusResult.java index a0de23aa7..0c3ee8228 100644 --- a/src/com/xilinx/rapidwright/util/ReportRouteStatusResult.java +++ b/src/com/xilinx/rapidwright/util/ReportRouteStatusResult.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Advanced Micro Devices, Inc. + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. * All rights reserved. * * Author: Zak Nafziger, Advanced Micro Devices, Inc. @@ -26,19 +26,20 @@ public class ReportRouteStatusResult { - public final int logicalNets; - public final int netsWithNoPlacedPins; - public final int netsNotNeedingRouting; - public final int internallyRoutedNets; - public final int netsWithNoLoads; - public final int implicitlyRoutedPorts; - public final int routableNets; - public final int unroutedNets; - public final int fullyRoutedNets; - public final int netsWithNoDriver; - public final int netsWithRoutingErrors; - public final int netsWithSomeUnplacedPins; - public final int netsWithSomeUnroutedPins; + public int logicalNets; + public int netsWithNoPlacedPins; + public int netsNotNeedingRouting; + public int internallyRoutedNets; + public int netsWithNoLoads; + public int implicitlyRoutedPorts; + public int routableNets; + public int unroutedNets; + public int fullyRoutedNets; + public int netsWithNoDriver; + public int netsWithRoutingErrors; + public int netsWithSomeUnplacedPins; + public int netsWithSomeUnroutedPins; + public int netsWithResourceConflicts; private static int parseLog(List log, String key) { List matchingLines = VivadoTools.searchVivadoLog(log, key); @@ -49,6 +50,9 @@ private static int parseLog(List log, String key) { return Integer.parseInt(matchingLines.get(0).replaceAll("[^\\d]", "")); } + public ReportRouteStatusResult() { + } + /** * Analyze a log file produced by Vivado's `report_route_status` * command. @@ -69,10 +73,49 @@ public ReportRouteStatusResult(List log) { netsWithRoutingErrors = parseLog(log, "# of nets with routing errors"); netsWithSomeUnplacedPins = parseLog(log, "# of nets with some unplaced pins"); netsWithSomeUnroutedPins = parseLog(log, "# of nets with some unrouted pins"); + netsWithResourceConflicts = parseLog(log, "# of nets with resource conflicts"); } public boolean isFullyRouted() { return logicalNets > 0 && unroutedNets == 0 && netsWithRoutingErrors == 0; } + @Override + public String toString() { + return toString("Design Route Status"); + } + + public String toString(String title) { + StringBuilder sb = new StringBuilder(); + sb.append(title); + sb.append("\n"); + sb.append(" : # nets :\n"); + sb.append(" ------------------------------------------- : ----------- :\n"); + sb.append(String.format(" # of logical nets.......................... : %11d :\n", logicalNets)); + sb.append(String.format(" # of nets not needing routing.......... : %11d :\n", netsNotNeedingRouting)); + if (internallyRoutedNets > 0) { + sb.append(String.format(" # of internally routed nets........ : %11d :\n", internallyRoutedNets)); + } + if (netsWithNoLoads > 0) { + sb.append(String.format(" # of nets with no loads............ : %11d :\n", netsWithNoLoads)); + } + if (implicitlyRoutedPorts > 0) { + sb.append(String.format(" # of implicitly routed ports....... : %11d :\n", implicitlyRoutedPorts)); + } + sb.append(String.format(" # of routable nets..................... : %11d :\n", routableNets)); + if (unroutedNets > 0) { + sb.append(String.format(" # of unrouted nets................. : %11d :\n", unroutedNets)); + } + sb.append(String.format(" # of fully routed nets............. : %11d :\n", fullyRoutedNets)); + sb.append(String.format(" # of nets with routing errors.......... : %11d :\n", netsWithRoutingErrors)); + if (netsWithSomeUnroutedPins > 0) { + sb.append(String.format(" # of nets with some unrouted pins.. : %11d :\n", netsWithSomeUnroutedPins)); + } + if (netsWithResourceConflicts > 0) { + sb.append(String.format(" # of nets with resource conflicts.. : %11d :\n", netsWithResourceConflicts)); + } + sb.append(" ------------------------------------------- : ----------- :"); + return sb.toString(); + } + } diff --git a/test/src/com/xilinx/rapidwright/util/TestReportRouteStatus.java b/test/src/com/xilinx/rapidwright/util/TestReportRouteStatus.java new file mode 100644 index 000000000..2b67bf4bf --- /dev/null +++ b/test/src/com/xilinx/rapidwright/util/TestReportRouteStatus.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Author: Eddie Hung, Advanced Micro Devices, Inc. + * + * This file is part of RapidWright. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.xilinx.rapidwright.util; + +import com.xilinx.rapidwright.design.Design; +import com.xilinx.rapidwright.design.DesignTools; +import com.xilinx.rapidwright.support.RapidWrightDCP; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestReportRouteStatus { + @Test + public void testReportRouteStatusMain() { + String path = RapidWrightDCP.getString("picoblaze_ooc_X10Y235.dcp"); + ReportRouteStatus.main(new String[]{path}); + } + + @Test + public void testReportRouteStatus() { + Design design = RapidWrightDCP.loadDCP("optical-flow.dcp"); + DesignTools.createMissingSitePinInsts(design); + ReportRouteStatusResult rrs = ReportRouteStatus.reportRouteStatus(design); + Assertions.assertEquals(185996, rrs.logicalNets); + Assertions.assertEquals(58865, rrs.routableNets); + Assertions.assertEquals(58865, rrs.unroutedNets); + Assertions.assertEquals(0, rrs.netsWithRoutingErrors); + } +}