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

Enable RWRoute to load Interchange designs from main() #834

Merged
merged 3 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 2 additions & 3 deletions src/com/xilinx/rapidwright/interchange/DcpToInterchange.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

Expand All @@ -43,8 +42,8 @@ public static void main(String[] args) throws IOException {
Design design = Design.readCheckpoint(args[0]);
String baseName = Paths.get(args[0]).getFileName().toString();
baseName = FileTools.removeFileExtension(baseName);
String logNetlistName = baseName + ".netlist";
String physNetlistName = baseName + ".phys";
String logNetlistName = baseName + Interchange.LOG_NETLIST_EXT;
String physNetlistName = baseName + Interchange.PHYS_NETLIST_EXT;
String xdcName = baseName + ".xdc";

LogNetlistWriter.writeLogNetlist(design.getNetlist(), logNetlistName);
Expand Down
192 changes: 182 additions & 10 deletions src/com/xilinx/rapidwright/interchange/Interchange.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,33 +23,205 @@

package com.xilinx.rapidwright.interchange;

import com.xilinx.rapidwright.design.Design;
import com.xilinx.rapidwright.edif.EDIFNetlist;
import com.xilinx.rapidwright.tests.CodePerfTracker;
import com.xilinx.rapidwright.util.FileTools;
import org.capnproto.MessageBuilder;
import org.capnproto.MessageReader;
import org.capnproto.ReaderOptions;
import org.capnproto.Serialize;
import org.capnproto.SerializePacked;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.capnproto.MessageBuilder;
import org.capnproto.MessageReader;
import org.capnproto.ReaderOptions;
import org.capnproto.Serialize;
import org.capnproto.SerializePacked;

import com.xilinx.rapidwright.design.ConstraintGroup;
import com.xilinx.rapidwright.design.Design;
import com.xilinx.rapidwright.edif.EDIFNetlist;
import com.xilinx.rapidwright.tests.CodePerfTracker;
import com.xilinx.rapidwright.util.FileTools;

public class Interchange {

/** Flag indicating use of Packed Cap'n Proto Serialization */
public static boolean IS_PACKED = false;
/** Flag indicating that files are gzipped on output */
public static boolean IS_GZIPPED = true;
/** Standard file extension for a logical netlist in the FPGA Interchange Format */
public static final String LOG_NETLIST_EXT = ".netlist";
/** Standard file extension for a physical netlist in the FPGA Interchange Format */
public static final String PHYS_NETLIST_EXT = ".phys";

/**
* Reads an FPGA Interchange Format design from a specified file. The file could
* be a logical netlist or physical netlist and this method will assume the
* corresponding file with the same root name to load as a companion. If the
* provided name is a logical netlist and no physical netlist is found, it will
* only load the logical netlist. It will also load any XDC file with the same
* root name.
*
* @param fileName The name of the logical or physical netlist file.
* @return The loaded design.
*/
public static Design readInterchangeDesign(String fileName) {
return readInterchangeDesign(Paths.get(fileName));
}

/**
* Reads an FPGA Interchange Format design from a specified file. The file could
* be a logical netlist or physical netlist and this method will assume the
* corresponding file with the same root name to load as a companion. If the
* provided name is a logical netlist and no physical netlist is found, it will
* only load the logical netlist. It will also load any XDC file with the same
* root name.
*
* @param filePath The path to the logical or physical netlist file.
* @return The loaded design.
*/
public static Design readInterchangeDesign(Path filePath) {
String lowerName = filePath.toString().toLowerCase();
Path logFileName = lowerName.endsWith(LOG_NETLIST_EXT) ? filePath : getExistingCompanionFile(filePath);
Path physFileName = lowerName.endsWith(PHYS_NETLIST_EXT) ? filePath : getExistingCompanionFile(filePath);

if (logFileName == null) {
throw new RuntimeException("ERROR: Could not find logical netlist file: " + logFileName);
}

String xdcFileName = FileTools.replaceExtension(logFileName, ".xdc").toString();

return readInterchangeDesign(logFileName.toString(), physFileName.toString(), xdcFileName, false, null);
}

/**
* Gets the existing Interchange companion file name based on the provided
* filename. For example, if the logical netlist file name is provided, it will
* return the physical netlist filename if it exists. If the physical netlist is
* provided, it returns the logical netlist filename if the file exists.
*
* @param filePath Path of an existing FPGA Interchange file (logical netlist
* with the {@link #LOG_NETLIST_EXT} extension or physical
* netlist with the {@link #PHYS_NETLIST_EXT})
* @return The companion file if it exists or null if it could not be found.
*/
private static Path getExistingCompanionFile(Path filePath) {
String lowerFileName = filePath.toString().toLowerCase();
if (lowerFileName.endsWith(LOG_NETLIST_EXT)) {
Path physFileName = FileTools.replaceExtension(filePath, PHYS_NETLIST_EXT);
if (Files.exists(physFileName)) {
return physFileName;
}
} else if (lowerFileName.endsWith(PHYS_NETLIST_EXT)) {
Path logFileName = FileTools.replaceExtension(filePath, LOG_NETLIST_EXT);
if (Files.exists(logFileName)) {
return logFileName;
}
}
return null;
}

/**
* Reads a set of existing FPGA Interchange files and returns a new design.
*
* @param logFileName The logical netlist file to be loaded.
* @param physFileName The physical netlist file to be loaded, this can be
* null for no placement or routing information.
* @param xdcFileName The constraints to associate with the design, this can
* be null for no constraints.
* @param isOutOfContext A flag indicating if the design should be marked out of
* context.
* @param t If using an existing CodePerfTracker, this allows
* continuity otherwise the default is null (an instance
* will be created each time) and will track runtime of
* each loading step. To silence this measurement, provide
* {@link CodePerfTracker#SILENT}).
* @return The newly created design based on the provided files.
*/
public static Design readInterchangeDesign(String logFileName, String physFileName, String xdcFileName,
boolean isOutOfContext, CodePerfTracker t) {
String msg = "Reading Interchange: " + logFileName;
CodePerfTracker tt = t == null ? new CodePerfTracker(msg, true) : t;
Design design = null;
try {
tt.start("Read Logical Netlist");
EDIFNetlist n = LogNetlistReader.readLogNetlist(logFileName);
if (physFileName != null) {
tt.stop().start("Read Physical Netlist");
design = PhysNetlistReader.readPhysNetlist(physFileName, n);
} else {
// No physical netlist information, let's attach the logical netlist to a new
// design
design = new Design(n);
}
if (xdcFileName != null) {
File xdcFile = new File(xdcFileName);
if (xdcFile.exists()) {
// Add XDC constraints
tt.stop().start("Read Constraints");
List<String> lines = Files.readAllLines(xdcFile.toPath(), Charset.defaultCharset());
design.setXDCConstraints(lines, ConstraintGroup.NORMAL);
}
}
if (isOutOfContext) {
design.setAutoIOBuffers(false);
design.setDesignOutOfContext(true);
}
tt.stop().printSummary();
return design;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

/**
* Writes out a set of Interchange files for the given design. It will write up
* to 3 files with the logical netlist always being written out. The two
* optional files are the physical netlist and XDC (constraints) file.
*
* @param design The design in memory to write out.
* @param rootFileName The root or common name among the output files.
*/
public static void writeDesignToInterchange(Design design, String rootFileName) {
String logFileName = rootFileName + LOG_NETLIST_EXT;
try {
LogNetlistWriter.writeLogNetlist(design.getNetlist(), logFileName);

if (design.getSiteInsts().size() > 0 || design.getNets().size() > 0) {
String physFileName = rootFileName + PHYS_NETLIST_EXT;
PhysNetlistWriter.writePhysNetlist(design, physFileName);
}

if (!design.getXDCConstraints(ConstraintGroup.NORMAL).isEmpty()) {
String xdcFileName = rootFileName + ".xdc";
FileTools.writeLinesToTextFile(design.getXDCConstraints(ConstraintGroup.NORMAL), xdcFileName);
}

} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

/**
* Checks if the provided file name is a logical or physical FPGA Interchange
* file.
*
* @param fileName The file name in question.
* @return True if the file name matches the logical or physical netlist file
* name type.
*/
public static boolean isInterchangeFile(String fileName) {
String lowerFileName = fileName.toLowerCase();
return lowerFileName.endsWith(LOG_NETLIST_EXT) || lowerFileName.endsWith(PHYS_NETLIST_EXT);
}

/**
* Common method to write out Interchange files
Expand Down
22 changes: 16 additions & 6 deletions src/com/xilinx/rapidwright/rwroute/RWRoute.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import com.xilinx.rapidwright.device.Tile;
import com.xilinx.rapidwright.device.TileTypeEnum;
import com.xilinx.rapidwright.edif.EDIFHierNet;
import com.xilinx.rapidwright.interchange.Interchange;
import com.xilinx.rapidwright.router.RouteThruHelper;
import com.xilinx.rapidwright.tests.CodePerfTracker;
import com.xilinx.rapidwright.timing.ClkRouteTiming;
Expand Down Expand Up @@ -1886,23 +1887,32 @@ protected static Design routeDesign(Design design, RWRoute router) {
}

/**
* The main interface of {@link RWRoute} that reads in a {@link Design} checkpoint,
* and parses the arguments for the {@link RWRouteConfig} object of the router.
* @param args An array of strings that are used to create a {@link RWRouteConfig} object for the router.
* The main interface of {@link RWRoute} that reads in a {@link Design} design
* (DCP or FPGA Interchange), and parses the arguments for the
* {@link RWRouteConfig} object of the router.
*
* @param args An array of strings that are used to create a
* {@link RWRouteConfig} object for the router.
*/
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("USAGE: <input.dcp> <output.dcp>");
System.out.println("USAGE: <input.dcp|input.phys> <output.dcp>");
return;
}
// Reads the output directory and set the output design checkpoint file name
String routedDCPfileName = args[1];

CodePerfTracker t = new CodePerfTracker("RWRoute", true);

// Reads in a design checkpoint and routes it
// Reads in a design and routes it
String[] rwrouteArgs = Arrays.copyOfRange(args, 2, args.length);
Design routed = routeDesignWithUserDefinedArguments(Design.readCheckpoint(args[0]), rwrouteArgs);
Design input = null;
if (Interchange.isInterchangeFile(args[0])) {
input = Interchange.readInterchangeDesign(args[0]);
} else {
input = Design.readCheckpoint(args[0]);
}
Design routed = routeDesignWithUserDefinedArguments(input, rwrouteArgs);

// Writes out the routed design checkpoint
routed.writeCheckpoint(routedDCPfileName,t);
Expand Down
10 changes: 9 additions & 1 deletion src/com/xilinx/rapidwright/util/FileTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -1997,7 +1997,15 @@ public static Path appendExtension(Path path, String extension) {
return path.resolveSibling(path.getFileName().toString()+extension);
}


/**
* Replaces the file extension of the provided file name. The current extension
* includes the final period '.' and all characters following it. If the file
* has no extension, it will add it as a suffix to the filename.
*
* @param path Name of the file to receive the updated extension.
* @param newExtension The new extension (should include starting period '.')
* @return The newly updated file path.
*/
public static Path replaceExtension(Path path, String newExtension) {
String fn = path.getFileName().toString();
int idx = fn.lastIndexOf('.');
Expand Down
35 changes: 35 additions & 0 deletions test/src/com/xilinx/rapidwright/rwroute/TestRWRoute.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

package com.xilinx.rapidwright.rwroute;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand All @@ -32,21 +34,26 @@
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Disabled;
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;
import org.junit.jupiter.params.provider.EnumSource;

import com.xilinx.rapidwright.design.Cell;
import com.xilinx.rapidwright.design.Design;
import com.xilinx.rapidwright.design.DesignTools;
import com.xilinx.rapidwright.design.Net;
import com.xilinx.rapidwright.design.SiteInst;
import com.xilinx.rapidwright.design.SitePinInst;
import com.xilinx.rapidwright.design.Unisim;
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.edif.EDIFTools;
import com.xilinx.rapidwright.interchange.Interchange;
import com.xilinx.rapidwright.support.LargeTest;
import com.xilinx.rapidwright.support.RapidWrightDCP;
import com.xilinx.rapidwright.util.FileTools;
Expand Down Expand Up @@ -316,4 +323,32 @@ public void testBug701() {
Assertions.assertEquals(0, rrs.unroutedNets);
}
}

private Design generateSmallPlacedDesign() {
Design d = new Design("HelloWorld", Device.KCU105);
Cell and2 = d.createAndPlaceCell("and2", Unisim.AND2, "SLICE_X100Y100/A6LUT");
Net net0 = d.createNet("button0_IBUF");
net0.connect(and2, "O");
net0.connect(and2, "I0");
net0.connect(and2, "I1");

// Route site internal nets
d.routeSites();

EDIFTools.ensureCorrectPartInEDIF(d.getNetlist(), d.getPartName());
return d;
}

@Test
public void testRWRouteInterchange(@TempDir Path dir) {
Path rootFile = dir.resolve("interchange-design");
Interchange.writeDesignToInterchange(generateSmallPlacedDesign(), rootFile.toString());
Path outputFile = dir.resolve("output.dcp");
RWRoute.main(new String[] {
rootFile.toString() + Interchange.PHYS_NETLIST_EXT,
outputFile.toString(),
"--nonTimingDriven"
});
Assertions.assertTrue(Files.exists(outputFile));
}
}