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");