staticPinsMap;
+ static {
+ staticPinsMap = new HashMap<>();
+ staticPinsMap.put(Series.Series7, new String[]{"CE", "SR"});
+ staticPinsMap.put(Series.UltraScale, new String[]{"CKEN1", "CKEN2", "CKEN3", "CKEN4", "SRST1", "SRST2"});
+ staticPinsMap.put(Series.UltraScalePlus, staticPinsMap.get(Series.UltraScale));
+ staticPinsMap.put(Series.Versal, new String[]{"CKEN1", "CKEN2", "CKEN3", "CKEN4", "RST"});
+ }
+
+ private static String[] getStaticPins(Design design) {
+ String[] staticPins = staticPinsMap.get(design.getSeries());
+ if (staticPins == null) {
+ throw new RuntimeException("Unsupported device series for removing inline flops");
+ }
+ return staticPins;
+ }
+
+ /**
+ * Parses the PBlock side map into a map from EDIFPorts to PBlockSide enums. The input file should be made up of
+ * some number of lines where each line contains a port name regex and a PBlockSide separated by a space. An
+ * example file:
+ *
+ * example_inputs.* TOP
+ * reset LEFT
+ * example_outputs.* BOTTOM
+ *
+ *
+ * @param netlist The netlist that the side map will be created for.
+ * @param filename The name of the input side map file.
+ * @return A map from EDIFPort to the PBlockSide the inline flop should be placed on.
+ */
+ public static Map parseSideMap(EDIFNetlist netlist, String filename) {
+ Map externalRoutabilitySideMap = new HashMap<>();
+ List lines = FileTools.getLinesFromTextFile(filename);
+
+ for (String line : lines) {
+ String[] splitLine = line.split("\\s+");
+ String portRegex = splitLine[0];
+ String pblockSide = splitLine[1].toUpperCase();
+ for (EDIFPort port : netlist.getTopCell().getPorts()) {
+ if (port.getBusName().matches(portRegex) ||
+ port.getName().matches("\\" + EDIFTools.VIVADO_PRESERVE_PORT_INTERFACE + portRegex)) {
+ if (externalRoutabilitySideMap.containsKey(port)) {
+ throw new RuntimeException("Port " + port + " matches multiple expressions in side map");
+ }
+ PBlockSide side = PBlockSide.valueOf(pblockSide);
+ externalRoutabilitySideMap.put(port, side);
+ }
+ }
+ }
+ return externalRoutabilitySideMap;
+ }
+
+
public static void main(String[] args) {
if (args.length < 3 || args.length > 4) {
- System.out.println("USAGE (to add flops) : "+CLK_OPT+"= "+PBLOCK_OPT+"=");
+ System.out.println("USAGE (to add flops) : " + CLK_OPT + "= " + PBLOCK_OPT + "=");
System.out.println("USAGE (to remove flops): " + REMOVE_FLOPS_OPT);
return;
}
-
+
Design d = Design.readCheckpoint(args[0]);
if (args[2].startsWith(CLK_OPT) || args[2].startsWith(PBLOCK_OPT)) {
@@ -248,18 +570,17 @@ public static void main(String[] args) {
String clkName = StringTools.getOptionValue(CLK_OPT, args);
String pblockRange = StringTools.getOptionValue(PBLOCK_OPT, args);
if (clkName == null || pblockRange == null) {
- throw new RuntimeException("ERROR: Missing value(s) for option(s): "
+ throw new RuntimeException("ERROR: Missing value(s) for option(s): "
+ CLK_OPT + "=" + clkName + ", " + PBLOCK_OPT + "=" + pblockRange);
}
PBlock pblock = new PBlock(d.getDevice(), pblockRange);
- createAndPlaceFlopsInlineOnTopPorts(d, clkName, pblock);
+ createAndPlaceFlopsInlineOnTopPortsArbitrarily(d, clkName, pblock);
} else if (args[2].equals(REMOVE_FLOPS_OPT)) {
removeInlineFlops(d);
} else {
- System.err.println("ERROR: Unrecognized option '" + args[2] +"'");
+ System.err.println("ERROR: Unrecognized option '" + args[2] + "'");
}
-
-
+
d.writeCheckpoint(args[1]);
}
}
diff --git a/src/com/xilinx/rapidwright/design/xdc/UnsupportedConstraintElement.java b/src/com/xilinx/rapidwright/design/xdc/UnsupportedConstraintElement.java
index 5e7aa1b2b..1e8068659 100644
--- a/src/com/xilinx/rapidwright/design/xdc/UnsupportedConstraintElement.java
+++ b/src/com/xilinx/rapidwright/design/xdc/UnsupportedConstraintElement.java
@@ -279,7 +279,6 @@ private static void absorbIntoCells(EdifCellLookup lookup, List void absorbIntoCells(EdifCellLookup lookup, List void absorbIntoCells(EdifCellLookup lookup, List 1) {
- System.out.println("chose arbitrary wildcard match for "+currLevel+": "+child);
- }
} else {
break;
}
@@ -317,15 +312,11 @@ private static void absorbIntoCells(EdifCellLookup lookup, List points, Set targetSiteTypes)
for (SitePinInst i : net.getPins()) {
points.add(new Point(i.getTile().getColumn(), i.getTile().getRow()));
}
- return ECOPlacementHelper.getCentroidOfPoints(net.getSource().getTile().getDevice(), points, targetSiteTypes);
+ return ECOPlacementHelper.getCentroidOfPoints(net.getDesign().getDevice(), points, targetSiteTypes);
}
/**
diff --git a/src/com/xilinx/rapidwright/edif/EDIFCell.java b/src/com/xilinx/rapidwright/edif/EDIFCell.java
index 23ee50845..e361e8ae6 100644
--- a/src/com/xilinx/rapidwright/edif/EDIFCell.java
+++ b/src/com/xilinx/rapidwright/edif/EDIFCell.java
@@ -313,7 +313,7 @@ public EDIFPort getPort(String name) {
/**
* Given a port instance name (not including the name of the cell instance),
* gets the associated port.
- *
+ *
* @param portInstName
* @return
*/
@@ -806,5 +806,33 @@ public int getNonHierInstantiationCount() {
public boolean isUniquified() {
return getNonHierInstantiationCount() <= 1;
}
+
+ /**
+ * Checks if this cell and the provided cell have the same set of ports.
+ * Port names that are the same except for starting with EDIFTools.VIVADO_PRESERVE_PORT_INTERFACE ("[]") are
+ * considered equivalent for the purpose of cells having a matching set of ports.
+ *
+ * @param other The other cell to match against.
+ * @return True if the set of ports on both this cell and the other cell match
+ * exactly. False otherwise.
+ */
+ public boolean matchesInterface(EDIFCell other) {
+ if (getPorts().size() != other.getPorts().size()) {
+ return false;
+ }
+ Map otherPorts = other.getPortMap();
+ for (EDIFPort port : getPorts()) {
+ EDIFPort otherPort = otherPorts.get(port.getBusName(true));
+ if (otherPort == null) {
+ otherPort = otherPorts.get(EDIFTools.VIVADO_PRESERVE_PORT_INTERFACE
+ + port.getBusName(true));
+ }
+ if (otherPort == null || port.getWidth() != otherPort.getWidth()
+ || port.getDirection() != otherPort.getDirection()) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/src/com/xilinx/rapidwright/edif/EDIFTools.java b/src/com/xilinx/rapidwright/edif/EDIFTools.java
index 564419f57..69e973cca 100644
--- a/src/com/xilinx/rapidwright/edif/EDIFTools.java
+++ b/src/com/xilinx/rapidwright/edif/EDIFTools.java
@@ -1783,18 +1783,25 @@ public static void ensurePreservedInterfaceVivado(EDIFNetlist netlist) {
}
}
+ /**
+ * Removes vivado bus prevention annotations from top-level ports. Vivado sometimes adds these annotations
+ * to prevent multiple single bit ports with similar names from getting merged into a single bus. The method
+ * used here will only work on the top-level cell as we do not traverse the netlist to ensure the new names
+ * are consistent.
+ *
+ * @param netlist The netlist to remove bus prevention annotations from.
+ *
+ */
public static void removeVivadoBusPreventionAnnotations(EDIFNetlist netlist) {
EDIFCell top = netlist.getTopCell();
- for (EDIFCell cell : netlist.getLibrary(top.getLibrary().getName()).getCells()) {
- List portsToRename = new ArrayList<>();
- for (EDIFPort p : cell.getPorts()) {
- if (p.getName().startsWith(VIVADO_PRESERVE_PORT_INTERFACE)) {
- portsToRename.add(p.getName());
- }
- }
- for (String p : portsToRename) {
- cell.renamePort(p, p.substring(VIVADO_PRESERVE_PORT_INTERFACE.length()));
+ List portsToRename = new ArrayList<>();
+ for (EDIFPort p : top.getPorts()) {
+ if (p.getName().startsWith(VIVADO_PRESERVE_PORT_INTERFACE)) {
+ portsToRename.add(p.getName());
}
}
+ for (String p : portsToRename) {
+ top.renamePort(p, p.substring(VIVADO_PRESERVE_PORT_INTERFACE.length()));
+ }
}
}
diff --git a/src/com/xilinx/rapidwright/router/Router.java b/src/com/xilinx/rapidwright/router/Router.java
index e2ac71e17..cb79fc92e 100644
--- a/src/com/xilinx/rapidwright/router/Router.java
+++ b/src/com/xilinx/rapidwright/router/Router.java
@@ -2006,7 +2006,7 @@ public Design routeDesign() {
if (currNet.getSource() == null && !currNet.isStaticNet()) {
EDIFNet logNet = currNet.getLogicalNet();
- if (logNet != null && logNet.getParentCell().getName().equals("IOBUF")) {
+ if (logNet != null && logNet.getSourcePortInsts(true).size() > 0) {
continue;
}
if (!supressWarningsErrors) MessageGenerator.briefError("WARNING: " + currNet.getName() + " does not have a source pin associated with it.");
diff --git a/src/com/xilinx/rapidwright/util/Job.java b/src/com/xilinx/rapidwright/util/Job.java
index 4cba6a023..46739d25b 100644
--- a/src/com/xilinx/rapidwright/util/Job.java
+++ b/src/com/xilinx/rapidwright/util/Job.java
@@ -57,6 +57,8 @@ public abstract class Job {
public static final String DEFAULT_COMMAND_LOG_FILE = DEFAULT_COMMAND_NAME + DEFAULT_LOG_EXTENSION;
+ public static final int DEFAULT_LOG_LINES = 80;
+
public abstract long launchJob();
public abstract JobState getJobState();
@@ -151,7 +153,7 @@ public Optional> getLastLogLines() {
String logFileName = getLogFilename();
if (new File(logFileName).exists()) {
ArrayList lines = FileTools.getLinesFromTextFile(logFileName);
- int start = lines.size() >= 8 ? lines.size()-8 : 0;
+ int start = lines.size() >= DEFAULT_LOG_LINES ? lines.size()-DEFAULT_LOG_LINES : 0;
return Optional.of(IntStream.range(start, lines.size()).mapToObj(lines::get).collect(Collectors.toList()));
}
return Optional.empty();
diff --git a/src/com/xilinx/rapidwright/util/Params.java b/src/com/xilinx/rapidwright/util/Params.java
index 76b4cd954..15a654744 100644
--- a/src/com/xilinx/rapidwright/util/Params.java
+++ b/src/com/xilinx/rapidwright/util/Params.java
@@ -40,6 +40,21 @@ public class Params {
public static String RW_COPY_EDNS_ON_DCP_WRITE_NAME = "RW_COPY_EDNS_ON_DCP_WRITE";
+ /**
+ * Directory where RapidWright should temporarily extract readable EDIF files
+ * embedded inside DCPs before parsing. This can be set either as an environment
+ * variable or JVM system property. If unset, RapidWright will try the DCP's
+ * directory first and then the JVM temp directory.
+ */
+ public static String RW_DCP_EDIF_TEMP_DIR_NAME = "RW_DCP_EDIF_TEMP_DIR";
+
+ /**
+ * Minimum uncompressed EDIF size in bytes where RapidWright should extract a
+ * readable EDIF embedded inside a DCP before parsing. If unset or negative,
+ * RapidWright uses the parallel EDIF parser's thread-count heuristic.
+ */
+ public static String RW_DCP_EDIF_EXTRACT_THRESHOLD_BYTES_NAME = "RW_DCP_EDIF_EXTRACT_THRESHOLD_BYTES";
+
/**
* Flag to have RapidWright decompress gzipped EDIF files to disk prior to
* parsing. This is a tradeoff where pre-decompression improves runtime over the
@@ -78,6 +93,20 @@ public class Params {
*/
public static boolean RW_COPY_EDNS_ON_DCP_WRITE = isParamSet(RW_COPY_EDNS_ON_DCP_WRITE_NAME);
+ /**
+ * Directory where RapidWright should temporarily extract readable EDIF files
+ * embedded inside DCPs before parsing, or null if unset.
+ */
+ public static String RW_DCP_EDIF_TEMP_DIR = getParamValue(RW_DCP_EDIF_TEMP_DIR_NAME);
+
+ /**
+ * Minimum uncompressed EDIF size in bytes where RapidWright should extract a
+ * readable EDIF embedded inside a DCP before parsing. A negative value means
+ * the threshold was not set explicitly.
+ */
+ public static long RW_DCP_EDIF_EXTRACT_THRESHOLD_BYTES =
+ getParamOrDefaultLongSetting(RW_DCP_EDIF_EXTRACT_THRESHOLD_BYTES_NAME, -1L);
+
/**
* Checks if the named RapidWright parameter is set via an environment variable
* or by a JVM parameter of the same name.
@@ -126,6 +155,27 @@ public static Integer getParamIntValue(String key) {
return null;
}
+ /**
+ * Gets the long value of the provided parameter name.
+ *
+ * @param key Name of the system parameter to get.
+ * @return The set long value of the parameter, or null if none was set. If
+ * the property is set to a value that is not a parsable long, a
+ * warning message is produced and returns null.
+ */
+ public static Long getParamLongValue(String key) {
+ String envValue = getParamValue(key);
+ if (envValue != null) {
+ try {
+ return Long.parseLong(envValue);
+ } catch (NumberFormatException e) {
+ System.err.println("WARNING: Couldn't interpret the value '" + envValue
+ + "' from the parameter '" + key + "' as a long.");
+ }
+ }
+ return null;
+ }
+
/**
* Gets the string value of the provided parameter name.
*
@@ -154,4 +204,18 @@ public static int getParamOrDefaultIntSetting(String key, int defaultValue) {
return setValue == null ? defaultValue : setValue;
}
+ /**
+ * Checks the parameter value of the provided key. If it is set, it returns the
+ * set value. Otherwise it will return the default value.
+ *
+ * @param key Name of the system parameter to check.
+ * @param defaultValue The default value to return if the parameter is not set.
+ * @return The system parameter value if is set, otherwise it returns
+ * defaultValue.
+ */
+ public static long getParamOrDefaultLongSetting(String key, long defaultValue) {
+ Long setValue = getParamLongValue(key);
+ return setValue == null ? defaultValue : setValue;
+ }
+
}
diff --git a/src/com/xilinx/rapidwright/util/PerformanceExplorer.java b/src/com/xilinx/rapidwright/util/PerformanceExplorer.java
index 35cfccfb4..adc668930 100644
--- a/src/com/xilinx/rapidwright/util/PerformanceExplorer.java
+++ b/src/com/xilinx/rapidwright/util/PerformanceExplorer.java
@@ -28,19 +28,22 @@
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
import com.xilinx.rapidwright.design.ConstraintGroup;
import com.xilinx.rapidwright.design.Design;
+import com.xilinx.rapidwright.design.NetTools;
import com.xilinx.rapidwright.design.blocks.PBlock;
+import com.xilinx.rapidwright.design.tools.InlineFlopTools;
+import com.xilinx.rapidwright.design.blocks.PBlockSide;
+import com.xilinx.rapidwright.edif.EDIFPort;
+import com.xilinx.rapidwright.edif.EDIFTools;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
@@ -52,8 +55,11 @@
public class PerformanceExplorer {
private static final String INITIAL_DCP_NAME = "initial.dcp";
+ private static final String INITIAL_ENCRYPTED_TCL_NAME = "initial_load.tcl";
private static final String PLACED_TIMING_RESULT = "place_timing.twr";
private static final String ROUTED_TIMING_RESULT = "route_timing.twr";
+ private static final String ROUTED_TIMING_SUMMARY = "route_timing_summary.rpt";
+ private static final String ROUTE_STATUS_RESULT = "route_status.rpt";
private static final String RUN_TCL_NAME = "run.tcl";
private static final double DEFAULT_MIN_CLK_UNCERT = -0.100;
private static final double DEFAULT_MAX_CLK_UNCERT = 0.250;
@@ -77,10 +83,14 @@ public class PerformanceExplorer {
/** Maps a pblock to optional named cell(s) to be constrained by the pblock */
private Map pblocks;
+ private PBlock[] pblockLookup;
+
private boolean containRouting;
private boolean addEDIFAndMetadata;
+ private boolean getBestPerPBlock;
+
private ArrayList placerDirectives;
private ArrayList routerDirectives;
@@ -95,6 +105,14 @@ public class PerformanceExplorer {
private String vivadoPath = DEFAULT_VIVADO;
+ private boolean reusePreviousResults;
+
+ private boolean ensureExternalRoutability;
+
+ private String externalRoutabilitySideFile;
+
+ Map externalRoutabilitySideMap = null;
+
public PerformanceExplorer(Design d, String testDir, String clkName, double targetPeriod) {
init(d, testDir, clkName, targetPeriod, null);
}
@@ -262,23 +280,71 @@ public void setAddEDIFAndMetadata(boolean addEDIFAndMetadata) {
this.addEDIFAndMetadata = addEDIFAndMetadata;
}
+ public boolean getBestPerPBlock() {
+ return getBestPerPBlock;
+ }
+
+ public void setGetBestPerPBlock(boolean getBestPerPBlock) {
+ this.getBestPerPBlock = getBestPerPBlock;
+ }
+
+ public boolean ensureExternalRoutability() {
+ return ensureExternalRoutability;
+ }
+
+ public void setEnsureExternalRoutability(boolean ensureExternalRoutability) {
+ this.ensureExternalRoutability = ensureExternalRoutability;
+ }
+
+ public String getExternalRoutabilitySideFile() {
+ return externalRoutabilitySideFile;
+ }
+
+ public void setExternalRoutabilitySideFile(String externalRoutabilitySideFile) {
+ this.externalRoutabilitySideFile = externalRoutabilitySideFile;
+ }
+
+ public PBlock getPBlock(int i) {
+ return pblockLookup[i];
+ }
+
+ public void setReusePreviousResults(boolean value) {
+ this.reusePreviousResults = value;
+ }
+
+ public boolean reusePreviousResults() {
+ return reusePreviousResults;
+ }
+
public ArrayList createTclScript(String initialDcp, String instDirectory,
- PlacerDirective p, RouterDirective r, String clockUncertainty, Entry pblockEntry) {
+ PlacerDirective p, RouterDirective r, String clockUncertainty,
+ Entry pblockEntry, String encryptedTcl) {
PBlock pblock = pblockEntry.getKey();
String pblockCells = pblockEntry.getValue();
ArrayList lines = new ArrayList<>();
- lines.add("open_checkpoint " + initialDcp);
+ if (encryptedTcl == null) {
+ lines.add("open_checkpoint " + initialDcp);
+ } else {
+ lines.add("source " + encryptedTcl);
+ }
lines.add("set_clock_uncertainty -setup "+clockUncertainty+" [get_clocks "+clkName+"]");
if (pblock != null) {
String pblockName = pblock.getName() == null ? "pe_pblock_1" : pblock.getName();
lines.add("create_pblock " + pblockName);
lines.add("resize_pblock "+pblockName+" -add {"+pblock.toString()+"}");
lines.add("add_cells_to_pblock "+pblockName+" " + (pblockCells == null ? "-top" : "[get_cells {"+ pblockCells +"}]" ));
- if (containRouting) {
+ lines.add("set_property IS_SOFT 0 [get_pblocks " + pblockName + "]");
+ if (isContainRouting()) {
lines.add("set_property CONTAIN_ROUTING 1 [get_pblocks "+ pblockName+"]");
}
}
- lines.add("place_design -unplace");
+ if (ensureExternalRoutability()) {
+ lines.add("lock_design -level placement");
+ }
+ lines.add("opt_design");
+ if (!ensureExternalRoutability()) {
+ lines.add("place_design -unplace");
+ }
lines.add("place_design -directive " + p.name());
lines.add("set_clock_uncertainty -setup 0.0 [get_clocks "+clkName+"]");
lines.add("report_timing -file "+instDirectory + File.separator+PLACED_TIMING_RESULT);
@@ -296,6 +362,25 @@ public ArrayList createTclScript(String initialDcp, String instDirectory
return lines;
}
+ public static void updateClockPeriodConstraint(Design design, String clkName, double period) {
+ // Update clock period constraint
+ boolean foundExistingConstraint = false;
+ for (ConstraintGroup g : ConstraintGroup.values()) {
+ List xdcList = design.getXDCConstraints(g);
+ for (int i = 0; i < xdcList.size(); i++) {
+ String xdc = xdcList.get(i);
+ if (xdc.contains("create_clock")
+ && (xdc.contains("-name " + clkName) || xdc.contains("[get_ports " + clkName + "]"))) {
+ // TODO This may overwrite other existing options
+ xdcList.set(i, "create_clock -period " + period + " [get_ports " + clkName + "]");
+ foundExistingConstraint = true;
+ }
+ }
+ }
+ if (!foundExistingConstraint) {
+ design.addXDCConstraint("create_clock -period " + period + " [get_ports " + clkName + "]");
+ }
+ }
public void explorePerformance() {
if (vivadoPath.equals(DEFAULT_VIVADO) && !FileTools.isVivadoOnPath()) {
@@ -306,16 +391,8 @@ public void explorePerformance() {
FileTools.makeDirs(runDirectory);
runDirectory = new File(runDirectory).getAbsolutePath();
String dcpName = runDirectory + File.separator + INITIAL_DCP_NAME;
- // Update clock period constraint
- for (ConstraintGroup g : ConstraintGroup.values()) {
- List xdcList = design.getXDCConstraints(g);
- for (int i=0; i < xdcList.size(); i++) {
- String xdc = xdcList.get(i);
- if (xdc.contains("create_clock") && xdc.contains("-name " + clkName)) {
- // TODO - For now, user will need to update DCP beforehand
- }
- }
- }
+
+ updateClockPeriodConstraint(design, getClkName(), getTargetPeriod());
design.writeCheckpoint(dcpName);
JobQueue jobs = new JobQueue();
@@ -327,38 +404,66 @@ public void explorePerformance() {
int pb = 0;
int jobsStarted = 0;
- int maxConcurrentJobs = JobQueue.isLSFAvailable() ? JobQueue.MAX_LSF_CONCURRENT_JOBS : JobQueue.MAX_LOCAL_CONCURRENT_JOBS;
+ int maxConcurrentJobs = JobQueue.isLSFAvailable() ? JobQueue.MAX_LSF_CONCURRENT_JOBS
+ : JobQueue.MAX_LOCAL_CONCURRENT_JOBS;
+ pblockLookup = new PBlock[pblocks.size()];
for (Entry e : pblocks.entrySet()) {
PBlock pblock = e.getKey();
+
+ String pblockDcpName = dcpName;
+ if (ensureExternalRoutability()) {
+ EDIFTools.removeVivadoBusPreventionAnnotations(design.getNetlist());
+ design.getNetlist().resetParentNetMap();
+ if (getExternalRoutabilitySideFile() == null) {
+ InlineFlopTools.createAndPlaceFlopsInlineOnTopPortsArbitrarily(design, clkName, pblock);
+ } else {
+ Map sideMap =
+ InlineFlopTools.parseSideMap(design.getNetlist(),
+ getExternalRoutabilitySideFile());
+ InlineFlopTools.createAndPlacePortFlopsOnSide(design, clkName, pblock, sideMap);
+ }
+ EDIFTools.ensurePreservedInterfaceVivado(design.getNetlist());
+ pblockDcpName = runDirectory + File.separator + "pblock" + pb + "_" + INITIAL_DCP_NAME;
+ design.writeCheckpoint(pblockDcpName);
+ }
+
for (PlacerDirective p : getPlacerDirectives()) {
for (RouterDirective r : getRouterDirectives()) {
for (double c : getClockUncertaintyValues()) {
String roundedC = printNS(c);
String uniqueID = p.name() + "_" + r.name() + "_" + roundedC;
if (pblock != null) {
- uniqueID = uniqueID + "_pblock" + pb + "_" + pblock.get(0).getLowerLeftSite() +"-";
+ uniqueID = uniqueID + "_pblock" + pb + "_" + pblock.get(0).getLowerLeftSite() + "-";
}
System.out.println(uniqueID);
String instDir = runDirectory + File.separator + uniqueID;
FileTools.makeDir(instDir);
- ArrayList tcl = createTclScript(dcpName, instDir, p, r, roundedC, e);
+ String encryptedTcl = null;
+ boolean encrypted = design.getNetlist().hasEncryptedCells();
+ if (encrypted) {
+ encryptedTcl = runDirectory + File.separator + "pblock" + pb +
+ "_" + INITIAL_ENCRYPTED_TCL_NAME;
+ }
+ ArrayList tcl = createTclScript(pblockDcpName, instDir, p, r, roundedC, e, encryptedTcl);
String scriptName = instDir + File.separator + RUN_TCL_NAME;
- FileTools.writeLinesToTextFile(tcl, scriptName);
-
- Job j = JobQueue.createJob();
- j.setRunDir(instDir);
- j.setCommand(getVivadoPath() + " -mode batch -source " + scriptName);
- if (jobsStarted < maxConcurrentJobs) {
- j.launchJob();
- jobs.addRunningJob(j);
- jobsStarted++;
- } else {
- jobs.addJob(j);
+
+ if (!reusePreviousResults()) {
+ FileTools.writeLinesToTextFile(tcl, scriptName);
+ Job j = JobQueue.createJob();
+ j.setRunDir(instDir);
+ j.setCommand(getVivadoPath() + " -mode batch -source " + scriptName);
+ if (jobsStarted < maxConcurrentJobs) {
+ j.launchJob();
+ jobs.addRunningJob(j);
+ jobsStarted++;
+ } else {
+ jobs.addJob(j);
+ }
}
}
}
}
- pb++;
+ pblockLookup[pb++] = pblock;
}
boolean success = jobs.runAllToCompletion(maxConcurrentJobs);
@@ -366,6 +471,80 @@ public void explorePerformance() {
System.out.println("Performance Explorer " + (success ? "Finished Successfully." : "Failed!"));
}
+ private Float parseWNSFromTimingReport(Path timingReport) {
+ for (String line : FileTools.getLinesFromTextFile(timingReport.toString())) {
+ if (line.contains("Slack ")) {
+ String[] values = line.split("\\s+");
+ assert (values.length >= 4);
+ String value = values[3].replace("ns", "");
+ return Float.parseFloat(value);
+ }
+ }
+ return null;
+ }
+
+ private static final String PBLOCK_SUFFIX = "_pblock";
+
+ public List> getBestResultsPerPBlock() {
+ List> results = new ArrayList<>();
+ for (int i = 0; i < pblocks.size(); i++) {
+ results.add(new Pair(null, -1 * Float.MAX_VALUE));
+ }
+ Path runDir = Paths.get(runDirectory);
+ String[] runDirFiles = runDir.toFile().list();
+ for (String dirName : runDirFiles) {
+ Path dir = runDir.resolve(dirName);
+ int pblockIdx = 0;
+ int i = dirName.indexOf(PBLOCK_SUFFIX);
+ if (i >= 0) {
+ int start = i + PBLOCK_SUFFIX.length();
+ int end = dirName.indexOf('_', start);
+ String tmp = dirName.substring(start, end);
+ pblockIdx = Integer.parseInt(tmp);
+ }
+ if (Files.isDirectory(dir)) {
+ Path routeTiming = dir.resolve("route_timing.twr");
+ if (Files.exists(routeTiming)) {
+ Float value = parseWNSFromTimingReport(routeTiming);
+ Pair bestFoundSoFar = results.get(pblockIdx);
+ if (value > bestFoundSoFar.getSecond()) {
+ bestFoundSoFar.setFirst(dir);
+ bestFoundSoFar.setSecond(value);
+ }
+ Pair curr = results.get(pblockIdx);
+ curr.setFirst(dir);
+ curr.setSecond(value);
+ }
+ }
+ }
+ return results;
+ }
+
+ public void getBestDesignPerPBlock() {
+ List> bestResultsPerPBlock = getBestResultsPerPBlock();
+ Path runDir = Paths.get(runDirectory);
+
+ int pblockNum = 0;
+ for (Pair best : bestResultsPerPBlock) {
+ String pblock = "pblock" + pblockNum;
+ Path bestPath = best.getFirst();
+ Float bestSlack = best.getSecond();
+ if (bestSlack < 0.0) {
+ System.out.println("No implementation passes timing for " + pblock);
+ }
+
+ System.out.println("Best result found at: " + bestPath);
+ Design d = Design.readCheckpoint(bestPath + File.separator + "routed.dcp");
+
+ if (ensureExternalRoutability()) {
+ InlineFlopTools.removeInlineFlops(d);
+ NetTools.unrouteTopLevelNetsThatLeavePBlock(d, getPBlock(pblockNum));
+ }
+
+ EDIFTools.ensurePreservedInterfaceVivado(d.getNetlist());
+ d.writeCheckpoint(runDirectory + File.separator + pblock + "_best.dcp");
+ }
+ }
private static final String INPUT_DCP_OPT = "i";
private static final String PBLOCK_FILE_OPT = "b";
@@ -383,6 +562,9 @@ public void explorePerformance() {
private static final String RUN_DIR_OPT = "d";
private static final String VIVADO_PATH_OPT = "y";
private static final String MAX_CONCURRENT_JOBS_OPT = "z";
+ private static final String ENSURE_EXT_ROUTABILITY = "e";
+ private static final String COLLECT_RESULTS_OPT = "o";
+ private static final String REUSE_PREVIOUS_RESULTS = "reuse-previous";
private static OptionParser createOptionParser() {
// Defaults
@@ -404,6 +586,9 @@ private static OptionParser createOptionParser() {
accepts(VIVADO_PATH_OPT).withOptionalArg().defaultsTo(DEFAULT_VIVADO).describedAs("Specifies vivado path");
accepts(CONTAIN_ROUTING_OPT).withOptionalArg().ofType(Boolean.class).defaultsTo(DEFAULT_CONTAIN_ROUTING).describedAs("Sets attribute on pblock to contain routing");
accepts(MAX_CONCURRENT_JOBS_OPT).withOptionalArg().ofType(Integer.class).defaultsTo(JobQueue.MAX_LOCAL_CONCURRENT_JOBS).describedAs("Max number of concurrent job when run locally");
+ accepts(ENSURE_EXT_ROUTABILITY).withOptionalArg().describedAs("Ensure all I/O are routable outside the pblock. Optionally provide a text file specifying which side of the pblock each top-level port should route to.");
+ accepts(COLLECT_RESULTS_OPT,"Collect results into output csv");
+ accepts(REUSE_PREVIOUS_RESULTS, "Reuse previous results if they exist");
acceptsAll( Arrays.asList(HELP_OPT, "?"), "Print Help" ).forHelp();
}};
@@ -425,7 +610,6 @@ private static void printHelp(OptionParser p) {
return;
}
-
public static String printNS(double num) {
return df.format(num);
}
@@ -445,6 +629,7 @@ public static void main(String[] args) {
String runDir = opts.hasArgument(RUN_DIR_OPT) ? (String) opts.valueOf(RUN_DIR_OPT) : System.getProperty("user.dir");
Design d = Design.readCheckpoint(dcpInputName);
+ EDIFTools.ensurePreservedInterfaceVivado(d.getNetlist());
PerformanceExplorer pe = new PerformanceExplorer(d, runDir, clkName, targetPeriod);
if (opts.hasArgument(MAX_CONCURRENT_JOBS_OPT)) {
@@ -467,7 +652,12 @@ public static void main(String[] args) {
pe.setVivadoPath((String)opts.valueOf(VIVADO_PATH_OPT));
pe.setContainRouting((boolean)opts.valueOf(CONTAIN_ROUTING_OPT));
pe.setAddEDIFAndMetadata((boolean)opts.valueOf(ADD_EDIF_METADATA_OPT));
-
+ pe.setGetBestPerPBlock(opts.has(COLLECT_RESULTS_OPT));
+ pe.setReusePreviousResults(opts.has(REUSE_PREVIOUS_RESULTS));
+ pe.setEnsureExternalRoutability(opts.has(ENSURE_EXT_ROUTABILITY));
+ if (opts.hasArgument(ENSURE_EXT_ROUTABILITY)) {
+ pe.setExternalRoutabilitySideFile((String) opts.valueOf(ENSURE_EXT_ROUTABILITY));
+ }
if (opts.hasArgument(PBLOCK_FILE_OPT)) {
String fileName = (String) opts.valueOf(PBLOCK_FILE_OPT);
@@ -491,5 +681,9 @@ public static void main(String[] args) {
}
pe.explorePerformance();
+
+ if (pe.getBestPerPBlock) {
+ pe.getBestDesignPerPBlock();
+ }
}
}
diff --git a/src/com/xilinx/rapidwright/util/Utils.java b/src/com/xilinx/rapidwright/util/Utils.java
index 69c243409..d66014d5b 100644
--- a/src/com/xilinx/rapidwright/util/Utils.java
+++ b/src/com/xilinx/rapidwright/util/Utils.java
@@ -393,7 +393,8 @@ public static boolean isNOC(SiteTypeEnum s) {
lockedSiteTypes = EnumSet.of(
SiteTypeEnum.CONFIG_SITE,
- SiteTypeEnum.BUFG
+ SiteTypeEnum.BUFG,
+ SiteTypeEnum.BUFGCE
);
sliceTypes = EnumSet.of(
diff --git a/src/com/xilinx/rapidwright/util/VivadoTools.java b/src/com/xilinx/rapidwright/util/VivadoTools.java
index b5cbce750..08ecb3ae2 100644
--- a/src/com/xilinx/rapidwright/util/VivadoTools.java
+++ b/src/com/xilinx/rapidwright/util/VivadoTools.java
@@ -161,15 +161,25 @@ public static String reportRouteStatus(Design design, String netName) {
}
private static Path writeCheckpoint(Design design) {
- final Path workdir = FileSystems.getDefault()
- .getPath("vivadoToolsWorkdir" + FileTools.getUniqueProcessAndHostID());
- File workdirHandle = new File(workdir.toString());
- workdirHandle.mkdirs();
+ final Path workdir = createTempVivadoToolsWorkDir();
final Path dcp = workdir.resolve("checkpoint.dcp");
design.writeCheckpoint(dcp);
return dcp;
}
+ /**
+ * Creates a unique, temporary work directory for Vivado interaction.
+ *
+ * @return
+ */
+ public static Path createTempVivadoToolsWorkDir() {
+ Path workDir = FileSystems.getDefault().getPath("vivadoToolsWorkdir" + FileTools.getUniqueProcessAndHostID());
+ ;
+ File workDirHandle = new File(workDir.toString());
+ workDirHandle.mkdirs();
+ return workDir;
+ }
+
/**
* Run Vivado's `write_bitstream` on the provided DCP file to generate a bit
* file at the specified location.
@@ -209,7 +219,6 @@ public static List writeBitstream(Path dcp, Path bitFile, boolean hasEnc
.getPath("vivadoToolsWorkdir" + FileTools.getUniqueProcessAndHostID());
File workdirHandle = new File(workdir.toString());
workdirHandle.mkdirs();
-
final Path outputLog = workdir.resolve("outputLog.log");
StringBuilder sb = new StringBuilder();
sb.append(createTclDCPLoadCommand(dcp, hasEncryptedIP));
@@ -282,15 +291,9 @@ public static ReportRouteStatusResult reportRouteStatus(Path dcp) {
* @return ReportRouteStatusResult object.
*/
public static ReportRouteStatusResult reportRouteStatus(Path dcp, boolean encrypted) {
- final Path workdir = FileSystems.getDefault()
- .getPath("vivadoToolsWorkdir" + FileTools.getUniqueProcessAndHostID());
- File workdirHandle = new File(workdir.toString());
- workdirHandle.mkdirs();
-
+ final Path workdir = createTempVivadoToolsWorkDir();
ReportRouteStatusResult rrs = reportRouteStatus(dcp, workdir, encrypted);
-
FileTools.deleteFolder(workdir.toString());
-
return rrs;
}
@@ -474,6 +477,31 @@ public static float getWorstSetupSlack(Path dcp, Path workdir, boolean encrypted
return Float.NaN;
}
+ /**
+ * Creates a utilization report and shapes report for a given synthesized DCP
+ * file.
+ *
+ * @param design The input DCP to measure and extract reports from.
+ * @param utilReport The desired path to a utilization report from Vivado
+ * (report_utilization -file ).
+ * @param shapesReport The desired shapes report.
+ */
+ public static void getUtilizationAndShapesReport(Path design, Path utilReport, Path shapesReport) {
+ final Path workDir = createTempVivadoToolsWorkDir();
+ final Path outputLog = workDir.resolve("outputLog.log");
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("open_checkpoint " + design.toString() + "\n");
+ sb.append("report_utilization -file " + utilReport.toString() + "\n");
+ sb.append("set_param place.debugShape " + shapesReport.toString() + "\n");
+ sb.append("place_design -directive Quick \n");
+ sb.append("set_param place.debugShape \"\"\n");
+
+ VivadoTools.runTcl(outputLog, sb.toString(), true);
+
+ FileTools.deleteFolder(workDir.toString());
+ }
+
/**
* Open a DCP in Vivado and write a new DCP.
*
diff --git a/test/src/com/xilinx/rapidwright/design/TestDesign.java b/test/src/com/xilinx/rapidwright/design/TestDesign.java
index 1bb0dddd3..1f8e9f0b3 100644
--- a/test/src/com/xilinx/rapidwright/design/TestDesign.java
+++ b/test/src/com/xilinx/rapidwright/design/TestDesign.java
@@ -59,6 +59,7 @@
import com.xilinx.rapidwright.edif.EDIFNetlist;
import com.xilinx.rapidwright.edif.EDIFPort;
import com.xilinx.rapidwright.edif.EDIFTools;
+import com.xilinx.rapidwright.router.Router;
import com.xilinx.rapidwright.support.RapidWrightDCP;
import com.xilinx.rapidwright.tests.CodePerfTracker;
import com.xilinx.rapidwright.util.FileTools;
@@ -524,6 +525,41 @@ public void testCreateModuleInstCopiesStaticSource(boolean unrouteStaticNets) {
}
}
+ @ParameterizedTest
+ @CsvSource({
+ "xc7z020clg400-1,SLICE_X100Y100/A6LUT,D19,D20,R14,LVCMOS33",
+ "xcku040-ffva1156-2-e,SLICE_X32Y46/A6LUT,AE10,AF9,AP8,LVCMOS18",
+ "xcau15p-ffvb676-2-e,SLICE_X1Y1/A6LUT,U26,R21,R20,LVCMOS12",
+ })
+ public void testCreateAndPlaceIOB(String partName, String lutLoc, String b0, String b1, String led, String ioStandard) {
+ Design d = new Design("HelloWorld", partName);
+
+ // Create all the design elements (LUT2, and 3 IOs)
+ Cell and2 = d.createAndPlaceCell("and2", Unisim.AND2, lutLoc);
+ Cell button0 = d.createAndPlaceIOB("button0", PinType.IN, b0, ioStandard);
+ Cell button1 = d.createAndPlaceIOB("button1", PinType.IN, b1, ioStandard);
+ Cell led0 = d.createAndPlaceIOB("led0", PinType.OUT, led, ioStandard);
+
+ // Connect Button 0 to the LUT2 input I0
+ Net net0 = d.createNet("button0_IBUF");
+ net0.connect(button0, "O");
+ net0.connect(and2, "I0");
+
+ // Connect Button 1 to the LUT2 input I1
+ Net net1 = d.createNet("button1_IBUF");
+ net1.connect(button1, "O");
+ net1.connect(and2, "I1");
+
+ // Connect the LUT2 (AND2) to the LED IO
+ Net net2 = d.createNet("and2");
+ net2.connect(and2, "O");
+ net2.connect(led0, "I");
+
+ new Router(d).routeDesign();
+
+ VivadoToolsHelper.assertFullyRouted(d);
+ }
+
@ParameterizedTest
@CsvSource({
"xcvu3p-ffvc1517-1-i,N28",
diff --git a/test/src/com/xilinx/rapidwright/design/TestModule.java b/test/src/com/xilinx/rapidwright/design/TestModule.java
index bf6c04e1e..d13f8e5e0 100644
--- a/test/src/com/xilinx/rapidwright/design/TestModule.java
+++ b/test/src/com/xilinx/rapidwright/design/TestModule.java
@@ -23,11 +23,11 @@
package com.xilinx.rapidwright.design;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
import com.xilinx.rapidwright.device.Device;
+import com.xilinx.rapidwright.device.Tile;
import com.xilinx.rapidwright.examples.AddSubGenerator;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
public class TestModule {
@@ -56,4 +56,18 @@ public void testModuleSLRRelocate() {
Assertions.assertTrue(slrAdder.isValidPlacement(slrAdder.getAnchor(), top));
Assertions.assertFalse(slrAdder.isValidPlacement(noSLRAdder.getAnchor(), top));
}
+
+ @Test
+ public void testGetCorrespondingTile() {
+ Device v80 = Device.getDevice("xcv80");
+
+ // Testing for SLL to SLL_1 special case because they have the same tile type but overlapping X,Y grids
+ Tile templateTile = v80.getTile("SLL_X23Y886");
+ Tile originalAnchor = v80.getTile("CLE_W_CORE_X22Y886");
+ Tile newAnchorTile = v80.getTile("CLE_W_CORE_X23Y900");
+ Tile newTile = Module.getCorrespondingTile(templateTile, newAnchorTile, originalAnchor);
+
+ Assertions.assertNotNull(newTile);
+ Assertions.assertEquals("SLL_1_X23Y900", newTile.getName());
+ }
}
diff --git a/test/src/com/xilinx/rapidwright/design/TestNetTools.java b/test/src/com/xilinx/rapidwright/design/TestNetTools.java
index a1469be9c..4628cff56 100644
--- a/test/src/com/xilinx/rapidwright/design/TestNetTools.java
+++ b/test/src/com/xilinx/rapidwright/design/TestNetTools.java
@@ -21,18 +21,26 @@
*/
package com.xilinx.rapidwright.design;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
+import com.xilinx.rapidwright.design.blocks.PBlock;
import com.xilinx.rapidwright.device.ClockRegion;
import com.xilinx.rapidwright.device.Device;
import com.xilinx.rapidwright.device.Node;
import com.xilinx.rapidwright.device.PIP;
+import com.xilinx.rapidwright.edif.EDIFCell;
+import com.xilinx.rapidwright.edif.EDIFDirection;
import com.xilinx.rapidwright.edif.EDIFHierNet;
+import com.xilinx.rapidwright.edif.EDIFNet;
+import com.xilinx.rapidwright.edif.EDIFPort;
+import com.xilinx.rapidwright.util.CodeGenerator;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
@@ -42,15 +50,15 @@ public class TestNetTools {
/**
* Tests the method NetTools.isGlobalClock(Net net).
* For each DCP file, Vivado uses the Tcl command
- * get_nets -hier -parent_net -filter { TYPE == "GLOBAL_CLOCK" }
- * to retrieve all nets of type GLOBAL_CLOCK.
+ * get_nets -hier -parent_net -filter { TYPE == "GLOBAL_CLOCK" }
+ * to retrieve all nets of type GLOBAL_CLOCK.
* The method NetTools.isGlobalClock(Net net) returns true for these nets and false for others.
- *
+ *
* All Versal/Ultrascale+/Series-7 designs under the RapidWrightDCP path are available to be checked, except for the following:
* picoblaze4_ooc_X6Y60_X6Y65_X10Y60_X10Y65.dcp The source cell bufgce_inst of this design is not placed yet.
* picoblaze_ooc_X10Y235_unreadable_edif.dcp This design would call vivado to generate an readable EDIF file.
- *
- * @param pathAndlobalClockNames Use ' ' as the delimiter to split this string, where the first element is the path to the DCP file,
+ *
+ * @param pathAndGlobalClockNames Use ' ' as the delimiter to split this string, where the first element is the path to the DCP file,
* and the remaining elements are the names of the global clock nets reported by Vivado.
*/
@ParameterizedTest
@@ -73,7 +81,7 @@ public class TestNetTools {
"picoblaze_partial.dcp clk",
// "reduce_or_routed_7overlaps.dcp",
// "testCopyImplementation.dcp",
-
+
// UltraScale
// "microblazeAndILA_3pblocks.dcp base_mb_i/clk_wiz_1/inst/clk_out1 base_mb_i/clk_wiz_1/inst/clkfbout_buf_base_mb_clk_wiz_1_0 base_mb_i/mdm_1/U0/No_Dbg_Reg_Access.BUFG_DRCK/Dbg_Clk_31 dbg_hub/inst/BSCANID.u_xsdbm_id/itck_i",
"microblazeAndILA_3pblocks_2024.1.dcp base_mb_i/clk_wiz_1/inst/clk_out1 base_mb_i/clk_wiz_1/inst/clkfbout_buf_base_mb_clk_wiz_1_0 base_mb_i/mdm_1/U0/No_Dbg_Reg_Access.BUFG_DRCK/Dbg_Clk_31 dbg_hub/inst/BSCANID.u_xsdbm_id/itck_i",
@@ -93,8 +101,8 @@ public class TestNetTools {
// "routethru_luts.dcp",
// "routethru_pip.dcp",
})
- public void testisGlobalClock(String pathAndlobalClockNames) {
- String[] tmpList = pathAndlobalClockNames.split(" ");
+ public void testisGlobalClock(String pathAndGlobalClockNames) {
+ String[] tmpList = pathAndGlobalClockNames.split(" ");
String path = tmpList[0];
HashSet globalClockNamesFromVivado = new HashSet<>();
for (int i = 1; i < tmpList.length; i++) {
@@ -258,4 +266,62 @@ public void testFindClockRootVRoute() {
ClockRegion clockRoot = NetTools.findClockRootVRoute(clkNet).getTile().getClockRegion();
Assertions.assertEquals(clockRoot, design.getDevice().getClockRegion(1, 1));
}
+
+ @Test
+ public void testUnrouteTopLevelNetsThatLeavePBlock() {
+ Design design = new Design("pblock_unroute_test", "xcv80-lsva4737-2MHP-e-S");
+ design.setDesignOutOfContext(true);
+ design.setAutoIOBuffers(false);
+ EDIFCell topCell = design.getNetlist().getTopCell();
+ EDIFPort clkPort = topCell.createPort("clk", EDIFDirection.INPUT, 1);
+ EDIFNet clkNet = topCell.createNet("clk");
+ clkNet.createPortInst(clkPort);
+ String[] pips = {"CLE_E_CORE_X31Y804/CLE_E_CORE.CLE_SLICEL_TOP_0_AQ_PIN->>CLE_SLICEL_TOP_0_AQ",
+ "INT_X31Y804/INT.LOGIC_OUTS_W1->INT_NODE_SDQ_ATOM_16_INT_OUT1",
+ "INT_X31Y804/INT.INT_NODE_SDQ_ATOM_16_INT_OUT1->>OUT_NN2_W_BEG0",
+ "INT_X31Y806/INT.IN_NN2_W_END0->INT_NODE_SDQ_ATOM_59_INT_OUT0",
+ "INT_X31Y806/INT.INT_NODE_SDQ_ATOM_59_INT_OUT0->>OUT_WW2_W_BEG0",
+ "INT_X30Y806/INT.IN_WW2_W_END0->INT_NODE_SDQ_ATOM_123_INT_OUT1",
+ "INT_X30Y806/INT.INT_NODE_SDQ_ATOM_123_INT_OUT1->>OUT_NN1_W_BEG2",
+ "INT_X30Y807/INT.IN_NN1_W_END2->INT_NODE_SDQ_ATOM_62_INT_OUT0",
+ "INT_X30Y807/INT.INT_NODE_SDQ_ATOM_62_INT_OUT0->>INT_SDQ_RED_ATOM_24_INT_OUT0",
+ "INT_X30Y807/INT.INT_SDQ_RED_ATOM_24_INT_OUT0->INT_NODE_IMUX_ATOM_65_INT_OUT0",
+ "INT_X30Y807/INT.INT_NODE_IMUX_ATOM_65_INT_OUT0->>BOUNCE_E0",
+ "CLE_W_CORE_X30Y807/CLE_W_CORE.CLE_SLICEL_TOP_0_AX->>CLE_SLICEL_TOP_0_AX_PIN",
+ "INT_X31Y806/INT.INT_NODE_SDQ_ATOM_59_INT_OUT0->>INT_SDQ_RED_ATOM_16_INT_OUT0",
+ "INT_X31Y806/INT.INT_SDQ_RED_ATOM_16_INT_OUT0->INT_NODE_SDQ_ATOM_11_INT_OUT1",
+ "INT_X31Y806/INT.INT_NODE_SDQ_ATOM_11_INT_OUT1->>OUT_EE1_E_BEG2",
+ "INT_X32Y806/INT.IN_EE1_E_END2->INT_NODE_IMUX_ATOM_97_INT_OUT0",
+ "INT_X32Y806/INT.INT_NODE_IMUX_ATOM_97_INT_OUT0->>BOUNCE_W0",
+ "CLE_E_CORE_X32Y806/CLE_E_CORE.CLE_SLICEL_TOP_0_AX->>CLE_SLICEL_TOP_0_AX_PIN"};
+
+
+ Net net = CodeGenerator.createTestNet(design, "test_net", pips);
+ EDIFHierNet logicalClkNet = new EDIFHierNet(design.getNetlist().getTopHierCellInst(), clkPort.getInternalNet());
+ Net clk = design.createNet(logicalClkNet);
+ Cell flop0 = design.createAndPlaceCell(design.getTopEDIFCell(), "flop0",
+ Unisim.FDRE, "SLICE_X98Y800/AFF");
+ net.connect(flop0, "Q");
+ design.getGndNet().connect(flop0, "R");
+ design.getVccNet().connect(flop0, "CE");
+ clk.connect(flop0, "C");
+
+ Cell flop1 = design.createAndPlaceCell(design.getTopEDIFCell(), "flop1",
+ Unisim.FDRE, "SLICE_X96Y803/AFF");
+ net.connect(flop1, "D");
+ design.getGndNet().connect(flop1, "R");
+ design.getVccNet().connect(flop1, "CE");
+ clk.connect(flop1, "C");
+
+ EDIFPort outPort = topCell.createPort("out", EDIFDirection.OUTPUT, 1);
+ EDIFNet testNet = design.getNetlist().getHierNetFromName("test_net").getNet();
+ testNet.createPortInst(outPort);
+ PBlock pblock = new PBlock(design.getDevice(), "IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 " +
+ "DSP_X0Y398:DSP_X1Y405 " + "DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 " + "SLICE_X92Y796:SLICE_X99Y811");
+
+ List unroutedNets = NetTools.unrouteTopLevelNetsThatLeavePBlock(design, pblock);
+ Assertions.assertEquals(1, unroutedNets.size());
+ Assertions.assertEquals("test_net", unroutedNets.get(0).getName());
+ Assertions.assertEquals(0, net.getPIPs().size());
+ }
}
diff --git a/test/src/com/xilinx/rapidwright/design/blocks/TestUtilizationType.java b/test/src/com/xilinx/rapidwright/design/blocks/TestUtilizationType.java
new file mode 100644
index 000000000..ac163d0ea
--- /dev/null
+++ b/test/src/com/xilinx/rapidwright/design/blocks/TestUtilizationType.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright (c) 2025, Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Author: Chris Lavin, AMD Advanced Research and Development.
+ *
+ * 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.design.blocks;
+
+import java.util.Map;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import com.xilinx.rapidwright.design.Design;
+import com.xilinx.rapidwright.support.RapidWrightDCP;
+
+public class TestUtilizationType {
+
+ @Test
+ public void testComputeUtilization() {
+ Design design = RapidWrightDCP.loadDCP("optical-flow.dcp");
+
+ Map utilization = UtilizationType.computeUtilization(design);
+ System.out.println(utilization);
+
+ Assertions.assertEquals(2891, utilization.get(UtilizationType.CARRY8S));
+ Assertions.assertEquals(25740, utilization.get(UtilizationType.CLB_REGS));
+ Assertions.assertEquals(124, utilization.get(UtilizationType.DSPS));
+ Assertions.assertEquals(59, utilization.get(UtilizationType.RAMB36S_FIFOS));
+ Assertions.assertEquals(10, utilization.get(UtilizationType.RAMB18S));
+ Assertions.assertEquals(287, utilization.get(UtilizationType.LUTS_AS_MEMORY));
+ Assertions.assertEquals(64, utilization.get(UtilizationType.BRAMS));
+ Assertions.assertEquals(30239, utilization.get(UtilizationType.CLB_LUTS));
+
+ }
+}
diff --git a/test/src/com/xilinx/rapidwright/design/tools/TestInlineFlopTools.java b/test/src/com/xilinx/rapidwright/design/tools/TestInlineFlopTools.java
new file mode 100644
index 000000000..7118a5749
--- /dev/null
+++ b/test/src/com/xilinx/rapidwright/design/tools/TestInlineFlopTools.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2025, Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Author: Andrew Butt, AMD Research and Advanced Development
+ *
+ * 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.design.tools;
+
+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.Unisim;
+import com.xilinx.rapidwright.design.blocks.PBlock;
+import com.xilinx.rapidwright.design.xdc.ConstraintTools;
+import com.xilinx.rapidwright.device.BEL;
+import com.xilinx.rapidwright.device.SLR;
+import com.xilinx.rapidwright.device.Site;
+import com.xilinx.rapidwright.eco.ECOPlacementHelper;
+import com.xilinx.rapidwright.edif.EDIFCell;
+import com.xilinx.rapidwright.edif.EDIFDirection;
+import com.xilinx.rapidwright.edif.EDIFHierNet;
+import com.xilinx.rapidwright.edif.EDIFPort;
+import com.xilinx.rapidwright.edif.EDIFPortInst;
+import com.xilinx.rapidwright.support.RapidWrightDCP;
+import com.xilinx.rapidwright.util.Pair;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class TestInlineFlopTools {
+
+ private static Pair nextAvailPlacement(Design design, Iterator itr, SLR slr, String bel) {
+ while (itr.hasNext()) {
+ Site curr = itr.next();
+ if (slr != null && curr.getTile().getSLR() != slr) {
+ continue;
+ }
+ SiteInst candidate = design.getSiteInstFromSite(curr);
+ if (candidate == null) {
+ return new Pair<>(curr, curr.getBEL(bel));
+ }
+ }
+ return null;
+ }
+
+ private static void createAndPlaceFlopInlineAtSpecificBEL(Design design, String clkNet, EDIFPort port, String bel,
+ PBlock keepOut) {
+ Site start = keepOut.getAllSites("SLICE").iterator().next();
+ boolean exclude = true;
+
+ EDIFHierNet clk = design.getNetlist().getHierNetFromName(clkNet);
+
+ Set siteInstsToRoute = new HashSet<>();
+
+ for (int i : port.getBitBlastedIndices()) {
+ EDIFPortInst inst = port.getInternalPortInstFromIndex(i);
+
+ Iterator siteItr = ECOPlacementHelper.spiralOutFrom(start, keepOut, exclude).iterator();
+ siteItr.next(); // Skip the first site, as we are suggesting one inside the pblock
+ Pair loc = nextAvailPlacement(design, siteItr, null, bel);
+ Cell flop = InlineFlopTools.createAndPlaceFlopInlineOnTopPortInst(design, inst, loc, clk);
+ siteInstsToRoute.add(flop.getSiteInst());
+ }
+ for (SiteInst si : siteInstsToRoute) {
+ si.routeSite();
+ }
+ }
+
+ @ParameterizedTest
+ @CsvSource({
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,AFF,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,AFF2,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,BFF,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,BFF2,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,CFF,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,CFF2,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,DFF,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,DFF2,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,EFF,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,EFF2,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,FFF,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,FFF2,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,GFF,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,GFF2,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,HFF,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcv80-lsva4737-2MHP-e-S,SLICE_X96Y803/AFF,HFF2,IRI_QUAD_X58Y3212:IRI_QUAD_X59Y3275 DSP_X0Y398:DSP_X1Y405 DSP58_CPLX_X0Y398:DSP58_CPLX_X0Y405 SLICE_X92Y796:SLICE_X99Y811",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,AFF,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,AFF2,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,BFF,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,BFF2,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,CFF,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,CFF2,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,DFF,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,DFF2,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,EFF,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,EFF2,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,FFF,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,FFF2,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,GFF,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,GFF2,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,HFF,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xcvu3p-ffvc1517-2-i,SLICE_X14Y237/AFF,HFF2,RAMB36_X1Y47:RAMB36_X1Y47 RAMB18_X1Y94:RAMB18_X1Y95 SLICE_X13Y235:SLICE_X16Y239",
+ "xc7a200tsbg484-1,SLICE_X60Y124/AFF,AFF,RAMB36_X3Y22:RAMB36_X3Y27 RAMB18_X3Y44:RAMB18_X3Y55 DSP48_X2Y44:DSP48_X3Y55 SLICE_X46Y110:SLICE_X67Y139",
+ "xc7a200tsbg484-1,SLICE_X60Y124/AFF,A5FF,RAMB36_X3Y22:RAMB36_X3Y27 RAMB18_X3Y44:RAMB18_X3Y55 DSP48_X2Y44:DSP48_X3Y55 SLICE_X46Y110:SLICE_X67Y139",
+ "xc7a200tsbg484-1,SLICE_X60Y124/AFF,BFF,RAMB36_X3Y22:RAMB36_X3Y27 RAMB18_X3Y44:RAMB18_X3Y55 DSP48_X2Y44:DSP48_X3Y55 SLICE_X46Y110:SLICE_X67Y139",
+ "xc7a200tsbg484-1,SLICE_X60Y124/AFF,B5FF,RAMB36_X3Y22:RAMB36_X3Y27 RAMB18_X3Y44:RAMB18_X3Y55 DSP48_X2Y44:DSP48_X3Y55 SLICE_X46Y110:SLICE_X67Y139",
+ "xc7a200tsbg484-1,SLICE_X60Y124/AFF,CFF,RAMB36_X3Y22:RAMB36_X3Y27 RAMB18_X3Y44:RAMB18_X3Y55 DSP48_X2Y44:DSP48_X3Y55 SLICE_X46Y110:SLICE_X67Y139",
+ "xc7a200tsbg484-1,SLICE_X60Y124/AFF,C5FF,RAMB36_X3Y22:RAMB36_X3Y27 RAMB18_X3Y44:RAMB18_X3Y55 DSP48_X2Y44:DSP48_X3Y55 SLICE_X46Y110:SLICE_X67Y139",
+ "xc7a200tsbg484-1,SLICE_X60Y124/AFF,DFF,RAMB36_X3Y22:RAMB36_X3Y27 RAMB18_X3Y44:RAMB18_X3Y55 DSP48_X2Y44:DSP48_X3Y55 SLICE_X46Y110:SLICE_X67Y139",
+ "xc7a200tsbg484-1,SLICE_X60Y124/AFF,D5FF,RAMB36_X3Y22:RAMB36_X3Y27 RAMB18_X3Y44:RAMB18_X3Y55 DSP48_X2Y44:DSP48_X3Y55 SLICE_X46Y110:SLICE_X67Y139",
+ })
+
+ public void testRemoveInlineFlops(String partName, String ffBel, String harnessBel, String pblockRange) {
+ Design design = new Design("inline_flops", partName);
+ design.setDesignOutOfContext(true);
+ design.setAutoIOBuffers(false);
+ EDIFCell topCell = design.getNetlist().getTopCell();
+ // Create clock port and net
+ EDIFPort clkPort = topCell.createPort("clk", EDIFDirection.INPUT, 1);
+ Net clk = design.createNet("clk");
+ EDIFHierNet logicalClkNet = clk.getLogicalHierNet();
+ logicalClkNet.getNet().createPortInst(clkPort);
+
+ // Create in port and net
+ EDIFPort inPort = topCell.createPort("in", EDIFDirection.INPUT, 1);
+ Net inNet = design.createNet("in");
+ EDIFHierNet logicalInNet = inNet.getLogicalHierNet();
+ logicalInNet.getNet().createPortInst(inPort);
+
+ // Create out port and net
+ EDIFPort outPort = topCell.createPort("out", EDIFDirection.OUTPUT, 1);
+ Net outNet = design.createNet("out");
+ EDIFHierNet logicalOutNet = outNet.getLogicalHierNet();
+ logicalOutNet.getNet().createPortInst(outPort);
+
+ Cell flop0 = design.createAndPlaceCell(design.getTopEDIFCell(), "flop0",
+ Unisim.FDRE, ffBel);
+ inNet.connect(flop0, "D");
+ outNet.connect(flop0, "Q");
+ design.getGndNet().connect(flop0, "R");
+ design.getVccNet().connect(flop0, "CE");
+ clk.connect(flop0, "C");
+
+ PBlock pblock = new PBlock(design.getDevice(), pblockRange);
+ createAndPlaceFlopInlineAtSpecificBEL(design, "clk", inPort, harnessBel, pblock);
+ design.getNetlist().resetParentNetMap();
+ createAndPlaceFlopInlineAtSpecificBEL(design, "clk", outPort, harnessBel, pblock);
+ InlineFlopTools.removeInlineFlops(design);
+ for (Cell c : design.getCells()) {
+ Assertions.assertFalse(c.getName().endsWith(InlineFlopTools.INLINE_SUFFIX));
+ }
+ Assertions.assertEquals(1, design.getVccNet().getSinkPins().size());
+ }
+
+ @Test
+ public void testArbitraryInlineFlopPlacement() {
+ Design design = RapidWrightDCP.loadDCP("PicoBlazeArray/pblock0.dcp");
+ Map pblocks = ConstraintTools.getPBlocksFromXDC(design);
+ PBlock pblock = pblocks.get("pe_pblock_1");
+ design.unrouteDesign();
+ design.unplaceDesign();
+ InlineFlopTools.createAndPlaceFlopsInlineOnTopPortsArbitrarily(design, "clk", pblock);
+ int count = 0;
+ for (Cell c : design.getCells()) {
+ if (c.isPlaced()) {
+ count++;
+ Assertions.assertTrue(c.getName().contains(InlineFlopTools.INLINE_SUFFIX));
+ Assertions.assertFalse(c.getName().contains("clk"));
+ }
+ }
+ Assertions.assertEquals(65, count);
+ }
+
+ @Test
+ public void testCentroidInlineFlopPlacement() {
+ Design design = RapidWrightDCP.loadDCP("PicoBlazeArray/pblock0.dcp");
+ Map pblocks = ConstraintTools.getPBlocksFromXDC(design);
+ PBlock pblock = pblocks.get("pe_pblock_1");
+ InlineFlopTools.createAndPlaceFlopsInlineOnTopPortsNearPins(design, "clk", pblock);
+ int count = 0;
+ for (Cell c : design.getCells()) {
+ if (c.isPlaced()) {
+ if (c.getName().contains(InlineFlopTools.INLINE_SUFFIX)) {
+ count++;
+ Assertions.assertFalse(c.getName().contains("clk"));
+ }
+ }
+ }
+ Assertions.assertEquals(65, count);
+ }
+}
diff --git a/test/src/com/xilinx/rapidwright/eco/TestECOTools.java b/test/src/com/xilinx/rapidwright/eco/TestECOTools.java
index 7557fcb11..7f4b9122f 100644
--- a/test/src/com/xilinx/rapidwright/eco/TestECOTools.java
+++ b/test/src/com/xilinx/rapidwright/eco/TestECOTools.java
@@ -1346,5 +1346,4 @@ public void testConnectNetWithoutSource() {
VivadoToolsHelper.assertFullyRouted(design);
}
-
}