From a952974db9fd7a21f15d9a639f452f1e3a6f44fc Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Thu, 12 Oct 2023 15:26:24 -0700 Subject: [PATCH 1/6] Add ECOTools package Signed-off-by: Eddie Hung --- .../rapidwright/design/DesignTools.java | 39 +- src/com/xilinx/rapidwright/eco/ECOTools.java | 1021 +++++++++++++++++ .../rapidwright/router/RouteThruHelper.java | 19 + .../xilinx/rapidwright/eco/TestECOTools.java | 438 +++++++ 4 files changed, 1514 insertions(+), 3 deletions(-) create mode 100644 src/com/xilinx/rapidwright/eco/ECOTools.java create mode 100644 test/src/com/xilinx/rapidwright/eco/TestECOTools.java diff --git a/src/com/xilinx/rapidwright/design/DesignTools.java b/src/com/xilinx/rapidwright/design/DesignTools.java index a7f802546..344488532 100644 --- a/src/com/xilinx/rapidwright/design/DesignTools.java +++ b/src/com/xilinx/rapidwright/design/DesignTools.java @@ -1997,11 +1997,11 @@ public static boolean stampPlacement(Design design, Module stamp, Map getConnectedCells(SitePinInst pin) { - HashSet cells = new HashSet(); + HashSet cells = new HashSet<>(); SiteInst si = pin.getSiteInst(); if (si == null) return cells; for (BELPin p : pin.getBELPin().getSiteConns()) { @@ -2028,6 +2028,39 @@ public static Set getConnectedCells(SitePinInst pin) { return cells; } + /** + * Looks in the site instance for BEL pins connected to this site pin. + * @param pin The pin to examine for connected BEL pins + * @return Set of BEL pins to this site pin + */ + public static Set getConnectedBELPins(SitePinInst pin) { + HashSet pins = new HashSet<>(); + SiteInst si = pin.getSiteInst(); + if (si == null) return pins; + for (BELPin p : pin.getBELPin().getSiteConns()) { + if (p.getBEL().getBELClass() == BELClass.RBEL) { + SitePIP pip = si.getUsedSitePIP(p.getBELName()); + if (pip == null) continue; + if (p.isOutput()) { + p = pip.getInputPin().getSiteConns().get(0); + Cell c = si.getCell(p.getBELName()); + if (c != null) pins.add(p); + } else { + for (BELPin snk : pip.getOutputPin().getSiteConns()) { + Cell c = si.getCell(snk.getBELName()); + if (c != null) pins.add(snk); + } + } + } else { + Cell c = si.getCell(p.getBELName()); + if (c != null && c.getLogicalPinMapping(p.getName()) != null) { + pins.add(p); + } + } + } + return pins; + } + /** * Quick and dumb placement of a cell. Does not attempt * any optimization and will not change the placement diff --git a/src/com/xilinx/rapidwright/eco/ECOTools.java b/src/com/xilinx/rapidwright/eco/ECOTools.java new file mode 100644 index 000000000..f0e26e2bd --- /dev/null +++ b/src/com/xilinx/rapidwright/eco/ECOTools.java @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2023, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Author: Eddie Hung, Advanced Micro Devices, Inc. + * + * 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.eco; + +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.BEL; +import com.xilinx.rapidwright.device.BELClass; +import com.xilinx.rapidwright.device.BELPin; +import com.xilinx.rapidwright.device.SitePIP; +import com.xilinx.rapidwright.edif.EDIFCell; +import com.xilinx.rapidwright.edif.EDIFCellInst; +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.EDIFPort; +import com.xilinx.rapidwright.edif.EDIFPortInst; +import com.xilinx.rapidwright.edif.EDIFTools; +import com.xilinx.rapidwright.rwroute.RouterHelper; +import com.xilinx.rapidwright.util.Pair; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A collection of methods for performing ECO operations. + * + * NOTE: These methods assume the EDIF (logical) netlist is unfolded. + * + * A folded netlist containing two instances of the 'foo' cell with instances + * names 'u1' and 'u2' would have the following hierarchy: 'top/u1(foo)' and + * 'top/u2(foo)'. Further, assume that 'foo' contains two leaf cell instances + * 'lut1' and 'lut2' for a total of four leaf cells in the netlist. + * + * Modifying (e.g. connecting, disconnecting, removing, etc.) instance + * 'top/u1(foo)/lut1' would cause the 'lut1' instance from cell 'foo' to be + * modified, which would have the un-obvious effect of also modifying + * 'top/u2(foo)/lut1' from the netlist too. + * With an unfolded netlist, the 'foo' cell would be expected to be replaced + * by two (identical) cells 'foo1' and 'foo2', each instantiated once. Here, + * modifying 'top/u1(foo1)/lut1' would no longer affect 'top/u2(foo2)/lut1'. + */ +public class ECOTools { + /** + * Given a list of EDIFHierPortInst objects, disconnect these pins from their current nets. + * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) + * state, and is modelled on Vivado's disconnect_net -pinlist command. + * @param design The design where the pin(s) are instantiated. + * @param pins A list of hierarchical pins for disconnection. + * @param deferredRemovals An optional map that, if passed in non-null will be populated with + * site pins marked for removal. The map allows for persistent tracking + * if this method is called many times as the process is expensive + * without batching. This map can also allow SitePinInst objects to be + * reused by {@link #connectNet(Design, Map, Map)}. + */ + public static void disconnectNet(Design design, + List pins, + Map> deferredRemovals) { + for (EDIFHierPortInst ehpi : pins) { + EDIFHierNet ehn = ehpi.getHierarchicalNet(); + List leafPortInsts; + EDIFHierNet internalEhn = ehpi.getInternalNet(); + if (internalEhn == null) { + // Pin does not have an internal net (the net on the other side + // of the port, inside the cell) + EDIFCell ec = ehpi.getCellType(); + if (ec.isLeafCellOrBlackBox()) { + // Pin is a leaf cell + if (ehpi.isInput()) { + // Input leaf port, meaning that this is the only pin affected + leafPortInsts = Collections.singletonList(ehpi); + } else { + // Output leaf port, thus all sinks are affected + assert (ehpi.isOutput()); + leafPortInsts = ehn.getLeafHierPortInsts(true); + } + } else { + // Pin must be unconnected (thus cannot have any leaf ports) + leafPortInsts = Collections.emptyList(); + } + } else { + // Not a leaf pin + + // Check downstream + Set visitedNets = new HashSet<>(); + visitedNets.add(ehn); + + leafPortInsts = internalEhn.getLeafHierPortInsts(true, visitedNets); + + if (ehpi.isInput()) { + // Pin is an input, cannot contain a source, remove all downstream sinks + } else { + // Pin is an output or inout, check if downstream contains a source + boolean sourcePresent = false; + for (EDIFHierPortInst leafPortInst : leafPortInsts) { + if (leafPortInst.isOutput()) { + sourcePresent = true; + break; + } + } + + if (sourcePresent) { + // Downstream does contains a source, remove everything upstream instead + visitedNets.clear(); + visitedNets.add(internalEhn); + leafPortInsts = ehn.getLeafHierPortInsts(false, visitedNets); + } else { + // Downstream contains only sinks, remove them all + } + } + } + + // Remove all affected SitePinInsts + for (EDIFHierPortInst leafEhpi : leafPortInsts) { + // Do nothing if we're disconnecting an input, but this leaf pin is not an input + if (ehpi.isInput() && !leafEhpi.isInput()) + continue; + + Cell cell = leafEhpi.getPhysicalCell(design); + for (SitePinInst spi : cell.getAllSitePinsFromLogicalPin(leafEhpi.getPortInst().getName(), null)) { + DesignTools.handlePinRemovals(spi, deferredRemovals); + } + } + + EDIFNet en = ehn.getNet(); + // Detach from net, but do not detach from cell instance since + // typically we would want to connect it to another net + en.removePortInst(ehpi.getPortInst()); + } + } + + /** + * Given a list of String-s with one more space-separated pins, disconnect these pins from + * their current nets. + * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) + * state, and is modelled on Vivado's disconnect_net -pinlist command. + * @param design The design where the pin(s) are instantiated. + * @param pins A list of hierarchical pins for disconnection. + * @param deferredRemovals An optional map that, if passed in non-null will be populated with + * site pins marked for removal. The map allows for persistent tracking + * if this method is called many times as the process is expensive + * without batching. This map can also allow SitePinInst objects to be + * reused by {@link #connectNet(Design, Map, Map)}. + */ + public static void disconnectNetPath(Design design, + List pins, + Map> deferredRemovals) { + final EDIFNetlist netlist = design.getNetlist(); + List pinObjects = new ArrayList<>(pins.size()); + for (String entry : pins) { + for (String pin : entry.split(" ")) { + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName(pin); + if (ehpi == null) { + int pos = pin.lastIndexOf(EDIFTools.EDIF_HIER_SEP); + String path = pin.substring(0, pos); + EDIFHierCellInst ehci = netlist.getHierCellInstFromName(path); + if (ehci == null) { + throw new RuntimeException("ERROR: Unable to find inst '" + path + "' corresponding to pin '" + pin + "'"); + } + String name = pin.substring(pos + 1); + EDIFCell cell = ehci.getCellType(); + EDIFPort ep = cell.getPort(name); + if (ep == null) { + ep = cell.getPort(EDIFTools.getRootBusName(name, false)); + } + if (ep == null) { + throw new RuntimeException("ERROR: Unable to find port '" + name + "' on inst '" + path + "' corresponding to pin '" + pin + "'"); + } + + // Cell inst exists, as does the port on its cell type, but port inst + // does not because it is not connected to anything. Nothing to be done. + continue; + } + + EDIFNet en = ehpi.getNet(); + if (en == null) { + System.err.println("WARNING: Pin '" + ehpi + "' is not connected to a net."); + throw new RuntimeException(pin); + } + + pinObjects.add(ehpi); + } + } + disconnectNet(design, pinObjects, deferredRemovals); + } + + /** + * Given a map of EDIFHierNet to list of EDIFHierPortInst object(s), connect the latter pins to + * the former net. + * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) + * state, and is modelled on Vivado's connect_net -hier -net_object_list command. + * @param design The design where the net(s) and pin(s) are instantiated. + * @param netToPortInsts A map of hierarchical nets and pins for connection. + * @param deferredRemovals An optional map that, if passed in non-null will allow any SitePinInst + * objects deferred previously for removal to be reused for new connections. + * See {@link #disconnectNet(Design, List, Map)}. + */ + public static void connectNet(Design design, + Map> netToPortInsts, + Map> deferredRemovals) { + final Map netToSourcePortInst = new HashMap<>(); + netToPortInsts.entrySet().removeIf((e) -> { + List portInsts = e.getValue(); + portInsts.removeIf((ehpi) -> { + if (!ehpi.isOutput()) { + return false; + } + EDIFHierPortInst oldValue = netToSourcePortInst.put(e.getKey(), ehpi); + assert(oldValue == null); + return true; + }); + // Keep an entry inside netToPortInsts, even if portInsts is empty, so that + // the physical netlist can still be updated + return false; + }); + + if (!netToSourcePortInst.isEmpty()) { + connectNetSource(design, netToSourcePortInst, deferredRemovals); + } + + // Modify the logical netlist + for (Map.Entry> e : netToPortInsts.entrySet()) { + EDIFHierNet ehn = e.getKey(); + EDIFNet en = ehn.getNet(); + List portInsts = e.getValue(); + + for (EDIFHierPortInst ehpi : portInsts) { + if (ehpi.isOutput()) { + for (EDIFHierPortInst src : ehn.getLeafHierPortInsts(true, false)) { + System.err.println("WARNING: Net '" + ehn.getHierarchicalNetName() + "' already has an output pin '" + + src + "'. Replacing with new pin '" + ehpi + "'."); + Cell cell = src.getPhysicalCell(design); + for (SitePinInst spi : cell.getAllSitePinsFromLogicalPin(src.getPortInst().getName(), null)) { + DesignTools.handlePinRemovals(spi, deferredRemovals); + } + src.getNet().removePortInst(src.getPortInst()); + } + } + if (ehn.getHierarchicalInst().equals(ehpi.getHierarchicalInst())) { + // Attach if port inst not attached by above + EDIFPortInst epi = ehpi.getPortInst(); + if (!en.getPortInsts().contains(epi)) { + en.addPortInst(epi); + } + } else { + // Use a unique baseName derived from the net name, to avoid + // name collisions with bus nets -- e.g. when baseName is 'foo' + // it may be possible that some cell in the hierarchy may contain + // a bus net 'foo' -- represented only by a collection of + // possibly-arbitrarily-indexed single-bit nets: 'foo[i]', 'foo[j]', + // 'foo[k]', etc. making it impractical to identify if a bus net exists. + String baseName = ehn.getNet().getName() + EDIFTools.getUniqueSuffix(); + EDIFTools.connectPortInstsThruHier(ehn, ehpi, baseName); + } + } + } + + final EDIFNetlist netlist = design.getNetlist(); + netlist.resetParentNetMap(); + + // Modify the physical netlist + EDIFCell ecGnd = netlist.getHDIPrimitive(Unisim.GND); + EDIFCell ecVcc = netlist.getHDIPrimitive(Unisim.VCC); + for (EDIFHierNet ehn : netToPortInsts.keySet()) { + Net newPhysNet = null; + + // Find the one and only source pin + List leafEdifPins = ehn.getLeafHierPortInsts(true); + EDIFHierPortInst sourceEhpi = null; + SiteInst sourceSi = null; + BELPin sourceBELPin = null; + for (EDIFHierPortInst ehpi : leafEdifPins) { + if (!ehpi.isOutput()) { + continue; + } + + if (sourceEhpi != null) { + throw new RuntimeException("ERROR: More than one source pin found on net '" + ehn.getHierarchicalNetName() + "'."); + } + sourceEhpi = ehpi; + Cell sourceCell = sourceEhpi.getPhysicalCell(design); + if (sourceCell == null) { + EDIFCell eci = ehpi.getCellType(); + if (eci.equals(ecGnd)) { + newPhysNet = design.getGndNet(); + } else if (eci.equals(ecVcc)) { + newPhysNet = design.getVccNet(); + } else { + throw new RuntimeException("ERROR: Cell corresponding to pin '" + sourceEhpi + "' not found."); + } + + // (Check that these have been handled by connectNetSource()) + Net oldPhysNet = design.getNet(ehn.getHierarchicalNetName()); + if (oldPhysNet != null) { + assert(oldPhysNet.isStaticNet()); + assert(oldPhysNet.getSource() == null); + assert(oldPhysNet.getAlternateSource() == null); + } + } else { + sourceSi = sourceCell.getSiteInst(); + sourceBELPin = sourceCell.getBELPin(sourceEhpi); + + EDIFHierNet parentEhn = sourceEhpi.getHierarchicalNet(); + newPhysNet = design.getNet(parentEhn.getHierarchicalNetName()); + if (newPhysNet == null) { + newPhysNet = design.createNet(parentEhn.getHierarchicalNetName()); + } + } + } + + for (EDIFHierPortInst ehpi : leafEdifPins) { + if (ehpi.isOutput()) { + continue; + } + + Cell cell = ehpi.getPhysicalCell(design); + if (cell == null) { + throw new RuntimeException("ERROR: Cell corresponding to pin '" + ehpi + "' not found."); + } + List sitePins = cell.getAllSitePinsFromLogicalPin(ehpi.getPortInst().getName(), null); + SiteInst si = cell.getSiteInst(); + if (!sitePins.isEmpty()) { + // This net's leaf pins already has some site pins + // Find those site pins and move them onto the new net + String logicalPinName = ehpi.getPortInst().getName(); + BEL bel = cell.getBEL(); + for (String physicalPinName : cell.getAllPhysicalPinMappings(logicalPinName)) { + String sitePinName = cell.getCorrespondingSitePinName(logicalPinName, physicalPinName, null); + SitePinInst spi = si.getSitePinInst(sitePinName); + if (spi == null) { + continue; + } + // Check that all port insts serviced by this SPI are on this net + List portInstsOnSpi = DesignTools.getPortInstsFromSitePinInst(spi); + assert(portInstsOnSpi.contains(ehpi)); + EDIFHierNet parentNet = sourceEhpi.getHierarchicalNet(); + for (EDIFHierPortInst otherEhpi : portInstsOnSpi) { + if (otherEhpi.equals(ehpi)) { + continue; + } + // TODO: Use getLeafHierPortInst() to get parent net? + EDIFHierNet otherParentNet = netlist.getParentNet(otherEhpi.getHierarchicalNet()); + if (!otherParentNet.equals(parentNet)) { + String message = "Site pin " + spi.getSitePinName() + " cannot be used " + + "to connect to logical pin '" + ehpi + "' since it is also connected to pin '" + + otherEhpi + "'."; + String warnIfCellInstStartsWith = System.getProperty("rapidwright.ecotools.connectNet.warnIfCellInstStartsWith"); + String cellInstName = (warnIfCellInstStartsWith != null) ? otherEhpi.getPortInst().getCellInst().getName() : null; + if (cellInstName != null && cellInstName.startsWith(warnIfCellInstStartsWith)) { + System.err.println("WARNING: " + message); + } else { + throw new RuntimeException("ERROR: " + message); + } + } + } + + Net oldPhysNet = spi.getNet(); + if (deferredRemovals != null) { + deferredRemovals.computeIfPresent(oldPhysNet, (k, v) -> { + v.remove(spi); + return v.isEmpty() ? null : v; + }); + } + if (!oldPhysNet.equals(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(); + } + + // Re-do intra-site routing and add pin to new net + if (!si.routeIntraSiteNet(newPhysNet, spi.getBELPin(), snkBp)) { + throw new RuntimeException("ERROR: Failed to route intra-site connection " + + spi.getSiteInst().getSiteName() + "/" + spi.getBELPin() + " to " + snkBp + "."); + } + newPhysNet.addPin(spi); + spi.setRouted(false); + } + } + } else { + // If source and sink pin happen to be in the same site, try intra-site routing first + if (si.equals(sourceSi) && si.routeIntraSiteNet(newPhysNet, sourceBELPin, cell.getBELPin(ehpi))) { + // Intra-site routing successful + continue; + } + + // Otherwise create and attach a new site-pin + String logicalPinName = ehpi.getPortInst().getName(); + if (cell.getAllPhysicalPinMappings(logicalPinName) != null) { + createExitSitePinInst(design, ehpi, newPhysNet); + } + } + } + + if (newPhysNet.getSource() == null && !newPhysNet.getPins().isEmpty() && !newPhysNet.isStaticNet()) { + // We have at least one sink pin -- ensure we have a + // source pin to exit the site with + createExitSitePinInst(design, sourceEhpi, newPhysNet); + assert (newPhysNet.getSource() != null); + } + } + } + + private static void connectNetSource(Design design, + Map netToSourcePortInst, + Map> deferredRemovals) { + for (Map.Entry e : netToSourcePortInst.entrySet()) { + EDIFHierPortInst ehpi = e.getValue(); + assert(ehpi.isOutput()); + + EDIFHierNet ehn = e.getKey(); + EDIFNet en = ehn.getNet(); + + // Modify the logical netlist + for (EDIFHierPortInst src : ehn.getLeafHierPortInsts(true, false)) { + System.err.println("WARNING: Net '" + ehn.getHierarchicalNetName() + "' already has an output pin '" + + src + "'. Replacing with new pin '" + ehpi + "'."); + Cell cell = src.getPhysicalCell(design); + for (SitePinInst spi : cell.getAllSitePinsFromLogicalPin(src.getPortInst().getName(), null)) { + deferredRemovals.computeIfAbsent(spi.getNet(), (p) -> new HashSet<>()).add(spi); + } + src.getNet().removePortInst(src.getPortInst()); + } + + if (ehn.getHierarchicalInst().equals(ehpi.getHierarchicalInst())) { + // Connect if not already connected + EDIFPortInst epi = ehpi.getPortInst(); + if (!en.getPortInsts().contains(epi)) { + en.addPortInst(epi); + } + } else { + // Use a unique baseName derived from the net name, to avoid + // name collisions with bus nets -- e.g. when baseName is 'foo' + // it may be possible that some cell in the hierarchy may contain + // a bus net 'foo' -- represented only by a collection of + // possibly-arbitrarily-indexed single-bit nets: 'foo[i]', 'foo[j]', + // 'foo[k]', etc. making it impractical to identify if a bus net exists. + String baseName = ehn.getNet().getName() + EDIFTools.getUniqueSuffix(); + EDIFTools.connectPortInstsThruHier(ehn, ehpi, baseName); + } + } + + // Since we have changed source pins, regenerate the parent net map + final EDIFNetlist netlist = design.getNetlist(); + netlist.resetParentNetMap(); + + // Modify the physical netlist + EDIFCell ecGnd = netlist.getHDIPrimitive(Unisim.GND); + EDIFCell ecVcc = netlist.getHDIPrimitive(Unisim.VCC); + for (Map.Entry e : netToSourcePortInst.entrySet()) { + EDIFHierNet ehn = e.getKey(); + + List leafLogicalPins = ehn.getLeafHierPortInsts(true, false); + if (leafLogicalPins.size() != 1) { + throw new RuntimeException(); + } + EDIFHierPortInst sourceEhpi = leafLogicalPins.get(0); + + Net newPhysNet = null; + Cell sourceCell = sourceEhpi.getPhysicalCell(design); + if (sourceCell == null) { + EDIFCell eci = sourceEhpi.getCellType(); + if (eci.equals(ecGnd)) { + newPhysNet = design.getGndNet(); + } else if (eci.equals(ecVcc)) { + newPhysNet = design.getVccNet(); + } else { + throw new RuntimeException("ERROR: Cell corresponding to pin '" + sourceEhpi + "' not found."); + } + } + + if (newPhysNet == null) { + EDIFHierNet parentEhn = sourceEhpi.getHierarchicalNet(); + newPhysNet = design.getNet(parentEhn.getHierarchicalNetName()); + if (newPhysNet == null) { + newPhysNet = design.createNet(parentEhn.getHierarchicalNetName()); + } + } else if (newPhysNet.isStaticNet()) { + Net oldPhysNet = design.getNet(ehn.getHierarchicalNetName()); + if (oldPhysNet != null) { + // Propagate the net type so that it can be reconciled by + // route_design's makePhysNetNamesConsistent + oldPhysNet.setType(newPhysNet.getType()); + + Net usedNet = design.getNet(Net.USED_NET); + if (usedNet == null) { + usedNet = design.createNet(Net.USED_NET); + } + // Unroute and remove all output pins on this net + oldPhysNet.unroute(); + for (SitePinInst spi : Arrays.asList(oldPhysNet.getAlternateSource(), oldPhysNet.getSource())) { + if (spi == null) { + continue; + } + + BELPin spiBELPin = spi.getBELPin(); + for (EDIFHierPortInst ehpi : DesignTools.getPortInstsFromSitePinInst(spi)) { + Pair p = ehpi.getRoutedBELPin(design); + p.getFirst().unrouteIntraSiteNet(p.getSecond(), spiBELPin); + } + spi.getSiteInst().removePin(spi); + oldPhysNet.removePin(spi); + + // Mark the output pin sitewire with USED_NET to block it from being + // used as a static source + spi.getSiteInst().routeIntraSiteNet(usedNet, spiBELPin, spiBELPin); + } + } + continue; + } + + Cell cell = sourceEhpi.getPhysicalCell(design); + if (cell == null) { + throw new RuntimeException("ERROR: Cell corresponding to pin '" + sourceEhpi + "' not found."); + } + + List sitePins = cell.getAllSitePinsFromLogicalPin(sourceEhpi.getPortInst().getName(), null); + if (!sitePins.isEmpty()) { + // This net's leaf pins already has some site pins + for (SitePinInst spi : sitePins) { + assert(spi.isOutPin()); + + Net oldPhysNet = spi.getNet(); + if (newPhysNet.equals(oldPhysNet)) { + // Site pin already on new net + continue; + } + + 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; + }); + fullyUnrouteSources(oldPhysNet); + } + + 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 + SiteInst si = cell.getSiteInst(); + Pair siteInstBelPin = sourceEhpi.getRoutedBELPin(design); + assert(siteInstBelPin.getFirst() == si); + assert(spi.getSiteInst() == null); + spi.setSiteInst(si); + newPhysNet.addPin(spi); + 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; + }); + fullyUnrouteSources(newPhysNet); + + // Create and add a new SitePinInst to new net, including + // updating any intra-site routing necessary to get out of site + createExitSitePinInst(design, sourceEhpi, newPhysNet); + } + } + } + + /** + * Given a list of String-s containing one net path followed by one or more pin paths + * (separated by spaces) connect the latter pins to the former net. + * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) + * state, and is modelled on Vivado's connect_net -hier -net_object_list command. + * @param design The design where the net(s) and pin(s) are instantiated. + * @param netPinList A list of String-s containing net and pin paths. + * @param deferredRemovals An optional map that, if passed in non-null will allow any SitePinInst + * objects deferred previously for removal to be reused for new connections. + * See {@link #disconnectNet(Design, List, Map)}. + */ + public static void connectNet(Design design, + List netPinList, + Map> deferredRemovals) { + final EDIFNetlist netlist = design.getNetlist(); + final Map> netPortInsts = new HashMap<>(netPinList.size()); + for (String i : netPinList) { + String[] net_pins = i.split(" ", 2); + String net = net_pins[0]; + if (net.isEmpty()) { + System.err.println("WARNING: Empty net specified for connection to pins: " + net_pins[1]); + continue; + } + EDIFHierNet ehn = netlist.getHierNetFromName(net); + if (ehn == null) throw new RuntimeException("ERROR: Net " + net + " not found"); + + String[] pins = net_pins[1].split("[{} ]+"); + List portInsts = netPortInsts.computeIfAbsent(ehn, (n) -> new ArrayList<>(pins.length)); + for (String pin : pins) { + if (pin.isEmpty()) { + continue; + } + + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName(pin); + if (ehpi == null) { + // No pin exists currently; create and attach one to its port + int pos = pin.lastIndexOf(EDIFTools.EDIF_HIER_SEP); + String path = pin.substring(0, pos); + EDIFHierCellInst ehci = netlist.getHierCellInstFromName(path); + if (ehci == null) { + throw new RuntimeException("ERROR: Unable to find inst '" + path + "' corresponding to pin '" + pin + "'"); + } + String name = pin.substring(pos+1); + EDIFCell cell = ehci.getCellType(); + EDIFPort ep = cell.getPort(name); + if (ep == null) { + ep = cell.getPort(EDIFTools.getRootBusName(name, false)); + } + if (ep == null) { + throw new RuntimeException("ERROR: Unable to find port '" + name + "' on inst '" + path + "' corresponding to pin '" + pin + "'"); + } + EDIFPortInst epi; + if (ep.isBus()) { + int portIdx = EDIFTools.getPortIndexFromName(name); + if (ep.isLittleEndian()) { + portIdx = ep.getWidth() - 1 - portIdx; + } + epi = new EDIFPortInst(ep, null, portIdx, ehci.getInst()); + } else { + epi = new EDIFPortInst(ep, null, ehci.getInst()); + } + ehpi = new EDIFHierPortInst(ehci.getParent(), epi); + } + portInsts.add(ehpi); + } + } + connectNet(design, netPortInsts, deferredRemovals); + } + + /** + * Creates a new SitePinInst source for the physical net provided. It will route out an + * internal pin to a site pin output. + * @param design The current design + * @param targetHierSrc The hierarchical source pin + * @param newPhysNet The net to add the new source pin to + * @return The newly created source site pin + */ + private static SitePinInst routeOutSitePinInstSource(Design design, + EDIFHierPortInst targetHierSrc, + Net newPhysNet) { + // This net is internal to a site, need to create a site pin and route out to it + Cell srcCell = design.getCell(targetHierSrc.getFullHierarchicalInstName()); + SiteInst si = srcCell.getSiteInst(); + String sitePinName = null; + // Find the first available site pin (e.g. between ?_O and ?MUX) + List sitePins = srcCell.getAllCorrespondingSitePinNames(targetHierSrc.getPortInst().getName()); + for (String pin : sitePins) { + if (si.getSitePinInst(pin) == null) { + sitePinName = pin; + break; + } + } + if (sitePinName == null) { + BELPin output = srcCell.getBELPin(targetHierSrc); + // Unroute single output path of slice + // Identify case (O5 -> MUX output must be blocked by O6, reroute O6 to _O pin, route O5 out MUX output) + if (output.getName().equals("O5") && sitePins.isEmpty()) { + char lutID = output.getBELName().charAt(0); + + // Remove OUTMUX SitePIP from O6 + String rBELName = "OUTMUX" + lutID; + SitePIP sitePIP = si.getUsedSitePIP(rBELName); + assert(sitePIP.getInputPinName().equals("D6")); + // TODO: Use DesignTools.unrouteAlternativeOutputSitePin() instead + si.unrouteIntraSiteNet(sitePIP.getInputPin(), sitePIP.getOutputPin()); + + // Move source from *MUX -> *_O + sitePinName = lutID + "MUX"; + SitePinInst lut6MuxOutput = si.getSitePinInst(sitePinName); + Net lut6Net = null; + if (lut6MuxOutput != null) { + lut6Net = lut6MuxOutput.getNet(); + lut6Net.removePin(lut6MuxOutput, true); + si.removePin(lut6MuxOutput); + } else { + // OUTMUX is configured to be D6 but no SPI exists; ignore + } + String mainPinName = lutID + "_O"; + SitePinInst lut6MainOutput = si.getSitePinInst(mainPinName); + if (lut6MainOutput != null) { + if (lut6Net == null) { + lut6Net = lut6MainOutput.getNet(); + } else { + // lut6Net already using the _O output + assert(lut6MainOutput.getNet().equals(lut6Net)); + } + + // Since unrouteIntraSiteNet() above removed the nets on the ?_O (sitePIP + // input) and ?MUX (sitePIP output) sitewires, restore the former here + si.routeIntraSiteNet(lut6Net, sitePIP.getInputPin(), sitePIP.getInputPin()); + } else { + lut6Net.createPin(mainPinName, si); + } + + // Reconfigure OUTMUX SitePIP to use O5 + sitePIP = si.getSitePIP(rBELName, "D5"); + si.routeIntraSiteNet(newPhysNet, sitePIP.getInputPin(), sitePIP.getOutputPin()); + }else { + System.err.println("ERROR: Unable to exit site for target src: " + + targetHierSrc + " " + output + " -> " + sitePins); + return null; + } + } + SitePinInst srcPin = newPhysNet.createPin(sitePinName, si); + BELPin belPinSrc = srcCell.getBELPin(targetHierSrc); + if (!si.routeIntraSiteNet(newPhysNet, belPinSrc, srcPin.getBELPin())) { + System.err.println("ERROR: Failed to route to site pin from target src: " + + targetHierSrc); + } + return srcPin; + } + + /** + * Creates a new SitePinInst source or sink for the physical net provided. It will route out an + * EDIFHierPortInst input/output pin to a corresponding SitePinInst. + * @param design The current design. + * @param ehpi The hierarchical pin. + * @param net The net to add the new SitePinInst to. + * @return The newly created SitePinInst. + */ + public static SitePinInst createExitSitePinInst(Design design, + EDIFHierPortInst ehpi, + Net net) { + if (ehpi.isOutput()) { + return routeOutSitePinInstSource(design, ehpi, net); + } + + Cell cell = ehpi.getPhysicalCell(design); + BELPin cellBp = cell.getBELPin(ehpi); + SiteInst si = cell.getSiteInst(); + String logicalPinName = ehpi.getPortInst().getName(); + List siteWires = new ArrayList<>(); + final boolean considerLutRoutethru = true; + List sitePinNames = cell.getAllCorrespondingSitePinNames(logicalPinName, siteWires, considerLutRoutethru); + if (sitePinNames.isEmpty()) { + // Following existing intra-site routing did not get us to a site pin + // (e.g. previous driver of this BELPin could be a LUT) + assert(!siteWires.isEmpty()); + + // Unroute the first SitePIP + for (String sitewire : siteWires) { + BELPin[] belPins = si.getSiteWirePins(sitewire); + BELPin srcBp = belPins[0].getSourcePin(); + BEL srcBEL = srcBp.getBEL(); + if (srcBEL.getBELClass() == BELClass.RBEL) { + SitePIP spip = si.getUsedSitePIP(srcBp); + if (spip == null) { + continue; + } + + BELPin inputBp = spip.getInputPin(); + + // Save the net on inputBp's sitewire, since + // SiteInst.unrouteIntraSiteNet() will rip it up + Net inputBpNet = si.getNetFromSiteWire(inputBp.getSiteWireName()); + + // Unroute SitePIP + if (!si.unrouteIntraSiteNet(inputBp, cellBp)) { + throw new RuntimeException("ERROR: Failed to unroute intra-site connection " + + si.getSiteName() + "/" + inputBp + " to " + cellBp); + } + + // Restore inputBp's sitewire + if (inputBpNet != null) { + si.routeIntraSiteNet(inputBpNet, inputBp, inputBp); + } + + // Try again + sitePinNames = cell.getAllCorrespondingSitePinNames(logicalPinName, siteWires, considerLutRoutethru); + break; + } + } + } + + SitePinInst spi = null; + for (String sitePinName : sitePinNames) { + Net siteWireNet = si.getNetFromSiteWire(sitePinName); + if (siteWireNet == null || DesignTools.isNetDrivenByHierPort(siteWireNet)) { + // Site Pin not currently used + spi = net.createPin(sitePinName, si); + break; + } + } + + if (spi == null) { + throw new RuntimeException("ERROR: Unable to route pin '" + ehpi + "' out of site " + si.getSiteName() + "."); + } + + BELPin snkBp = cell.getBELPin(ehpi); + if (!si.unrouteIntraSiteNet(spi.getBELPin(), snkBp)) { + throw new RuntimeException("ERROR: Failed to unroute intra-site connection " + + spi.getSiteInst().getSiteName() + "/" + spi.getBELPin() + " to " + snkBp + "."); + } + if (!si.routeIntraSiteNet(net, spi.getBELPin(), snkBp)) { + throw new RuntimeException("ERROR: Failed to route intra-site connection " + + spi.getSiteInst().getSiteName() + "/" + spi.getBELPin() + " to " + snkBp + "."); + } + + return spi; + } + + // Unroute both primary and alternate site pin sources on a net, should they exist, + // and remove those pins from their SiteInst too. + // Also rip up associated intra-site routing. + private static void fullyUnrouteSources(Net net) { + for (SitePinInst spi : Arrays.asList(net.getSource(), net.getAlternateSource())) { + if (spi == null) continue; + BELPin srcBp = DesignTools.getLogicalBELPinDriver(spi); + SiteInst si = spi.getSiteInst(); + si.unrouteIntraSiteNet(srcBp, spi.getBELPin()); + net.removePin(spi); + si.removePin(spi); + spi.setSiteInst(null); + } + } + + /** + * Given a list of EDIFHierCellInst objects, remove these cell instances from the design. + * This method removes and disconnects cells from the EDIF (logical) netlist + * as well as the place-and-route (physical) state, and is modelled on Vivado's + * remove_cell command. + * @param design The current design. + * @param insts A list of hierarchical cell instances for removal. + * @param deferredRemovals An optional map that, if passed in non-null will be populated with + * site pins marked for removal. The map allows for persistent tracking + * if this method is called many times as the process is expensive + * without batching. + */ + public static void removeCell(Design design, + List insts, + Map> deferredRemovals) { + final EDIFNetlist netlist = design.getNetlist(); + for (EDIFHierCellInst ehci : insts) { + // Disconnect hierarchical cell from connected nets + EDIFCellInst eci = ehci.getInst(); + for (EDIFPortInst ehpi : eci.getPortInsts()) { + EDIFNet en = ehpi.getNet(); + if (en != null) { + en.removePortInst(ehpi); + } + } + + // Remove all leaf cells from physical design + for (EDIFHierCellInst leafEhci : netlist.getAllLeafDescendants(ehci)) { + String cellName = leafEhci.getCellName(); + if (cellName.equals("GND") || cellName.equals("VCC")) { + continue; + } + Cell physCell = design.getCell(leafEhci.getFullHierarchicalInstName()); + if (physCell == null) { + throw new RuntimeException("ERROR: Cannot find physical cell corresponding to logical cell '" + + leafEhci.getFullHierarchicalInstName() + "'."); + } + + DesignTools.fullyUnplaceCell(physCell, deferredRemovals); + design.removeCell(physCell); + } + } + + for (EDIFHierCellInst ehci : insts) { + // Remove cell instance from parent cell + EDIFCell parentCell = ehci.getParent().getCellType(); + parentCell.removeCellInst(ehci.getInst()); + } + } + + /** + * Given a list of String-s containing the hierarchical path to cell instances, + * remove these instances from the design. + * This method removes and disconnects cells from the EDIF (logical) netlist + * as well as the place-and-route (physical) state, and is modelled on Vivado's + * remove_cell command. + * @param design The current design. + * @param paths A list of instance paths for removal. + * @param deferredRemovals An optional map that, if passed in non-null will be populated with + * site pins marked for removal. The map allows for persistent tracking + * if this method is called many times as the process is expensive + * without batching. + */ + public static void removeCellPath(Design design, + List paths, + Map> deferredRemovals) { + final EDIFNetlist netlist = design.getNetlist(); + List edifCellInsts = new ArrayList<>(paths.size()); + for (String instName : paths) { + EDIFHierCellInst ehci = netlist.getHierCellInstFromName(instName); + if (ehci == null) { + throw new RuntimeException("ERROR: Cannot find cell '" + instName + '"'); + } + edifCellInsts.add(ehci); + } + removeCell(design, edifCellInsts, deferredRemovals); + } + + private static Pair getParentCellInstAndName(EDIFNetlist netlist, String path) + { + int pos = path.lastIndexOf(EDIFTools.EDIF_HIER_SEP); + String name = path.substring(pos+1); + EDIFHierCellInst parentEhci; + if (pos == -1) { + parentEhci = netlist.getTopHierCellInst(); + } else { + String parentPath = path.substring(0, pos); + parentEhci = netlist.getHierCellInstFromName(parentPath); + } + if (parentEhci == null) { + throw new RuntimeException("ERROR: Cannot find parent cell in path '" + path + "'."); + } + return new Pair<>(parentEhci, name); + } + + /** + * Given a EDIFCell object and a list of instance paths, create these cell instantiations + * in the design. + * This method inserts cells in the EDIF (logical) netlist as well as corresponding leaf cells + * into the physical state (unplaced), and is modelled on Vivado's create_cell command. + * @param design The current design. + * @param reference The cell to be instantiated. + * @param paths A list of instance paths for creation. + */ + public static void createCell(Design design, + EDIFCell reference, + List paths) + { + final EDIFNetlist netlist = design.getNetlist(); + for (String path : paths) { + // Modify logical netlist + Pair p = getParentCellInstAndName(netlist, path); + EDIFHierCellInst parentEhci = p.getFirst(); + EDIFCell parentCell = parentEhci.getCellType(); + String cellName = p.getSecond(); + EDIFCellInst eci = parentCell.createChildCellInst(cellName, reference); + + // Modify physical netlist + EDIFHierCellInst ehci = parentEhci.getChild(eci); + for (EDIFHierCellInst leaf : netlist.getAllLeafDescendants(ehci)) { + String leafCellName = leaf.getCellName(); + if (leafCellName.equals("VCC") || leafCellName.equals("GND")) { + continue; + } + design.addCell(new Cell(leaf.getFullHierarchicalInstName())); + } + } + } + + /** + * 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 + * into the physical state (unplaced), and is modelled on Vivado's create_net command. + * @param design The current design. + * @param paths A list of net paths for creation. + */ + public static void createNet(Design design, + List paths) + { + final EDIFNetlist netlist = design.getNetlist(); + for (String path : paths) { + // Modify logical netlist + Pair p = getParentCellInstAndName(netlist, path); + EDIFHierCellInst parentEhci = p.getFirst(); + EDIFCell parentCell = parentEhci.getCellType(); + String netName = p.getSecond(); + parentCell.createNet(netName); + + // Modify physical netlist + assert(design.getNet(path) == null); + design.createNet(path); + } + } +} diff --git a/src/com/xilinx/rapidwright/router/RouteThruHelper.java b/src/com/xilinx/rapidwright/router/RouteThruHelper.java index f9d66c739..b30901072 100644 --- a/src/com/xilinx/rapidwright/router/RouteThruHelper.java +++ b/src/com/xilinx/rapidwright/router/RouteThruHelper.java @@ -177,6 +177,25 @@ public static boolean isRouteThruPIPAvailable(Design design, Wire start, Wire en return true; } + public static boolean isRouteThruPIPAvailable(Design design, Node start, Node end) { + SitePin outPin = end.getSitePin(); + if (outPin == null) return false; + SiteInst siteInst = design.getSiteInstFromSite(outPin.getSite()); + if (siteInst == null) return true; + Net outputNetCollision = siteInst.getNetFromSiteWire(outPin.getBELPin().getSiteWireName()); + if (outputNetCollision != null) return false; + SitePin inPin = start.getSitePin(); + BELPin belPin = inPin.getBELPin(); + Net inputNetCollision = siteInst.getNetFromSiteWire(belPin.getSiteWireName()); + if (inputNetCollision != null) return false; + + for (BELPin sink : belPin.getSiteConns()) { + Cell collision = siteInst.getCell(sink.getBEL()); + if (collision != null) return false; + } + return true; + } + public static void main(String[] args) { RouteThruHelper rtHelper = new RouteThruHelper(Device.getDevice(Device.AWS_F1)); diff --git a/test/src/com/xilinx/rapidwright/eco/TestECOTools.java b/test/src/com/xilinx/rapidwright/eco/TestECOTools.java new file mode 100644 index 000000000..6cf932b49 --- /dev/null +++ b/test/src/com/xilinx/rapidwright/eco/TestECOTools.java @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2023, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Author: Eddie Hung, Advanced Micro Devices, Inc. + * + * 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.eco; + +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.SitePinInst; +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.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.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class TestECOTools { + @Test + public void testDisconnectNet() { + Design design = RapidWrightDCP.loadDCP("picoblaze_ooc_X10Y235.dcp"); + EDIFNetlist netlist = design.getNetlist(); + Map> deferredRemovals = new HashMap<>(); + + // *** Internally routed net (input pin) + { + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName("processor/parity_muxcy_CARRY4_CARRY8/S[1]"); + EDIFPortInst epi = ehpi.getPortInst(); + EDIFNet en = ehpi.getHierarchicalNet().getNet(); + int portInstsBefore = en.getPortInsts().size(); + Assertions.assertTrue(en.getPortInsts().contains(epi)); + + ECOTools.disconnectNet(design, Collections.singletonList(ehpi), deferredRemovals); + Assertions.assertFalse(en.getPortInsts().contains(epi)); + Assertions.assertEquals(portInstsBefore - 1, en.getPortInsts().size()); + + Assertions.assertEquals(0, deferredRemovals.size()); + } + deferredRemovals.clear(); + + + // *** Internally routed net (output pin) + { + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName("processor/read_strobe_lut/LUT6/O"); + EDIFNet en = ehpi.getHierarchicalNet().getNet(); + int portInstsBefore = en.getPortInsts().size(); + Assertions.assertTrue(en.getPortInsts().contains(ehpi.getPortInst())); + + ECOTools.disconnectNet(design, Collections.singletonList(ehpi), deferredRemovals); + Assertions.assertFalse(en.getPortInsts().contains(ehpi.getPortInst())); + Assertions.assertEquals(portInstsBefore - 1, en.getPortInsts().size()); + + Assertions.assertEquals(0, deferredRemovals.size()); + } + deferredRemovals.clear(); + + // *** Externally routed 2-pin net (input pin) + { + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName("processor/t_state1_flop/D"); + Net net = design.getNet(netlist.getParentNetName(ehpi.getHierarchicalNetName())); + EDIFNet en = ehpi.getHierarchicalNet().getNet(); + int portInstsBefore = en.getPortInsts().size(); + Assertions.assertTrue(en.getPortInsts().contains(ehpi.getPortInst())); + + ECOTools.disconnectNet(design, Collections.singletonList(ehpi), deferredRemovals); + Assertions.assertFalse(en.getPortInsts().contains(ehpi.getPortInst())); + Assertions.assertEquals(portInstsBefore - 1, en.getPortInsts().size()); + + Assertions.assertEquals("[IN SLICE_X13Y237.E_I]", deferredRemovals.get(net).toString()); + } + deferredRemovals.clear(); + + // *** Externally routed 2-pin net (output pin) + { + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName("your_program/ram_4096x8/DOUTBDOUT[3]"); + Net net = design.getNet(netlist.getParentNetName(ehpi.getHierarchicalNetName())); + EDIFNet en = ehpi.getHierarchicalNet().getNet(); + int portInstsBefore = en.getPortInsts().size(); + Assertions.assertTrue(en.getPortInsts().contains(ehpi.getPortInst())); + + ECOTools.disconnectNet(design, Collections.singletonList(ehpi), deferredRemovals); + Assertions.assertFalse(en.getPortInsts().contains(ehpi.getPortInst())); + Assertions.assertEquals(portInstsBefore - 1, en.getPortInsts().size()); + + Assertions.assertEquals("[IN RAMB36_X1Y47.DIBU1, OUT RAMB36_X1Y47.DOBU1]", + deferredRemovals.get(net).stream().map(Object::toString).sorted().collect(Collectors.toList()).toString()); + } + deferredRemovals.clear(); + + // *** Externally routed many-pin net (input pin) + { + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName("processor/stack_loop[4].upper_stack.stack_pointer_lut/I0"); + Net net = design.getNet(netlist.getParentNetName(ehpi.getHierarchicalNetName())); + EDIFNet en = ehpi.getHierarchicalNet().getNet(); + int portInstsBefore = en.getPortInsts().size(); + Assertions.assertTrue(en.getPortInsts().contains(ehpi.getPortInst())); + + ECOTools.disconnectNet(design, Collections.singletonList(ehpi), deferredRemovals); + Assertions.assertFalse(en.getPortInsts().contains(ehpi.getPortInst())); + Assertions.assertEquals(portInstsBefore - 1, en.getPortInsts().size()); + + Assertions.assertEquals("[IN SLICE_X13Y238.E1]", deferredRemovals.get(net).toString()); + } + deferredRemovals.clear(); + + // *** Externally routed many-pin net (output pin) + { + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName("processor/alu_mux_sel0_flop/Q"); + Net net = design.getNet(netlist.getParentNetName(ehpi.getHierarchicalNetName())); + EDIFNet en = ehpi.getHierarchicalNet().getNet(); + int portInstsBefore = en.getPortInsts().size(); + Assertions.assertTrue(en.getPortInsts().contains(ehpi.getPortInst())); + + ECOTools.disconnectNet(design, Collections.singletonList(ehpi), deferredRemovals); + Assertions.assertFalse(en.getPortInsts().contains(ehpi.getPortInst())); + Assertions.assertEquals(portInstsBefore - 1, en.getPortInsts().size()); + + Assertions.assertEquals("[IN SLICE_X15Y235.G6, IN SLICE_X15Y235.H2, IN SLICE_X15Y237.G5, IN SLICE_X15Y239.H5, IN SLICE_X16Y235.F6, IN SLICE_X16Y235.G4, IN SLICE_X16Y238.D4, IN SLICE_X16Y239.B6, OUT SLICE_X16Y239.EQ]", + deferredRemovals.get(net).stream().map(Object::toString).sorted().collect(Collectors.toList()).toString()); + } + deferredRemovals.clear(); + + // *** Externally routed global net (input pin) + { + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName("processor/address_loop[10].output_data.pc_vector_mux_lut/I0"); + Net net = design.getGndNet(); + EDIFNet en = ehpi.getHierarchicalNet().getNet(); + int portInstsBefore = en.getPortInsts().size(); + Assertions.assertTrue(en.getPortInsts().contains(ehpi.getPortInst())); + + ECOTools.disconnectNet(design, Collections.singletonList(ehpi), deferredRemovals); + Assertions.assertFalse(en.getPortInsts().contains(ehpi.getPortInst())); + Assertions.assertEquals(portInstsBefore - 1, en.getPortInsts().size()); + + Assertions.assertEquals("[IN SLICE_X13Y237.G1]", deferredRemovals.get(net).toString()); + } + deferredRemovals.clear(); + } + + @Test + public void testConnectNetSwapSinks() { + Design design = RapidWrightDCP.loadDCP("microblazeAndILA_3pblocks.dcp"); + EDIFNetlist netlist = design.getNetlist(); + Map> deferredRemovals = new HashMap<>(); + + DesignTools.updatePinsIsRouted(design); + + // Disconnect the ILA inputs + List disconnectPins = new ArrayList<>(); + for (int i = 0; i < 14; i++) { + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName("u_ila_0/probe0[" + i + "]"); + Assertions.assertNotNull(ehpi); + Assertions.assertTrue(ehpi.isInput()); + disconnectPins.add(ehpi); + } + ECOTools.disconnectNet(design, disconnectPins, deferredRemovals); + Assertions.assertEquals(14, deferredRemovals.size()); + + // Re-connect those inputs to some other nets + final Map> netToPortInsts = new HashMap<>(); + for (int i = 0; i < 14; i++) { + int busIdx = (74 + i); + EDIFHierNet ehn = netlist.getHierNetFromName("base_mb_i/microblaze_0/U0/MicroBlaze_Core_I/Performance.Core/Data_Flow_I/Data_Addr[0][" + busIdx + "]"); + EDIFHierPortInst ehpi = disconnectPins.get(i); + + // Check that leaves of net and pin are disjoint + List ehpiLeaves = ehpi.getInternalNet().getLeafHierPortInsts(false, true); + Assertions.assertFalse(ehn.getLeafHierPortInsts(false, true).stream().anyMatch(ehpiLeaves::contains)); + + netToPortInsts.put(ehn, new ArrayList(){{ add(ehpi); }}); + } + ECOTools.connectNet(design, netToPortInsts, deferredRemovals); + Assertions.assertEquals(0, deferredRemovals.size()); + + // Check that leaves of net and pin are one and the same now + List unroutedPins = new ArrayList<>(); + for (Map.Entry> e : netToPortInsts.entrySet()) { + EDIFHierNet ehn = e.getKey(); + List ehnLeaves = ehn.getLeafHierPortInsts(false, true); + for (EDIFHierPortInst ehpi : e.getValue()) { + List ehpiLeaves = ehpi.getInternalNet().getLeafHierPortInsts(false, true); + Assertions.assertEquals(ehnLeaves.size(), ehpiLeaves.size()); + Assertions.assertTrue(ehnLeaves.containsAll(ehpiLeaves)); + } + + EDIFHierNet parentEhn = netlist.getParentNet(ehn); + Net parentNet = design.getNet(parentEhn.getHierarchicalNetName()); + for (SitePinInst spi : parentNet.getPins()) { + if (!spi.isOutPin() && !spi.isRouted()) { + unroutedPins.add(spi); + } + } + } + + Assertions.assertEquals("[IN SLICE_X51Y84.G_I, IN SLICE_X49Y84.EX, IN SLICE_X49Y87.EX, IN SLICE_X51Y84.H_I, IN SLICE_X49Y86.FX, IN SLICE_X49Y86.E_I, IN SLICE_X49Y88.EX, IN SLICE_X50Y82.EX, IN SLICE_X49Y86.EX, IN SLICE_X49Y84.F_I, IN SLICE_X49Y85.EX, IN SLICE_X50Y84.EX, IN SLICE_X49Y84.FX, IN SLICE_X49Y84.E_I]", + unroutedPins.toString()); + + if (FileTools.isVivadoOnPath()) { + // Check that Vivado shows 14 unrouted nets + ReportRouteStatusResult rrs = VivadoTools.reportRouteStatus(design); + Assertions.assertEquals(14, rrs.netsWithRoutingErrors); + Assertions.assertEquals(14, rrs.netsWithSomeUnroutedPins); + } + } + + @Test + public void testConnectNetSwapSource() { + Design design = RapidWrightDCP.loadDCP("picoblaze_ooc_X10Y235.dcp"); + EDIFNetlist netlist = design.getNetlist(); + Map> deferredRemovals = new HashMap<>(); + + DesignTools.updatePinsIsRouted(design); + + // Disconnect the outputs + List disconnectPins = new ArrayList<>(); + List disconnectedNets = new ArrayList<>(); + List> sourceSitePinInsts = new ArrayList<>(); + Map> sinkSitePinInsts = new HashMap<>(); + for (int i = 0; i < 2; i++) { + EDIFHierPortInst ehpi = netlist.getHierPortInstFromName("processor/data_path_loop[" + i + "].alu_mux_lut/O"); + EDIFHierNet ehn = ehpi.getHierarchicalNet(); + disconnectedNets.add(ehn); + Net net = design.getNet(ehn.getHierarchicalNetName()); + Set sourcePins = new HashSet<>(); + Set sinkPins = new HashSet<>(); + for (SitePinInst spi : net.getPins()) { + Assertions.assertTrue(spi.isRouted()); + if (!spi.isOutPin()) { + sinkPins.add(spi); + } else { + sourcePins.add(spi.getSitePinName()); + } + } + sourceSitePinInsts.add(sourcePins); + sinkSitePinInsts.put(net, sinkPins); + + Assertions.assertNotNull(ehpi); + Assertions.assertTrue(ehpi.isOutput()); + disconnectPins.add(ehpi); + } + ECOTools.disconnectNet(design, disconnectPins, deferredRemovals); + Assertions.assertEquals(2, deferredRemovals.size()); + + // Swap those output pins + Map> netToPortInsts = new HashMap<>(); + netToPortInsts.put(disconnectedNets.get(0), new ArrayList() {{ add(disconnectPins.get(1)); }}); + netToPortInsts.put(disconnectedNets.get(1), new ArrayList() {{ add(disconnectPins.get(0)); }}); + + ECOTools.connectNet(design, netToPortInsts, deferredRemovals); + Assertions.assertEquals(0, deferredRemovals.size()); + + List physNets = new ArrayList<>(); + for (EDIFHierNet ehn : disconnectedNets) { + Net net = design.getNet(ehn.getHierarchicalNetName()); + physNets.add(net); + Assertions.assertFalse(net.hasPIPs()); + Set sinkPins = sinkSitePinInsts.get(net); + for (SitePinInst spi : net.getPins()) { + Assertions.assertFalse(spi.isRouted()); + // Check sink SPIs are not swapped + Assertions.assertTrue(spi.isOutPin() || sinkPins.contains(spi)); + } + } + + // Check source SPI is swapped + Assertions.assertTrue(sourceSitePinInsts.get(0).contains(physNets.get(1).getSource().getSitePinName())); + Assertions.assertTrue(sourceSitePinInsts.get(1).contains(physNets.get(0).getSource().getSitePinName())); + + if (FileTools.isVivadoOnPath()) { + ReportRouteStatusResult rrs = VivadoTools.reportRouteStatus(design); + Assertions.assertEquals(0, rrs.netsWithRoutingErrors); + Assertions.assertEquals(2, rrs.unroutedNets); + } + } + + @Test + @Disabled("Currently, ECOTools.removeCell() does not work for hierarchical cells. Specifically, for this testcase " + + "exclusively intra-site routes (e.g. 'processor/data_path_loop[4].small_spm.small_spm_ram.spm_ram/DOA') " + + "are not removed and appear in the DCP causing Vivado to emit 'placement information for XX sites failed to" + + "restore' warnings and cells (e.g. 'your_program/ram_4096x8') to be unplaced.") + public void testRemoveCell() { + Design design = RapidWrightDCP.loadDCP("picoblaze_ooc_X10Y235.dcp"); + EDIFNetlist netlist = design.getNetlist(); + Map> deferredRemovals = new HashMap<>(); + + EDIFHierCellInst ehciToDelete = netlist.getHierCellInstFromName("processor"); + List leavesToDelete = netlist.getAllLeafDescendants(ehciToDelete); + + ECOTools.removeCell(design, Collections.singletonList(ehciToDelete), deferredRemovals); + + for (EDIFHierCellInst ehci : leavesToDelete) { + String instName = ehci.getFullHierarchicalInstName(); + // Logical leaf cell not present + Assertions.assertNull(netlist.getHierCellInstFromName(instName)); + // Physical cell not present + Assertions.assertNull(design.getCell(instName)); + } + + // Logical hierarchical cell not present + Assertions.assertNull(netlist.getHierCellInstFromName(ehciToDelete.getFullHierarchicalInstName())); + + DesignTools.batchRemoveSitePins(deferredRemovals, true); + + design.writeCheckpoint("/group/zircon2/eddieh/pb.dcp"); + + if (FileTools.isVivadoOnPath()) { + ReportRouteStatusResult rrs = VivadoTools.reportRouteStatus(design); + Assertions.assertEquals(0 /* TODO */, rrs.netsWithRoutingErrors); + } + } + + @Test + public void testRemoveCellLeaf() { + Design design = RapidWrightDCP.loadDCP("picoblaze_ooc_X10Y235.dcp"); + EDIFNetlist netlist = design.getNetlist(); + Map> deferredRemovals = new HashMap<>(); + + EDIFHierCellInst ehciToDelete = netlist.getHierCellInstFromName("your_program/ram_4096x8"); + Assertions.assertTrue(ehciToDelete.getCellType().isLeafCellOrBlackBox()); + + ECOTools.removeCell(design, Collections.singletonList(ehciToDelete), deferredRemovals); + + String instName = ehciToDelete.getFullHierarchicalInstName(); + + // Logical leaf cell not present + Assertions.assertNull(netlist.getHierCellInstFromName(instName)); + // Physical cell not present + Assertions.assertNull(design.getCell(instName)); + + DesignTools.batchRemoveSitePins(deferredRemovals, true); + + if (FileTools.isVivadoOnPath()) { + ReportRouteStatusResult rrs = VivadoTools.reportRouteStatus(design); + Assertions.assertEquals(8, rrs.netsWithRoutingErrors); + Assertions.assertEquals(8, rrs.netsWithNoDriver); + Assertions.assertEquals(8, rrs.netsWithSomeUnroutedPins); + } + } + + @Test + public void testCreateCell() { + Design design = RapidWrightDCP.loadDCP("picoblaze_ooc_X10Y235.dcp"); + EDIFNetlist netlist = design.getNetlist(); + + DesignTools.updatePinsIsRouted(design); + + EDIFCell reference = netlist.getCell("kcpsm6"); + List instNames = Arrays.asList("processor2", "processor3"); + ECOTools.createCell(design, reference, instNames); + + List goldenLeaves = netlist.getAllLeafDescendants("processor"); + + for (String instName : instNames) { + // Logical hierarchical cell is present + EDIFHierCellInst ehci = netlist.getHierCellInstFromName(instName); + Assertions.assertNotNull(ehci); + + // Physical leaf cells are present and unplaced + List leaves = netlist.getAllLeafDescendants(ehci); + Assertions.assertEquals(goldenLeaves.size(), leaves.size()); + for (EDIFHierCellInst leaf : leaves) { + String cellName = leaf.getCellName(); + if (cellName.equals("VCC") || cellName.equals("GND")) { + continue; + } + String leafName = leaf.getFullHierarchicalInstName(); + Cell leafCell = design.getCell(leafName); + Assertions.assertNotNull(leafCell); + Assertions.assertFalse(leafCell.isPlaced()); + } + } + + if (FileTools.isVivadoOnPath()) { + ReportRouteStatusResult rrs = VivadoTools.reportRouteStatus(design); + Assertions.assertEquals(1135, rrs.logicalNets); + Assertions.assertEquals(728, rrs.netsWithNoPlacedPins); + Assertions.assertEquals(2, rrs.netsWithRoutingErrors); + Assertions.assertEquals(2, rrs.netsWithSomeUnplacedPins); + } + } + + @Test + public void testCreateNet() { + Design design = RapidWrightDCP.loadDCP("picoblaze_ooc_X10Y235.dcp"); + EDIFNetlist netlist = design.getNetlist(); + + DesignTools.updatePinsIsRouted(design); + + List netNames = Arrays.asList("processor/foo", "your_program/bar"); + ECOTools.createNet(design, netNames); + + for (String netName : netNames) { + // Logical net is present + EDIFHierNet ehn = netlist.getHierNetFromName(netName); + Assertions.assertNotNull(ehn); + + // Physical nets are also present + Assertions.assertNotNull(design.getNet(netName)); + } + } +} From 174cdc9300271f3dcc71295b7969c9579ddc7da3 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Thu, 12 Oct 2023 15:31:56 -0700 Subject: [PATCH 2/6] Remove unrelated RouteThruHelper change Signed-off-by: Eddie Hung --- .../rapidwright/router/RouteThruHelper.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/com/xilinx/rapidwright/router/RouteThruHelper.java b/src/com/xilinx/rapidwright/router/RouteThruHelper.java index b30901072..f9d66c739 100644 --- a/src/com/xilinx/rapidwright/router/RouteThruHelper.java +++ b/src/com/xilinx/rapidwright/router/RouteThruHelper.java @@ -177,25 +177,6 @@ public static boolean isRouteThruPIPAvailable(Design design, Wire start, Wire en return true; } - public static boolean isRouteThruPIPAvailable(Design design, Node start, Node end) { - SitePin outPin = end.getSitePin(); - if (outPin == null) return false; - SiteInst siteInst = design.getSiteInstFromSite(outPin.getSite()); - if (siteInst == null) return true; - Net outputNetCollision = siteInst.getNetFromSiteWire(outPin.getBELPin().getSiteWireName()); - if (outputNetCollision != null) return false; - SitePin inPin = start.getSitePin(); - BELPin belPin = inPin.getBELPin(); - Net inputNetCollision = siteInst.getNetFromSiteWire(belPin.getSiteWireName()); - if (inputNetCollision != null) return false; - - for (BELPin sink : belPin.getSiteConns()) { - Cell collision = siteInst.getCell(sink.getBEL()); - if (collision != null) return false; - } - return true; - } - public static void main(String[] args) { RouteThruHelper rtHelper = new RouteThruHelper(Device.getDevice(Device.AWS_F1)); From 24300aab0c6737b35b16c5480be6b6fb6e305497 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Thu, 12 Oct 2023 15:32:47 -0700 Subject: [PATCH 3/6] Remove unrelated DesignTools change Signed-off-by: Eddie Hung --- .../rapidwright/design/DesignTools.java | 39 ++----------------- 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/src/com/xilinx/rapidwright/design/DesignTools.java b/src/com/xilinx/rapidwright/design/DesignTools.java index 344488532..a7f802546 100644 --- a/src/com/xilinx/rapidwright/design/DesignTools.java +++ b/src/com/xilinx/rapidwright/design/DesignTools.java @@ -1997,11 +1997,11 @@ public static boolean stampPlacement(Design design, Module stamp, Map getConnectedCells(SitePinInst pin) { - HashSet cells = new HashSet<>(); + HashSet cells = new HashSet(); SiteInst si = pin.getSiteInst(); if (si == null) return cells; for (BELPin p : pin.getBELPin().getSiteConns()) { @@ -2028,39 +2028,6 @@ public static Set getConnectedCells(SitePinInst pin) { return cells; } - /** - * Looks in the site instance for BEL pins connected to this site pin. - * @param pin The pin to examine for connected BEL pins - * @return Set of BEL pins to this site pin - */ - public static Set getConnectedBELPins(SitePinInst pin) { - HashSet pins = new HashSet<>(); - SiteInst si = pin.getSiteInst(); - if (si == null) return pins; - for (BELPin p : pin.getBELPin().getSiteConns()) { - if (p.getBEL().getBELClass() == BELClass.RBEL) { - SitePIP pip = si.getUsedSitePIP(p.getBELName()); - if (pip == null) continue; - if (p.isOutput()) { - p = pip.getInputPin().getSiteConns().get(0); - Cell c = si.getCell(p.getBELName()); - if (c != null) pins.add(p); - } else { - for (BELPin snk : pip.getOutputPin().getSiteConns()) { - Cell c = si.getCell(snk.getBELName()); - if (c != null) pins.add(snk); - } - } - } else { - Cell c = si.getCell(p.getBELName()); - if (c != null && c.getLogicalPinMapping(p.getName()) != null) { - pins.add(p); - } - } - } - return pins; - } - /** * Quick and dumb placement of a cell. Does not attempt * any optimization and will not change the placement From 87f951355472acc2f330d97ebacf6fe643b7aad5 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 16 Oct 2023 13:33:33 -0700 Subject: [PATCH 4/6] Add some overloads with default deferredRemovals of null Signed-off-by: Eddie Hung --- src/com/xilinx/rapidwright/eco/ECOTools.java | 38 ++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/com/xilinx/rapidwright/eco/ECOTools.java b/src/com/xilinx/rapidwright/eco/ECOTools.java index f0e26e2bd..95cbcdd86 100644 --- a/src/com/xilinx/rapidwright/eco/ECOTools.java +++ b/src/com/xilinx/rapidwright/eco/ECOTools.java @@ -74,6 +74,18 @@ * modifying 'top/u1(foo1)/lut1' would no longer affect 'top/u2(foo2)/lut1'. */ public class ECOTools { + /** + * Given a list of EDIFHierPortInst objects, disconnect these pins from their current nets. + * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) + * state, and is modelled on Vivado's disconnect_net -pinlist command. + * @param design The design where the pin(s) are instantiated. + * @param pins A list of hierarchical pins for disconnection. + */ + public static void disconnectNet(Design design, + List pins) { + disconnectNet(design, 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) @@ -162,6 +174,19 @@ public static void disconnectNet(Design design, } } + /** + * Given a list of String-s with one more space-separated pins, disconnect these pins from + * their current nets. + * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) + * state, and is modelled on Vivado's disconnect_net -pinlist command. + * @param design The design where the pin(s) are instantiated. + * @param pins A list of hierarchical pins for disconnection. + */ + public static void disconnectNetPath(Design design, + List pins) { + disconnectNetPath(design, pins, null); + } + /** * Given a list of String-s with one more space-separated pins, disconnect these pins from * their current nets. @@ -609,6 +634,19 @@ private static void connectNetSource(Design design, } } + /** + * Given a list of String-s containing one net path followed by one or more pin paths + * (separated by spaces) connect the latter pins to the former net. + * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) + * state, and is modelled on Vivado's connect_net -hier -net_object_list command. + * @param design The design where the net(s) and pin(s) are instantiated. + * @param netPinList A list of String-s containing net and pin paths. + */ + public static void connectNet(Design design, + List netPinList) { + connectNet(design, netPinList, null); + } + /** * Given a list of String-s containing one net path followed by one or more pin paths * (separated by spaces) connect the latter pins to the former net. From 7ed0471ac9fdeaf332bb845af5200b017a758dfa Mon Sep 17 00:00:00 2001 From: eddieh-xlnx Date: Mon, 16 Oct 2023 13:56:34 -0700 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Chris Lavin Signed-off-by: eddieh-xlnx --- src/com/xilinx/rapidwright/eco/ECOTools.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/com/xilinx/rapidwright/eco/ECOTools.java b/src/com/xilinx/rapidwright/eco/ECOTools.java index 95cbcdd86..c956a70df 100644 --- a/src/com/xilinx/rapidwright/eco/ECOTools.java +++ b/src/com/xilinx/rapidwright/eco/ECOTools.java @@ -74,6 +74,7 @@ * modifying 'top/u1(foo1)/lut1' would no longer affect 'top/u2(foo2)/lut1'. */ public class ECOTools { + /** * 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) @@ -175,7 +176,7 @@ public static void disconnectNet(Design design, } /** - * Given a list of String-s with one more space-separated pins, disconnect these pins from + * Given a list of strings with one more space-separated pins, disconnect these pins from * their current nets. * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) * state, and is modelled on Vivado's disconnect_net -pinlist command. @@ -981,8 +982,7 @@ public static void removeCellPath(Design design, removeCell(design, edifCellInsts, deferredRemovals); } - private static Pair getParentCellInstAndName(EDIFNetlist netlist, String path) - { + private static Pair getParentCellInstAndName(EDIFNetlist netlist, String path) { int pos = path.lastIndexOf(EDIFTools.EDIF_HIER_SEP); String name = path.substring(pos+1); EDIFHierCellInst parentEhci; @@ -1007,10 +1007,7 @@ private static Pair getParentCellInstAndName(EDIFNetlis * @param reference The cell to be instantiated. * @param paths A list of instance paths for creation. */ - public static void createCell(Design design, - EDIFCell reference, - List paths) - { + public static void createCell(Design design, EDIFCell reference, List paths) { final EDIFNetlist netlist = design.getNetlist(); for (String path : paths) { // Modify logical netlist @@ -1039,9 +1036,7 @@ public static void createCell(Design design, * @param design The current design. * @param paths A list of net paths for creation. */ - public static void createNet(Design design, - List paths) - { + public static void createNet(Design design, List paths) { final EDIFNetlist netlist = design.getNetlist(); for (String path : paths) { // Modify logical netlist From 8f30841e0bd56c0fa7255a22448b9121d111349f Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 16 Oct 2023 15:10:11 -0700 Subject: [PATCH 6/6] Address review comments Signed-off-by: Eddie Hung --- src/com/xilinx/rapidwright/eco/ECOTools.java | 31 ++++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/com/xilinx/rapidwright/eco/ECOTools.java b/src/com/xilinx/rapidwright/eco/ECOTools.java index c956a70df..f79843dfa 100644 --- a/src/com/xilinx/rapidwright/eco/ECOTools.java +++ b/src/com/xilinx/rapidwright/eco/ECOTools.java @@ -189,7 +189,7 @@ public static void disconnectNetPath(Design design, } /** - * Given a list of String-s with one more space-separated pins, disconnect these pins from + * Given a list of strings with one more space-separated pins, disconnect these pins from * their current nets. * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) * state, and is modelled on Vivado's disconnect_net -pinlist command. @@ -253,6 +253,13 @@ public static void disconnectNetPath(Design design, * @param deferredRemovals An optional map that, if passed in non-null will allow any SitePinInst * objects deferred previously for removal to be reused for new connections. * See {@link #disconnectNet(Design, List, Map)}. + * + * By default, this method will throw a RuntimeException if there is a mismatch between + * the net connected to logical pins (EDIFHierPortInst) and the net connected to its physical pin + * (SitePinInst). The Java property "rapidwright.ecotools.connectNet.warnIfCellInstStartsWith" + * allows such pins with an EDIFCellInst name that start with its value to be demoted from an + * RuntimeException to a printed warning. An example use case for this feature would be if + * it is known that the conflicting logical pin will be removed later. */ public static void connectNet(Design design, Map> netToPortInsts, @@ -520,7 +527,7 @@ private static void connectNetSource(Design design, List leafLogicalPins = ehn.getLeafHierPortInsts(true, false); if (leafLogicalPins.size() != 1) { - throw new RuntimeException(); + throw new RuntimeException("ERROR: Net '" + ehn.getHierarchicalNetName() + "' does not contain exactly one source pin."); } EDIFHierPortInst sourceEhpi = leafLogicalPins.get(0); @@ -636,12 +643,12 @@ private static void connectNetSource(Design design, } /** - * Given a list of String-s containing one net path followed by one or more pin paths - * (separated by spaces) connect the latter pins to the former net. + * 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. * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) * state, and is modelled on Vivado's connect_net -hier -net_object_list command. * @param design The design where the net(s) and pin(s) are instantiated. - * @param netPinList A list of String-s containing net and pin paths. + * @param netPinList A list of strings containing net and pin paths. */ public static void connectNet(Design design, List netPinList) { @@ -649,12 +656,12 @@ public static void connectNet(Design design, } /** - * Given a list of String-s containing one net path followed by one or more pin paths - * (separated by spaces) connect the latter pins to the former net. + * 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. * This method modifies the EDIF (logical) netlist as well as the place-and-route (physical) * state, and is modelled on Vivado's connect_net -hier -net_object_list command. * @param design The design where the net(s) and pin(s) are instantiated. - * @param netPinList A list of String-s containing net and pin paths. + * @param netPinList A list of strings containing net and pin paths. * @param deferredRemovals An optional map that, if passed in non-null will allow any SitePinInst * objects deferred previously for removal to be reused for new connections. * See {@link #disconnectNet(Design, List, Map)}. @@ -728,6 +735,12 @@ public static void connectNet(Design design, private static SitePinInst routeOutSitePinInstSource(Design design, EDIFHierPortInst targetHierSrc, Net newPhysNet) { + if (!targetHierSrc.isOutput()) { + throw new RuntimeException("ERROR: Pin '" + targetHierSrc + "' is not an output pin."); + } + if (!targetHierSrc.getCellType().isPrimitive()) { + throw new RuntimeException("ERROR: Pin '" + targetHierSrc + "' is not an primitive source pin."); + } // This net is internal to a site, need to create a site pin and route out to it Cell srcCell = design.getCell(targetHierSrc.getFullHierarchicalInstName()); SiteInst si = srcCell.getSiteInst(); @@ -955,7 +968,7 @@ public static void removeCell(Design design, } /** - * Given a list of String-s containing the hierarchical path to cell instances, + * Given a list of strings containing the hierarchical path to cell instances, * remove these instances from the design. * This method removes and disconnects cells from the EDIF (logical) netlist * as well as the place-and-route (physical) state, and is modelled on Vivado's