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

[ECOTools] Inline cell insertion #917

Merged
merged 3 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
150 changes: 128 additions & 22 deletions src/com/xilinx/rapidwright/eco/ECOTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -53,6 +54,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
Expand All @@ -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 <TT>disconnect_net -pinlist</TT> 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)
Expand Down Expand Up @@ -427,20 +442,22 @@ 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)) {
throw new RuntimeException("ERROR: Failed to unroute intra-site connection " +
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
Expand Down Expand Up @@ -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());
}
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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<EDIFHierNet, List<EDIFHierPortInst>> 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.
Expand Down Expand Up @@ -1062,6 +1106,68 @@ public static void createCell(Design design, EDIFCell reference, List<String> 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. 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 createInlineCellOnInputPin(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<EDIFHierNet, List<EDIFHierPortInst>> 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
Expand Down
16 changes: 16 additions & 0 deletions src/com/xilinx/rapidwright/edif/EDIFCellInst.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,22 @@ public EDIFPortInst getPortInst(String name) {
return portInsts.get(this, name);
}

/**
* Gets the named EDIFPortInst or creates it (if it 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).
Expand Down
19 changes: 19 additions & 0 deletions src/com/xilinx/rapidwright/edif/EDIFPortInst.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
54 changes: 54 additions & 0 deletions test/src/com/xilinx/rapidwright/eco/TestECOTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,26 @@
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;
import com.xilinx.rapidwright.edif.EDIFHierPortInst;
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.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;

Expand Down Expand Up @@ -416,6 +423,53 @@ public void testCreateCell() {
}
}

@Test
public void testCreateInlineCellOnInputPin() {
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.createInlineCellOnInputPin(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());

Assumptions.assumeTrue(FileTools.isVivadoOnPath());
Assertions.assertTrue(VivadoTools.reportRouteStatus(d).isFullyRouted());
}

@Test
public void testCreateNet() {
Design design = RapidWrightDCP.loadDCP("picoblaze_ooc_X10Y235.dcp");
Expand Down