diff --git a/src/com/xilinx/rapidwright/eco/ECOTools.java b/src/com/xilinx/rapidwright/eco/ECOTools.java index 42bee6063..c6a18107d 100644 --- a/src/com/xilinx/rapidwright/eco/ECOTools.java +++ b/src/com/xilinx/rapidwright/eco/ECOTools.java @@ -32,6 +32,7 @@ import com.xilinx.rapidwright.device.BEL; import com.xilinx.rapidwright.device.BELClass; import com.xilinx.rapidwright.device.BELPin; +import com.xilinx.rapidwright.device.Site; import com.xilinx.rapidwright.device.SitePIP; import com.xilinx.rapidwright.edif.EDIFCell; import com.xilinx.rapidwright.edif.EDIFCellInst; @@ -53,6 +54,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -75,6 +77,19 @@ */ public class ECOTools { + private static int UNIQUE_COUNT = 0; + + /** + * Given a list of EDIFHierPortInst objects, disconnect these pins from their current nets. + * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) + * state, and is modelled on Vivado's disconnect_net -pinlist command. + * @param design The design where the pin(s) are instantiated. + * @param pins A variable array of hierarchical pins for disconnection. + */ + public static void disconnectNet(Design design, EDIFHierPortInst... pins) { + disconnectNet(design, Arrays.asList(pins), null); + } + /** * Given a list of EDIFHierPortInst objects, disconnect these pins from their current nets. * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) @@ -427,7 +442,7 @@ public static void connectNet(Design design, return v.isEmpty() ? null : v; }); } - if (!oldPhysNet.equals(newPhysNet)) { + if (!Objects.equals(oldPhysNet, newPhysNet)) { // Unroute and remove pin from old net BELPin snkBp = bel.getPin(physicalPinName); if (!si.unrouteIntraSiteNet(spi.getBELPin(), snkBp)) { @@ -435,12 +450,14 @@ public static void connectNet(Design design, spi.getSiteInst().getSiteName() + "/" + spi.getBELPin() + " to " + snkBp + "."); } boolean preserveOtherRoutes = true; - oldPhysNet.removePin(spi, preserveOtherRoutes); - if (RouterHelper.isLoadLessNet(oldPhysNet) && oldPhysNet.hasPIPs()) { - // Since oldPhysNet has no sink pins left, yet still has PIPs, then it may - // mean that a routing stub persevered. To handle such cases, unroute the - // whole net. - oldPhysNet.unroute(); + if (oldPhysNet != null) { + oldPhysNet.removePin(spi, preserveOtherRoutes); + if (RouterHelper.isLoadLessNet(oldPhysNet) && oldPhysNet.hasPIPs()) { + // Since oldPhysNet has no sink pins left, yet still has PIPs, then it may + // mean that a routing stub persevered. To handle such cases, unroute the + // whole net. + oldPhysNet.unroute(); + } } // Re-do intra-site routing and add pin to new net @@ -493,7 +510,10 @@ private static void connectNetSource(Design design, Cell cell = src.getPhysicalCell(design); for (SitePinInst spi : cell.getAllSitePinsFromLogicalPin(src.getPortInst().getName(), null)) { assert(spi.getNet() != null); - deferredRemovals.computeIfAbsent(spi.getNet(), (p) -> new HashSet<>()).add(spi); + if (deferredRemovals != null) { + deferredRemovals.computeIfAbsent(spi.getNet(), (p) -> new HashSet<>()).add(spi); + } + } src.getNet().removePortInst(src.getPortInst()); } @@ -605,18 +625,22 @@ private static void connectNetSource(Design design, if (oldPhysNet != null) { // Site pin is attached to a different physical net, move them over to the new net // (erasing them from deferredRemovals if present) - deferredRemovals.computeIfPresent(oldPhysNet, (k, v) -> { - v.remove(spi); - return v.isEmpty() ? null : v; - }); + if (deferredRemovals != null) { + deferredRemovals.computeIfPresent(oldPhysNet, (k, v) -> { + v.remove(spi); + return v.isEmpty() ? null : v; + }); + } fullyUnrouteSources(oldPhysNet); } - deferredRemovals.computeIfPresent(newPhysNet, (k, v) -> { - v.remove(k.getSource()); - v.remove(k.getAlternateSource()); - return v.isEmpty() ? null : v; - }); + if (deferredRemovals != null) { + deferredRemovals.computeIfPresent(newPhysNet, (k, v) -> { + v.remove(k.getSource()); + v.remove(k.getAlternateSource()); + return v.isEmpty() ? null : v; + }); + } fullyUnrouteSources(newPhysNet); // Add existing site pin to new net, and update intra-site routing @@ -629,11 +653,14 @@ private static void connectNetSource(Design design, si.routeIntraSiteNet(newPhysNet, siteInstBelPin.getSecond(), spi.getBELPin()); } } else { - deferredRemovals.computeIfPresent(newPhysNet, (k, v) -> { - v.remove(k.getSource()); - v.remove(k.getAlternateSource()); - return v.isEmpty() ? null : v; - }); + if (deferredRemovals != null) { + deferredRemovals.computeIfPresent(newPhysNet, (k, v) -> { + v.remove(k.getSource()); + v.remove(k.getAlternateSource()); + return v.isEmpty() ? null : v; + }); + + } fullyUnrouteSources(newPhysNet); // Create and add a new SitePinInst to new net, including @@ -643,6 +670,23 @@ private static void connectNetSource(Design design, } } + /** + * Convenience wrapper method to connectNet() that allows for minimal + * specification of a single connection to be made. + * + * @param d The design where the net exists. + * @param cell The cell on which the pin to be connected exists + * @param logPin The logical name on the cell to connect + * @param net The physical net to connect the logical pin to + */ + public static void connectNet(Design d, Cell cell, String logPin, Net net) { + Map> map = new HashMap<>(); + EDIFPortInst portInst = cell.getEDIFCellInst().getOrCreatePortInst(logPin); + map.put(net.getLogicalHierNet(),new ArrayList<>(Arrays.asList( + new EDIFHierPortInst(cell.getEDIFHierCellInst().getParent(), portInst)))); + ECOTools.connectNet(d, map, null); + } + /** * Given a list of strings containing one net path followed by one or more pin paths * separated by spaces (in line with Tcl convention) connect the latter pins to the former net. @@ -1062,6 +1106,69 @@ public static void createCell(Design design, EDIFCell reference, List pa } } + /** + * Creates and places a new primitive cell inline from the existing net driving + * the input pin provided and connects its output to the existing input. The + * cell will be created as a sibling to the instance of the provided input pin + * and will be named with the prefix 'inline_insertion_'. For example, if input + * is 'FF/D' and reference is a LUT1, this will create a new cell instance of + * type LUT1 where its input 'I0' will replace the connection 'D' on 'FF' and a + * new net will be created that will connect 'LUT1/O' to 'FF/D'. + * + * This is useful for scenarios like inserting a LUT1 inline when routethru + * instances are not possible. + * + * @param design The existing design. + * @param input The reference logical input pin on which the new inline cell + * should be created and connected. + * @param reference The primitive cell type to create. + * @param site The site on which the new cell should be placed. + * @param bel The BEL within the provided site onto which the cell should + * be placed. + * @param logInput The logical cell's input pin that should connect to the + * existing input's net. + * @param logOutput The logical cell's output pin that should connect to the + * existing input. + * @return A hierarchical reference to the newly created cell instance. + */ + public static Cell createAndPlaceInlineCellOnInputPin(Design design, EDIFHierPortInst input, + Unisim reference, Site site, BEL bel, String logInput, String logOutput) { + if (!input.isInput()) return null; + EDIFHierNet net = input.getHierarchicalNet(); + EDIFCell parent = input.getPortInst().getParentCell(); + EDIFNet newNet = parent.createNet("inline_insertion_net_" + UNIQUE_COUNT++); + EDIFHierNet newHierNet = new EDIFHierNet(net.getHierarchicalInst(), newNet); + + // Create new cell instance, add to primitives library if necessary + EDIFCell refCell = design.getNetlist().getHDIPrimitivesLibrary().getCell(reference.name()); + if (refCell == null) { + refCell = Design.getUnisimCell(reference); + design.getNetlist().getHDIPrimitivesLibrary().addCell(refCell); + } + EDIFCellInst newInst = refCell.createCellInst("inline_insertion_" + refCell.getName() + + "_" + UNIQUE_COUNT++, parent); + EDIFHierCellInst newHierInst = net.getHierarchicalInst().getChild(newInst); + Cell cell = design.createCell(newHierInst.getFullHierarchicalInstName(), newInst); + if (!design.placeCell(cell, site, bel)) { + throw new RuntimeException("ERROR: Unable to create new inline cell to be placed on " + + site + "/" + bel); + } + + EDIFPortInst newInput = new EDIFPortInst(refCell.getPort(logInput), null, newInst); + EDIFPortInst newOutput = new EDIFPortInst(refCell.getPort(logOutput), null, newInst); + EDIFHierPortInst newHierInput = newHierInst.getPortInst(newInput.getName()); + EDIFHierPortInst newHierOutput = newHierInst.getPortInst(newOutput.getName()); + + disconnectNet(design, input); + + // Connect new cell input to existing net, output and existing input to new net + Map> connectMap = new HashMap<>(); + connectMap.put(net, new ArrayList<>(Arrays.asList(newHierInput))); + connectMap.put(newHierNet, new ArrayList<>(Arrays.asList(newHierOutput, input))); + connectNet(design, connectMap, null); + return cell; + } + /** * Given list of net paths, create these nets in the design. * This method inserts nets into the EDIF (logical) netlist as well as corresponding nets diff --git a/src/com/xilinx/rapidwright/edif/EDIFCellInst.java b/src/com/xilinx/rapidwright/edif/EDIFCellInst.java index 8a399252a..7fa70ffb9 100644 --- a/src/com/xilinx/rapidwright/edif/EDIFCellInst.java +++ b/src/com/xilinx/rapidwright/edif/EDIFCellInst.java @@ -152,6 +152,22 @@ public EDIFPortInst getPortInst(String name) { return portInsts.get(this, name); } + /** + * Gets the named EDIFPortInst or creates it (if correctly named) and returns + * it. If the port instance is to be created, it will not be connected to an + * EDIFNet. + * + * @param name Name of the port instance to get. + * @return The existing or created port instance. + */ + public EDIFPortInst getOrCreatePortInst(String name) { + EDIFPortInst portInst = portInsts == null ? null : portInsts.get(this, name); + if (portInsts == null || portInst == null) { + portInst = EDIFPortInst.createPortInstFromPortInstName(name, this); + } + return portInst; + } + /** * Gets the port on the underlying cell type. It is the same as * calling getCellType().getPort(name). diff --git a/src/com/xilinx/rapidwright/edif/EDIFPortInst.java b/src/com/xilinx/rapidwright/edif/EDIFPortInst.java index 228ca73c2..be81008bb 100644 --- a/src/com/xilinx/rapidwright/edif/EDIFPortInst.java +++ b/src/com/xilinx/rapidwright/edif/EDIFPortInst.java @@ -149,6 +149,25 @@ protected EDIFPortInst() { } + /** + * Creates a new port instance without connecting it to a net. + * + * @param portInstName Name of the port instance + * @param inst The instance on which to create the new port instance + * @return The new port instance. + */ + public static EDIFPortInst createPortInstFromPortInstName(String portInstName, EDIFCellInst inst) { + EDIFPort port = inst.getCellType().getPortByPortInstName(portInstName); + if (port == null) + return null; + int portIdx = -1; + if (port.isBus()) { + int idx = EDIFTools.getPortIndexFromName(portInstName); + portIdx = port.getPortIndexFromNameIndex(idx); + } + return new EDIFPortInst(port, null, portIdx, inst, false); + } + public String getPortInstNameFromPort() { return port.getPortInstNameFromPort(index); } diff --git a/test/src/com/xilinx/rapidwright/eco/TestECOTools.java b/test/src/com/xilinx/rapidwright/eco/TestECOTools.java index 6cf932b49..8a2598f2b 100644 --- a/test/src/com/xilinx/rapidwright/eco/TestECOTools.java +++ b/test/src/com/xilinx/rapidwright/eco/TestECOTools.java @@ -26,7 +26,12 @@ import com.xilinx.rapidwright.design.Design; import com.xilinx.rapidwright.design.DesignTools; import com.xilinx.rapidwright.design.Net; +import com.xilinx.rapidwright.design.PinType; import com.xilinx.rapidwright.design.SitePinInst; +import com.xilinx.rapidwright.design.Unisim; +import com.xilinx.rapidwright.device.BEL; +import com.xilinx.rapidwright.device.Device; +import com.xilinx.rapidwright.device.Site; import com.xilinx.rapidwright.edif.EDIFCell; import com.xilinx.rapidwright.edif.EDIFHierCellInst; import com.xilinx.rapidwright.edif.EDIFHierNet; @@ -34,11 +39,14 @@ import com.xilinx.rapidwright.edif.EDIFNet; import com.xilinx.rapidwright.edif.EDIFNetlist; import com.xilinx.rapidwright.edif.EDIFPortInst; +import com.xilinx.rapidwright.router.Router; +import com.xilinx.rapidwright.rwroute.TestRWRoute; import com.xilinx.rapidwright.support.RapidWrightDCP; import com.xilinx.rapidwright.util.FileTools; 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.Disabled; import org.junit.jupiter.api.Test; @@ -416,6 +424,52 @@ public void testCreateCell() { } } + @Test + public void testCreateAndPlaceInlineCellOnInputPin() { + Design d = new Design("Test", Device.KCU105); + + Cell and2 = d.createAndPlaceCell("and2", Unisim.AND2, "SLICE_X100Y100/A6LUT"); + Cell button0 = d.createAndPlaceIOB("button0", PinType.IN, "AE10", "LVCMOS18"); + Cell button1 = d.createAndPlaceIOB("button1", PinType.IN, "AF9", "LVCMOS18"); + Cell led0 = d.createAndPlaceIOB("led0", PinType.OUT, "AP8", "LVCMOS18"); + + // Connect Button 0 to the LUT2 input I0 + EDIFHierCellInst hierButton0 = button0.getEDIFHierCellInst().getParent(); + Net net0 = d.createNet(new EDIFHierNet(hierButton0, hierButton0.getCellType().getNet("O"))); + ECOTools.connectNet(d, and2, "I0", net0); + + // Connect Button 1 to the LUT2 input I1 + EDIFHierCellInst hierButton1 = button1.getEDIFHierCellInst().getParent(); + Net net1 = d.createNet(new EDIFHierNet(hierButton1, hierButton1.getCellType().getNet("O"))); + ECOTools.connectNet(d, and2, "I1", net1); + + // Connect the LUT2 (AND2) to the LED IO + Net net2 = d.createNet("and2"); + net2.connect(and2, "O"); + net2.connect(led0, "I"); + + // Route site internal nets + d.routeSites(); + + // Insert a LUT1 in between 'and2.I0' and its source, 'button0.O' + EDIFHierPortInst input = and2.getEDIFHierCellInst().getPortInst("I0"); + Site site = d.getDevice().getSite("SLICE_X100Y101"); + BEL bel = site.getBEL("A6LUT"); + Unisim lut1Type = Unisim.LUT1; + ECOTools.createAndPlaceInlineCellOnInputPin(d, input, lut1Type, site, bel, "I0", "O"); + + // Route nets between sites + new Router(d).routeDesign(); + + Cell lut1 = d.getSiteInstFromSite(site).getCell(bel); + Assertions.assertNotNull(lut1); + Assertions.assertEquals(lut1Type.name(), lut1.getEDIFHierCellInst().getCellType().getName()); + Assertions.assertEquals(net0, lut1.getSitePinFromLogicalPin("I0", null).getNet()); + Assertions.assertNotEquals(net0, lut1.getSitePinFromLogicalPin("O", null).getNet()); + + TestRWRoute.assertVivadoFullyRouted(d); + } + @Test public void testCreateNet() { Design design = RapidWrightDCP.loadDCP("picoblaze_ooc_X10Y235.dcp");