diff --git a/src/com/xilinx/rapidwright/rwroute/RWRoute.java b/src/com/xilinx/rapidwright/rwroute/RWRoute.java index 8e352e392..d7819abda 100644 --- a/src/com/xilinx/rapidwright/rwroute/RWRoute.java +++ b/src/com/xilinx/rapidwright/rwroute/RWRoute.java @@ -482,7 +482,8 @@ protected void routeStaticNets() { List gndPins = staticNetAndRoutingTargets.get(design.getGndNet()); if (gndPins != null) { - Set newVccPins = RouterHelper.invertPossibleGndPinsToVccPins(design, gndPins); + boolean invertGndToVccForLutInputs = config.isInvertGndToVccForLutInputs(); + Set newVccPins = RouterHelper.invertPossibleGndPinsToVccPins(design, gndPins, invertGndToVccForLutInputs); if (!newVccPins.isEmpty()) { gndPins.removeAll(newVccPins); staticNetAndRoutingTargets.computeIfAbsent(design.getVccNet(), (net) -> new ArrayList<>()) diff --git a/src/com/xilinx/rapidwright/rwroute/RWRouteConfig.java b/src/com/xilinx/rapidwright/rwroute/RWRouteConfig.java index c96b3cd75..1812030be 100644 --- a/src/com/xilinx/rapidwright/rwroute/RWRouteConfig.java +++ b/src/com/xilinx/rapidwright/rwroute/RWRouteConfig.java @@ -1,7 +1,7 @@ /* * * Copyright (c) 2021 Ghent University. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. * All rights reserved. * * Author: Yun Zhou, Ghent University. @@ -92,6 +92,8 @@ public class RWRouteConfig { private boolean lutPinSwapping; /* true to enable LUT routethru */ private boolean lutRoutethru; + /* true to enable GND -> VCC optimization for LUT inputs */ + private boolean invertGndToVccForLutInputs; /** Constructs a Configuration Object */ public RWRouteConfig(String[] arguments) { @@ -122,6 +124,7 @@ public RWRouteConfig(String[] arguments) { printConnectionSpan = false; lutPinSwapping = false; lutRoutethru = false; + invertGndToVccForLutInputs = true; if (arguments != null) { parseArguments(arguments); } @@ -227,6 +230,9 @@ private void parseArguments(String[] arguments) { case "--lutRoutethru": setLutRoutethru(true); break; + case "--noInvertGndToVccForLutInputs": + setInvertGndToVccForLutInputs(false); + break; default: throw new IllegalArgumentException("ERROR: RWRoute argument '" + arg + "' not recognized."); } @@ -737,6 +743,16 @@ public boolean isLutRoutethru() { return lutRoutethru; } + /** + * Gets the flag indicating if GND to VCC inversion for LUT inputs is enabled. + * Default: true. + * + * @return True if the flag is set, false otherwise. + */ + public boolean isInvertGndToVccForLutInputs() { + return invertGndToVccForLutInputs; + } + /** * Sets critical path delay pessimism factor b. It should be greater than 0. * Default: 100. Can be modified by using "--pessimismB" option, e.g. @@ -850,6 +866,16 @@ public void setLutRoutethru(boolean lutRoutethru) { this.lutRoutethru = lutRoutethru; } + /** + * Sets the flag for enabling GND to VCC inversion for LUT inputs. + * Default: true. + * + * @param invertGndToVccForLutInputs true to enableGND to VCC inversion for LUT inputs. + */ + public void setInvertGndToVccForLutInputs(boolean invertGndToVccForLutInputs) { + this.invertGndToVccForLutInputs = invertGndToVccForLutInputs; + } + /** * Sets verbose. * If true, there will be more info in the routing log file regarding design netlist, routing statistics, and timing report. diff --git a/src/com/xilinx/rapidwright/rwroute/RouterHelper.java b/src/com/xilinx/rapidwright/rwroute/RouterHelper.java index 496f07786..f51bd02e7 100644 --- a/src/com/xilinx/rapidwright/rwroute/RouterHelper.java +++ b/src/com/xilinx/rapidwright/rwroute/RouterHelper.java @@ -1,7 +1,7 @@ /* * * Copyright (c) 2021 Ghent University. - * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. + * Copyright (c) 2022-2024, Advanced Micro Devices, Inc. * All rights reserved. * * Author: Yun Zhou, Ghent University. @@ -39,10 +39,14 @@ import java.util.Queue; import java.util.Set; +import com.xilinx.rapidwright.design.Cell; import com.xilinx.rapidwright.design.Design; import com.xilinx.rapidwright.design.DesignTools; import com.xilinx.rapidwright.design.Net; +import com.xilinx.rapidwright.design.SiteInst; import com.xilinx.rapidwright.design.SitePinInst; +import com.xilinx.rapidwright.design.tools.LUTTools; +import com.xilinx.rapidwright.device.BEL; import com.xilinx.rapidwright.device.BELPin; import com.xilinx.rapidwright.device.IntentCode; import com.xilinx.rapidwright.device.Node; @@ -50,6 +54,7 @@ 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; import com.xilinx.rapidwright.timing.delayestimator.DelayEstimatorBase; @@ -366,43 +371,121 @@ private static boolean isInvertibleDSPBELPin(BELPin belPin) { /** * Inverts all possible GND sink pins to VCC pins. * @param design The target design. - * @param pins The static net pins. + * @param pins The GND net pins. */ public static Set invertPossibleGndPinsToVccPins(Design design, List pins) { - Net staticNet = design.getGndNet(); + return invertPossibleGndPinsToVccPins(design, pins, true); + } + + /** + * Inverts all possible GND sink pins to VCC pins. + * @param design The target design. + * @param pins The GND net pins. + * @param invertLutInputs True to invert LUT inputs. + */ + public static Set invertPossibleGndPinsToVccPins(Design design, + List pins, + boolean invertLutInputs) { + Net gndNet = design.getGndNet(); Set toInvertPins = new HashSet<>(); - for (SitePinInst currSitePinInst : pins) { - if (!currSitePinInst.getNet().equals(staticNet)) - throw new RuntimeException(currSitePinInst.toString()); - BELPin[] belPins = currSitePinInst.getSiteInst().getSiteWirePins(currSitePinInst.getName()); - if (belPins.length != 2) { - continue; - } - for (BELPin belPin : belPins) { - if (belPin.isSitePort()) { - continue; + nextSitePin: for (SitePinInst spi : pins) { + if (!spi.getNet().equals(gndNet)) + throw new RuntimeException(spi.toString()); + SiteInst si = spi.getSiteInst(); + String siteWireName = spi.getSiteWireName(); + if (invertLutInputs && spi.isLUTInputPin()) { + Collection connectedCells = DesignTools.getConnectedCells(spi); + if (connectedCells.isEmpty()) { + for (BELPin belPin : si.getSiteWirePins(siteWireName)) { + if (belPin.isSitePort()) { + continue; + } + BEL bel = belPin.getBEL(); + Cell cell = si.getCell(bel); + if (cell == null) { + continue; + } + if (cell.getType().equals("SRL16E") && siteWireName.endsWith("6")) { + // SRL16Es that have been transformed from SRLC32E (assume so here, + // since we don't always have the logical netlist to check) + // require GND on their A6 pin + // See DesignTools.createMissingStaticSitePins(BELPin, SiteInst, Cell) + continue nextSitePin; + } + } + throw new RuntimeException("ERROR: " + gndNet.getName() + " not connected to any Cells"); } - if (!belPin.getBEL().canInvert()) { + for (Cell cell : connectedCells) { + if (!LUTTools.isCellALUT(cell)) { + continue nextSitePin; + } + + EDIFHierCellInst ehci = cell.getEDIFHierCellInst(); + if (ehci == null) { + // No logical cell (likely encrypted) + continue nextSitePin; + } + + if (!ehci.getParent().isUniquified()) { + // Parent cell (instantiating this LUT) is not unique + // This parent may be a LUT6_2 macro cell that has been expanded into LUT6+LUT5, + // and which does not get uniquified by EDIFTools.uniqueifyNetlist(). + // Thus, LUT6/LUT5 inside expanded LUT6_2 macros are not eligible for inversion. + continue nextSitePin; + } + } + + toInvertPins.add(spi); + + for (Cell cell : connectedCells) { + // Find the logical pin name + String physicalPinName = "A" + spi.getName().charAt(1); + String logicalPinName = cell.getLogicalPinMapping(physicalPinName); + + // Get the LUT equation + String lutEquation = LUTTools.getLUTEquation(cell); + assert(lutEquation.contains(logicalPinName)); + + // Compute a new LUT equation with that logical input inverted + String newLutEquation = lutEquation.replace(logicalPinName, "!" + logicalPinName) + // Cancel out double inversions + // (Note: LUTTools.getLUTEquation() only produces equations with '!' instead of '~') + .replace("!!", ""); + LUTTools.configureLUT(cell, newLutEquation); + } + } else { + BELPin[] belPins = si.getSiteWirePins(siteWireName); + if (belPins.length != 2) { continue; } - if (currSitePinInst.getSite().getName().startsWith("RAM")) { - if (belPin.getBELName().startsWith("CLK")) { + for (BELPin belPin : belPins) { + if (belPin.isSitePort()) { + continue; + } + if (!belPin.getBEL().canInvert()) { continue; } + if (spi.getSite().getName().startsWith("RAM")) { + if (belPin.getBELName().startsWith("CLK")) { + continue; + } + } + toInvertPins.add(spi); } - toInvertPins.add(currSitePinInst); - } + } } // Unroute all pins in a batch fashion - DesignTools.unroutePins(staticNet, toInvertPins); + DesignTools.unroutePins(gndNet, toInvertPins); // Manually remove pins from net, because using DesignTools.batchRemoveSitePins() // will cause SitePinInst.detachSiteInst() to be called, which we do not want // as we are simply moving the SPI from one net to another - staticNet.getPins().removeAll(toInvertPins); + gndNet.getPins().removeAll(toInvertPins); + + Net vccNet = design.getVccNet(); for (SitePinInst toinvert:toInvertPins) { assert(toinvert.getSiteInst() != null); - if (!design.getVccNet().addPin(toinvert)) { + if (!vccNet.addPin(toinvert)) { throw new RuntimeException("ERROR: Couldn't invert site pin " + toinvert); } diff --git a/test/shared/com/xilinx/rapidwright/support/rwroute/RouterHelperSupport.java b/test/shared/com/xilinx/rapidwright/support/rwroute/RouterHelperSupport.java new file mode 100644 index 000000000..c1664c4e8 --- /dev/null +++ b/test/shared/com/xilinx/rapidwright/support/rwroute/RouterHelperSupport.java @@ -0,0 +1,60 @@ +/* + * 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.support.rwroute; + +import com.xilinx.rapidwright.design.Cell; +import com.xilinx.rapidwright.design.Design; +import com.xilinx.rapidwright.design.SiteInst; +import com.xilinx.rapidwright.design.SitePinInst; +import com.xilinx.rapidwright.design.tools.LUTTools; +import com.xilinx.rapidwright.device.BELPin; + +import java.util.Set; + +public class RouterHelperSupport { + public static void invertVccLutPinsToGndPins(Design design, Set pins) { + for (SitePinInst spi : pins) { + assert (spi.getNet() == design.getVccNet()); + SiteInst si = spi.getSiteInst(); + for (BELPin bp : spi.getSiteWireBELPins()) { + if (bp.isSitePort() || bp.getName().charAt(0) != 'A') + continue; + if (bp.getBEL().isLUT()) { + Cell lut = si.getCell(bp.getBEL()); + if (lut != null) { + String eq = LUTTools.getLUTEquation(lut); + String logInput = lut.getLogicalPinMapping(bp.getName()); + if (logInput != null) { + LUTTools.configureLUT(lut, eq.replace(logInput, "(~" + logInput + ")")); + } else { + // Doesn't look like this pin is used by this [65]LUT, + // could be used by the other [56]LUT + } + } + } + } + spi.getNet().removePin(spi, true); + design.getGndNet().addPin(spi, true); + } + } +} diff --git a/test/src/com/xilinx/rapidwright/edif/TestEDIFCellInst.java b/test/src/com/xilinx/rapidwright/edif/TestEDIFCellInst.java index 57c8be9ee..e54272891 100644 --- a/test/src/com/xilinx/rapidwright/edif/TestEDIFCellInst.java +++ b/test/src/com/xilinx/rapidwright/edif/TestEDIFCellInst.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Advanced Micro Devices, Inc. + * Copyright (c) 2023-2024, Advanced Micro Devices, Inc. * All rights reserved. * * Author: Eddie Hung, Advanced Micro Devices, Inc. @@ -78,7 +78,7 @@ public void testIsUniquifiedFalse() { )) { EDIFCellInst eci = picoblazeTop.getCellInst(name); // Only checks that this cell instance is unique (e.g. that there is only - // one instantiation) but does not check that any parents on a full + // one instantiation) but does not check that all parents on a full // hierarchical path (e.g. "picoblaze_{0,1}_{12,13}/processor") is also // unique --- use EDIFHierCellInst.isUniquified() for that Assertions.assertTrue(eci.isUniquified()); diff --git a/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java b/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java index ea03cae3c..da7ab95c4 100644 --- a/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java +++ b/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java @@ -336,7 +336,7 @@ public void testRWRouteDeviceSupport(Series series) { public void testBug701() { Design design = RapidWrightDCP.loadDCP("bug701.dcp"); - RWRoute.routeDesignFullNonTimingDriven(design); + RWRoute.routeDesignWithUserDefinedArguments(design, new String[] {"--nonTimingDriven", "--noInvertGndToVccForLutInputs"}); Net vcc = design.getVccNet(); Assertions.assertEquals(1, vcc.getPins().size()); diff --git a/test/src/com/xilinx/rapidwright/rwroute/TestRouterHelper.java b/test/src/com/xilinx/rapidwright/rwroute/TestRouterHelper.java index f4b82e12b..7f75f0751 100644 --- a/test/src/com/xilinx/rapidwright/rwroute/TestRouterHelper.java +++ b/test/src/com/xilinx/rapidwright/rwroute/TestRouterHelper.java @@ -22,23 +22,33 @@ package com.xilinx.rapidwright.rwroute; +import com.xilinx.rapidwright.design.Cell; import com.xilinx.rapidwright.design.Design; import com.xilinx.rapidwright.design.Net; import com.xilinx.rapidwright.design.SiteInst; import com.xilinx.rapidwright.design.SitePinInst; +import com.xilinx.rapidwright.design.Unisim; +import com.xilinx.rapidwright.design.tools.LUTTools; import com.xilinx.rapidwright.device.Node; +import com.xilinx.rapidwright.edif.EDIFTools; +import com.xilinx.rapidwright.support.RapidWrightDCP; +import com.xilinx.rapidwright.support.rwroute.RouterHelperSupport; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.stream.Stream; public class TestRouterHelper { @@ -199,7 +209,103 @@ public void testProjectOutputPinToINTNodeBitslice() { SitePinInst p = new SitePinInst("TX_T_OUT", si); Node intNode = RouterHelper.projectOutputPinToINTNode(p); - // FIXME:Known broken -- https://github.com/Xilinx/RapidWright/issues/558 + // FIXME: Known broken -- https://github.com/Xilinx/RapidWright/issues/558 Assertions.assertNotEquals(Objects.toString(intNode), "INT_INTF_L_CMT_X182Y90/LOGIC_OUTS_R19"); } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void testInvertPossibleGndPinsToVccPinsLutInput(boolean invertLutInputs) { + Design design = new Design("design", "xcvu3p"); + Cell cell = design.createAndPlaceCell("lut", Unisim.LUT1, "SLICE_X0Y0/A6LUT"); + LUTTools.configureLUT(cell, "O=~I0"); + Assertions.assertEquals("O=!I0", LUTTools.getLUTEquation(cell)); + + Net gndNet = design.getGndNet(); + gndNet.createPin("A6", cell.getSiteInst()); + + // Check A6 was inverted, and it was moved off gndNet + Set invertedPins = RouterHelper.invertPossibleGndPinsToVccPins(design, gndNet.getPins(), invertLutInputs); + if (invertLutInputs) { + Assertions.assertEquals("[IN SLICE_X0Y0.A6]", invertedPins.toString()); + } else { + Assertions.assertTrue(invertedPins.isEmpty()); + } + Assertions.assertEquals(invertLutInputs, gndNet.getPins().isEmpty()); + + Net targetNet = invertLutInputs ? design.getVccNet() : design.getGndNet(); + Net sourceNet = !invertLutInputs ? design.getVccNet() : design.getGndNet(); + Assertions.assertEquals("[IN SLICE_X0Y0.A6]", targetNet.getPins().toString()); + Assertions.assertTrue(sourceNet.getPins().isEmpty()); + if (invertLutInputs) { + // Must have moved onto vccNet, and the LUT mask inverted + Assertions.assertEquals("O=I0", LUTTools.getLUTEquation(cell)); + + // Now undo this optimization by going from VCC pin back to GND pin + RouterHelperSupport.invertVccLutPinsToGndPins(design, invertedPins); + + // Check that pin is back on the original VCC net + Assertions.assertTrue(targetNet.getPins().isEmpty()); + Assertions.assertEquals("[IN SLICE_X0Y0.A6]", sourceNet.getPins().toString()); + } + + // Check that LUT equation is back to normal + Assertions.assertEquals("O=!I0", LUTTools.getLUTEquation(cell)); + } + + @ParameterizedTest + @CsvSource({"" + + "false,false", + "false,true", + "true,false", + "true,true" + }) + public void testInvertPossibleGndPinsToVccPinsLutInputOnlyIfFlattenedAndUniquified(boolean flatten, boolean uniquify) { + Design design = RapidWrightDCP.loadDCP("picoblaze4_ooc_X6Y60_X6Y65_X10Y60_X10Y65.dcp"); + + Assertions.assertEquals(1, design.getModules().size()); + Assertions.assertEquals(4, design.getModuleInsts().size()); + + if (flatten) { + // Since ModuleInst-s (and indeed Vivado's write_edif) can create folded netlists, + // completely flatten the design (required for ModuleInst designs) as well as uniqueify + // all leaf cells so that modifying a LUT's INIT mask does not inadvertently modify + // masks for other leaf cells + design.flattenDesign(); + + Assertions.assertEquals(0, design.getModules().size()); + Assertions.assertEquals(0, design.getModuleInsts().size()); + } else { + // Not flattening nor uniquifying means that less/no opportunities exist for making + // GND -> VCC transformations as they are only applied to uniquified LUTs. + // It's assumed/expected that Vivado will pick up at least one error when RWRoute + // incorrectly inverts a non-uniquified LUT + } + + if (uniquify) { + Boolean result = EDIFTools.uniqueifyNetlist(design); + if (!flatten && uniquify) { + // Cannot uniqueify without flattening -- skip test if this is the case + Assumptions.assumeTrue(result != null); + } + Assertions.assertTrue(result); + } + + RWRoute.preprocess(design); + + Net gndNet = design.getGndNet(); + List gndLutPins = new ArrayList<>(); + for (SitePinInst spi : gndNet.getPins()) { + if (!spi.isLUTInputPin()) { + continue; + } + gndLutPins.add(spi); + } + Assertions.assertFalse(gndLutPins.isEmpty()); + + Set invertedPins = RouterHelper.invertPossibleGndPinsToVccPins(design, gndLutPins); + + // If not flattening/uniquifying, there must be no inverted pins + Assertions.assertEquals(!flatten || !uniquify, invertedPins.isEmpty()); + } }