From 06d94d4d7102c83dde3046dfdd47d69e121bf1c5 Mon Sep 17 00:00:00 2001 From: Chris Lavin Date: Tue, 7 Oct 2025 08:58:47 -0600 Subject: [PATCH 1/5] Adds external library for netlists to compensate for black boxed cells Signed-off-by: Chris Lavin --- .../rapidwright/edif/EDIFDirection.java | 3 +- .../xilinx/rapidwright/edif/EDIFNetlist.java | 62 +++++++++++++++++ .../rapidwright/rwroute/TestRWRoute.java | 69 ++++++++++++++++++- 3 files changed, 130 insertions(+), 4 deletions(-) diff --git a/src/com/xilinx/rapidwright/edif/EDIFDirection.java b/src/com/xilinx/rapidwright/edif/EDIFDirection.java index 80e6c2e29..6c44cea2c 100644 --- a/src/com/xilinx/rapidwright/edif/EDIFDirection.java +++ b/src/com/xilinx/rapidwright/edif/EDIFDirection.java @@ -39,7 +39,8 @@ public enum EDIFDirection { INPUT, OUTPUT, - INOUT; + INOUT, + INTERNAL; private final byte[] arr; diff --git a/src/com/xilinx/rapidwright/edif/EDIFNetlist.java b/src/com/xilinx/rapidwright/edif/EDIFNetlist.java index 6b3b79d1b..a65ba017b 100644 --- a/src/com/xilinx/rapidwright/edif/EDIFNetlist.java +++ b/src/com/xilinx/rapidwright/edif/EDIFNetlist.java @@ -108,6 +108,10 @@ public class EDIFNetlist extends EDIFName { private String origDirectory; private List encryptedCells; + + private EDIFLibrary external; + + private Map externalMappings; private boolean trackCellChanges = false; @@ -1894,6 +1898,28 @@ public void expandMacroUnisims(Series series) { } } } + + public void populateExternalCells() { + externalMappings = new HashMap<>(); + for (EDIFLibrary lib : getLibraries()) { + if (lib.isHDIPrimitivesLibrary()) { + continue; + } + + for (EDIFCell cell : lib.getCells()) { + for (EDIFCellInst inst : cell.getCellInsts()) { + if (!inst.getCellType().isPrimitive() && inst.getCellType().isLeafCellOrBlackBox()) { + // Likely an encrypted cell, see if we have a substitute netlist in external lib + EDIFCell sub = external.getCell(inst.getCellName()); + externalMappings.put(sub, inst.getCellType()); + if (sub != null) { + inst.setCellType(sub); + } + } + } + } + } + } private Boolean checkIOStandardForExpansion(EDIFCellInst inst, Pair> exception) { Boolean expand = null; @@ -1991,10 +2017,35 @@ public void collapseMacroUnisims(Series series) { for (String name : primsToRemoveOnCollapse) { prims.removeCell(name); } + + if (external != null) { + blackBoxExternalCells(); + } + // Invalidate parent net map due to macro collapses resetParentNetMap(); } + public void blackBoxExternalCells() { + for (EDIFLibrary lib : getLibraries()) { + if (lib.isHDIPrimitivesLibrary()) { + continue; + } + + for (EDIFCell cell : lib.getCells()) { + if (cell.isLeafCellOrBlackBox()) { + for (EDIFCellInst inst : cell.getCellInsts()) { + if (inst.getCellType().getLibrary() == external) { + EDIFCell origBB = externalMappings.get(inst.getCellType()); + assert (origBB != null); + inst.setCellType(origBB); + } + } + } + } + } + } + /** * Keeps track of the original source directory from where this EDIF file was loaded. * @return Original directory path from where the EDIF file was loaded @@ -2195,6 +2246,17 @@ public void resetCellInstIOStandardFallbackMap() { cellInstIOStandardFallback = null; } + public void setExternalLibrary(EDIFLibrary external) { + this.external = external; + if (external != null) { + populateExternalCells(); + } + } + + public EDIFLibrary getExternalLibrary() { + return external; + } + public static void main(String[] args) throws FileNotFoundException { CodePerfTracker t = new CodePerfTracker("EDIF Import/Export", true); t.start("Read EDIF"); diff --git a/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java b/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java index 197ae2d6d..1a2462fa5 100644 --- a/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java +++ b/test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java @@ -32,9 +32,6 @@ import java.util.Map; import java.util.Random; -import com.xilinx.rapidwright.design.tools.LUTTools; -import com.xilinx.rapidwright.eco.ECOTools; -import com.xilinx.rapidwright.util.VivadoToolsHelper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -51,12 +48,17 @@ 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.Device; import com.xilinx.rapidwright.device.Node; import com.xilinx.rapidwright.device.PIP; import com.xilinx.rapidwright.device.Part; import com.xilinx.rapidwright.device.PartNameTools; import com.xilinx.rapidwright.device.Series; +import com.xilinx.rapidwright.eco.ECOTools; +import com.xilinx.rapidwright.edif.EDIFCell; +import com.xilinx.rapidwright.edif.EDIFHierCellInst; +import com.xilinx.rapidwright.edif.EDIFNetlist; import com.xilinx.rapidwright.edif.EDIFTools; import com.xilinx.rapidwright.interchange.Interchange; import com.xilinx.rapidwright.support.LargeTest; @@ -64,6 +66,7 @@ import com.xilinx.rapidwright.util.FileTools; import com.xilinx.rapidwright.util.ReportRouteStatusResult; import com.xilinx.rapidwright.util.VivadoTools; +import com.xilinx.rapidwright.util.VivadoToolsHelper; public class TestRWRoute { private static void assertAllPinsRouted(Net net) { @@ -613,4 +616,64 @@ public void testDiscussion1245_20250807(boolean forceLagPin) { VivadoToolsHelper.assertFullyRouted(test_route); } + @Test + public void testRWRouteSubstituteBlackBoxFlow(@TempDir Path dir) { + // Load an example design, make sure RWRoute can route it + Path dcp = RapidWrightDCP.getPath("optical-flow.dcp"); + Design design = Design.readCheckpoint(dcp); + RWRoute.routeDesignFullNonTimingDriven(design); + // VivadoToolsHelper.assertFullyRouted(design); + + // Pick a cell to blackbox + String instToBlackBox = "bd_0_i/hls_inst/inst/Loop_FRAMES_CP_OUTER_U0"; + EDIFHierCellInst inst = design.getNetlist().getHierCellInstFromName(instToBlackBox); + + // Create example external library, store the guts of this netlist in external lib + String cellType = "bd_0_hls_inst_0_Loop_FRAMES_CP_OUTER"; + EDIFNetlist external = EDIFTools.createNewNetlist(inst.getInst()); + Path externalEDIF = dir.resolve(cellType + ".edn"); + external.exportEDIF(externalEDIF); + + inst.getCellType().makePrimitive(); // makeBlackBox + + // Write out top EDIF that now has a black boxed cell + Path opticalFlowTopEDIF = dir.resolve("optical-flow.edf"); + design.getNetlist().collapseMacroUnisims(design.getSeries()); + design.getNetlist().exportEDIF(opticalFlowTopEDIF); + + // Re-load DCP with black boxed netlist + Design designWithBlackBox = Design.readCheckpoint(dcp, opticalFlowTopEDIF); + + // Load external lib, set it as the netlist's external library + EDIFNetlist externalLib = EDIFTools.readEdifFile(externalEDIF); + EDIFCell cell = designWithBlackBox.getNetlist().getCellInstFromHierName(instToBlackBox).getCellType(); + Assertions.assertTrue(cell.isLeafCellOrBlackBox() && !cell.isPrimitive()); + designWithBlackBox.getNetlist().setExternalLibrary(externalLib.getLibrary("work")); + cell = designWithBlackBox.getNetlist().getCellInstFromHierName(instToBlackBox).getCellType(); + Assertions.assertFalse(cell.isLeafCellOrBlackBox()); + + // Run RWRoute + RWRoute.routeDesignFullNonTimingDriven(designWithBlackBox); + + // Sanity check on routing + for (Net net : design.getNets()) { + Net other = designWithBlackBox.getNet(net.getName()); + Assertions.assertNotNull(other); + Assertions.assertEquals(net.getPins().size(), other.getPins().size()); + Assertions.assertEquals(net.hasPIPs(), other.hasPIPs()); + } + + // Return the netlist back to its original state for Vivado to read it correctly + designWithBlackBox.getNetlist().blackBoxExternalCells(); + designWithBlackBox.getNetlist().setExternalLibrary(null); + EDIFCell guts = externalLib.getCell(cellType); + EDIFHierCellInst instToRestore = designWithBlackBox.getNetlist() + .getHierCellInstFromName(instToBlackBox); + designWithBlackBox.getNetlist().getWorkLibrary().removeCell(instToRestore.getCellType()); + designWithBlackBox.getNetlist().copyCellAndSubCells(guts); + guts = designWithBlackBox.getNetlist().getWorkLibrary().getCell(cellType); + instToRestore.getInst().setCellType(guts); + + VivadoToolsHelper.assertFullyRouted(designWithBlackBox); + } } From b3f4c38d3919fe0b36f2f3f1469208e27c72ea2b Mon Sep 17 00:00:00 2001 From: Chris Lavin Date: Thu, 9 Oct 2025 08:12:55 -0600 Subject: [PATCH 2/5] Removing overconstrained check Signed-off-by: Chris Lavin --- src/com/xilinx/rapidwright/edif/EDIFNetlist.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/com/xilinx/rapidwright/edif/EDIFNetlist.java b/src/com/xilinx/rapidwright/edif/EDIFNetlist.java index a65ba017b..a75872433 100644 --- a/src/com/xilinx/rapidwright/edif/EDIFNetlist.java +++ b/src/com/xilinx/rapidwright/edif/EDIFNetlist.java @@ -2033,13 +2033,11 @@ public void blackBoxExternalCells() { } for (EDIFCell cell : lib.getCells()) { - if (cell.isLeafCellOrBlackBox()) { - for (EDIFCellInst inst : cell.getCellInsts()) { - if (inst.getCellType().getLibrary() == external) { - EDIFCell origBB = externalMappings.get(inst.getCellType()); - assert (origBB != null); - inst.setCellType(origBB); - } + for (EDIFCellInst inst : cell.getCellInsts()) { + if (inst.getCellType().getLibrary() == external) { + EDIFCell origBB = externalMappings.get(inst.getCellType()); + assert (origBB != null); + inst.setCellType(origBB); } } } From 785fdf9f14cd28bdc45b811f2af06d42dd6126b1 Mon Sep 17 00:00:00 2001 From: Chris Lavin Date: Thu, 9 Oct 2025 10:17:43 -0600 Subject: [PATCH 3/5] Add additional validity check in new EDIFPortInst() Signed-off-by: Chris Lavin --- src/com/xilinx/rapidwright/edif/EDIFPortInst.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/xilinx/rapidwright/edif/EDIFPortInst.java b/src/com/xilinx/rapidwright/edif/EDIFPortInst.java index 5adca8808..6b74a445e 100644 --- a/src/com/xilinx/rapidwright/edif/EDIFPortInst.java +++ b/src/com/xilinx/rapidwright/edif/EDIFPortInst.java @@ -126,6 +126,10 @@ public EDIFPortInst(EDIFPort port, EDIFNet parentNet, int index, EDIFCellInst ce throw new RuntimeException("ERROR: Use a different constructor, " + "need index for bussed port " + port.getName()); } + if (index != -1 && !port.isBus()) { + throw new RuntimeException("ERROR: Use a different constructor, " + "port " + + port.getName() + " is not a bus, cannot index into a single bit signal."); + } if (cellInst != null) { if (!port.equals(cellInst.getPort(port.getBusName(true)))) { // check for name collision From 315a6d1e4bf90ff4fc67cf901bff316051586f9e Mon Sep 17 00:00:00 2001 From: Chris Lavin Date: Thu, 9 Oct 2025 11:16:31 -0600 Subject: [PATCH 4/5] Fix failing test case after EDIFPortInst check Signed-off-by: Chris Lavin --- .../examples/PipelineGeneratorWithRouting.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/com/xilinx/rapidwright/examples/PipelineGeneratorWithRouting.java b/src/com/xilinx/rapidwright/examples/PipelineGeneratorWithRouting.java index 2d9e9812e..9438180d0 100644 --- a/src/com/xilinx/rapidwright/examples/PipelineGeneratorWithRouting.java +++ b/src/com/xilinx/rapidwright/examples/PipelineGeneratorWithRouting.java @@ -337,8 +337,22 @@ public static PBlock createPipeline(Design d, Site startingPoint, int width, int if (i == 0) prevSite = currSlice; - if (j == 0) inputNet.createPortInst(inputPort, (width - 1) - i); - if (j == depth - 1) outputNet.createPortInst(outputPort, (width - 1) - i); + if (j == 0) { + if (inputPort.isBus()) { + inputNet.createPortInst(inputPort, (width - 1) - i); + } else { + inputNet.createPortInst(inputPort); + } + + } + if (j == depth - 1) { + if (outputPort.isBus()) { + outputNet.createPortInst(outputPort, (width - 1) - i); + } else { + outputNet.createPortInst(outputPort); + } + + } clkNet.getLogicalNet().createPortInst("C", ffCell); rstNet.getLogicalNet().createPortInst("R", ffCell); From 6329b534cefe263adcdc68a5acbb57f9ad4aa92f Mon Sep 17 00:00:00 2001 From: Chris Lavin Date: Mon, 13 Oct 2025 15:37:49 -0600 Subject: [PATCH 5/5] Adds support for negative indexed busses on ports Signed-off-by: Chris Lavin --- src/com/xilinx/rapidwright/edif/EDIFTools.java | 12 ++++++------ .../com/xilinx/rapidwright/edif/TestEDIFPort.java | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/com/xilinx/rapidwright/edif/EDIFTools.java b/src/com/xilinx/rapidwright/edif/EDIFTools.java index f14abf623..0197f55f4 100644 --- a/src/com/xilinx/rapidwright/edif/EDIFTools.java +++ b/src/com/xilinx/rapidwright/edif/EDIFTools.java @@ -424,10 +424,10 @@ public static int lengthOfNameWithoutBus(char[] name) { } /** - * Determines if the char[] ends with the pattern [#:#] where # are positive bus - * values (e.g., [7:0]) and then returns the length of the string without the - * bus suffix (if it exists). If the name does not end with the bus pattern, it - * returns the original length of the char[]. + * Determines if the char[] ends with the pattern [#:#] where # are integer bus + * values (e.g., [7:0] or [0:-1]) and then returns the length of the string + * without the bus suffix (if it exists). If the name does not end with the bus + * pattern, it returns the original length of the char[]. * * @param name * @param keepOpenBracket In the case of a bussed name, this will return the @@ -439,11 +439,11 @@ public static int lengthOfNameWithoutBus(char[] name, boolean keepOpenBracket) { int len = name.length; int i = len-1; if (name[i--] != ']') return len; - while (Character.isDigit(name[i])) { + while (Character.isDigit(name[i]) || name[i] == '-') { i--; } if (name[i--] != ':') return len; - while (Character.isDigit(name[i])) { + while (Character.isDigit(name[i]) || name[i] == '-') { i--; } if (name[i] != '[') return len; diff --git a/test/src/com/xilinx/rapidwright/edif/TestEDIFPort.java b/test/src/com/xilinx/rapidwright/edif/TestEDIFPort.java index 525359017..fddd7ab77 100644 --- a/test/src/com/xilinx/rapidwright/edif/TestEDIFPort.java +++ b/test/src/com/xilinx/rapidwright/edif/TestEDIFPort.java @@ -82,8 +82,8 @@ public void testCreatePort() { EDIFCell cell = new EDIFCell(netlist.getWorkLibrary(), "cell_1"); int outer = 0; // Creates ports: {bus_output[0][3:0], bus_output[2][5:2], - // bus_output[1][0:3], bus_output[3][2:5]} - for (String range : new String[] { "3:0", "0:3", "5:2", "2:5" }) { + // bus_output[1][0:3], bus_output[3][2:5], , bus_output[4][0:-1], bus_output[5][-3:-1]} + for (String range : new String[] { "3:0", "0:3", "5:2", "2:5", "0:-1", "-3:-1" }) { int left = Integer.parseInt(range.substring(0, range.indexOf(':'))); int right = Integer.parseInt(range.substring(range.indexOf(':') + 1)); int width = Math.abs(left - right) + 1;