Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
130 changes: 95 additions & 35 deletions src/com/xilinx/rapidwright/router/VersalClockRouting.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@

package com.xilinx.rapidwright.router;

import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
Expand Down Expand Up @@ -51,6 +53,8 @@
import com.xilinx.rapidwright.rwroute.NodeStatus;
import com.xilinx.rapidwright.rwroute.RouterHelper;
import com.xilinx.rapidwright.rwroute.RouterHelper.NodeWithPrev;
import com.xilinx.rapidwright.util.FileTools;
import com.xilinx.rapidwright.util.Pair;
import com.xilinx.rapidwright.util.Utils;

/**
Expand All @@ -65,6 +69,8 @@ public class VersalClockRouting {
private static final EnumSet<IntentCode> vRouteTypes;
private static final EnumSet<IntentCode> allRouteTypes;

private static Map<String, Map<Integer, VersalClockTree>> vdistrTrees;

static {
hRouteTypes = EnumSet.of(
IntentCode.NODE_GLOBAL_HROUTE,
Expand Down Expand Up @@ -262,11 +268,10 @@ public static Map<ClockRegion, Node> routeVrouteToVerticalDistributionLines(Net
IntentCode.NODE_GLOBAL_VDISTR_SHARED,
IntentCode.NODE_GLOBAL_GCLK);

Set<NodeWithPrevAndCost> startingPoints = new HashSet<>();
// The VROUTE node is the precursor to the clock root, technically the first
// VDISTR node is the center point. If we have more than one VROUTE->VDISTR
// transition we end up with multiple clock roots
startingPoints.add(new NodeWithPrevAndCost(vroute));
NodeWithPrevAndCost clockRootNode = new NodeWithPrevAndCost(vroute);

// Identify top and bottom clock region spine targets
int minY = Integer.MAX_VALUE;
Expand All @@ -282,46 +287,54 @@ public static Map<ClockRegion, Node> routeVrouteToVerticalDistributionLines(Net
verticalSpineCRs.add(device.getClockRegion(i, x));
}

nextClockRegion: for (ClockRegion cr : verticalSpineCRs) {
VersalClockTree clkTree = getVersalClockTree(device, minY, maxY);
if (clkTree == null) {
System.err.println("ERROR: No clock tree found for " + device + " Y" + minY + "-Y" + maxY
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the plan (in a future PR, prior to final 2025.2.0 release) to support more devices than just the V80 that the data file in this PR supports? If so, should this be turned into a Exception (after which any code to do suboptimal vertical routing can also be removed?)

+ " while routing clock " + clk + ", skew will be suboptimal.");
}

for (ClockRegion cr : verticalSpineCRs) {
q.clear();
visited.clear();
q.addAll(startingPoints);
Tile crApproxCenterTile = cr.getApproximateCenter();
while (!q.isEmpty()) {
NodeWithPrevAndCost curr = q.poll();
if (getNodeStatus.apply(curr) != NodeStatus.AVAILABLE) {
continue;
}
IntentCode c = curr.getIntentCode();
ClockRegion currCR = curr.getTile().getClockRegion();
if (currCR != null && cr.getRow() == currCR.getRow() && c == IntentCode.NODE_GLOBAL_VDISTR) {
// Only consider base wires
if (getNodeStatus.apply(curr) == NodeStatus.INUSE) {
startingPoints.add(curr);
} else {
List<Node> path = curr.getPrevPath();

for (Node node : path) {
startingPoints.add(new NodeWithPrevAndCost(node));
}
allPIPs.addAll(RouterHelper.getPIPsFromNodes(path));
}
crToVdist.put(cr, curr);
continue nextClockRegion;
}

for (Node downhill : curr.getAllDownhillNodes()) {
if (!allowedIntentCodes.contains(downhill.getIntentCode())) {
q.add(clockRootNode);

List<Pair<IntentCode, ClockRegion>> distrPath = getVDistrPath(clkTree, cr);
nextDistrLevel: for (Pair<IntentCode, ClockRegion> target : distrPath) {
IntentCode targetIC = target.getFirst();
ClockRegion targetCR = target.getSecond();
Tile crApproxCenterTile = targetCR.getApproximateCenter();

while (!q.isEmpty()) {
NodeWithPrevAndCost curr = q.poll();
if (getNodeStatus.apply(curr) != NodeStatus.AVAILABLE) {
continue;
}
if (!visited.add(downhill)) {
continue;
IntentCode currIC = curr.getIntentCode();
ClockRegion currCR = curr.getTile().getClockRegion();
if (currCR != null && targetCR == currCR && currIC == targetIC) {
q.clear();
visited.clear();
q.add(curr);
allPIPs.addAll(RouterHelper.getPIPsFromNodes(curr.getPrevPath()));
if (targetIC == IntentCode.NODE_GLOBAL_VDISTR) {
crToVdist.put(cr, curr);
}
continue nextDistrLevel;
}

for (Node downhill : curr.getAllDownhillNodes()) {
if (!allowedIntentCodes.contains(downhill.getIntentCode())) {
continue;
}
if (!visited.add(downhill)) {
continue;
}
int cost = downhill.getTile().getManhattanDistance(crApproxCenterTile) + (curr.depth * 100);
q.add(new NodeWithPrevAndCost(downhill, curr, cost));
}
int cost = downhill.getTile().getManhattanDistance(crApproxCenterTile) + curr.depth;
q.add(new NodeWithPrevAndCost(downhill, curr, cost));
}
throw new RuntimeException("ERROR: Couldn't route to distribution line in clock region " + cr);
}
throw new RuntimeException("ERROR: Couldn't route to distribution line in clock region " + cr);
}
clk.getPIPs().addAll(allPIPs);

Expand All @@ -343,6 +356,12 @@ public static Map<ClockRegion, Node> routeVrouteToVerticalDistributionLines(Net
return crToVdist;
}

private static List<Pair<IntentCode, ClockRegion>> getVDistrPath(VersalClockTree clkTree,
ClockRegion target) {
return clkTree == null ? Arrays.asList(new Pair<>(IntentCode.NODE_GLOBAL_VDISTR, target))
: clkTree.getClockRegionVDistrPath(target);
}

/**
* For each target clock region, route from the provided vertical distribution line to a
* horizontal distribution line that has a GLOBAL_GLK child node in this clock region.
Expand Down Expand Up @@ -649,4 +668,45 @@ public static void routeNonLCBPins(Net clk, List<SitePinInst> sinks,
}
clk.getPIPs().addAll(allPIPs);
}

@SuppressWarnings("unchecked")
public static Map<String, Map<Integer, VersalClockTree>> readVersalVDistrTrees() {
InputStream is = FileTools.getRapidWrightResourceInputStream(FileTools.VERSAL_VDISTR_TREES_FILE_NAME);
return (Map<String, Map<Integer, VersalClockTree>>) FileTools.readObjectFromKryoFile(is);
}

public static void writeVersalVDistrTreesFile() {
String fileName = FileTools.getRapidWrightResourceFileName(FileTools.VERSAL_VDISTR_TREES_FILE_NAME);
if (vdistrTrees == null) {
throw new RuntimeException("ERROR: Cannot write file '" + fileName + "', source map is null.");
}
FileTools.writeObjectToKryoFile(fileName, vdistrTrees);
}

public static VersalClockTree getVersalClockTree(Device device, int minY, int maxY) {
Map<Integer, VersalClockTree> map = getDeviceVDistrTrees(device.getName());
return map == null ? null : map.get(VersalClockTree.getMinMaxYRangeKey(minY, maxY));
}

private static Map<Integer, VersalClockTree> getDeviceVDistrTrees(String deviceName) {
if (vdistrTrees == null) {
vdistrTrees = readVersalVDistrTrees();
}
return vdistrTrees.get(deviceName);
}

/**
* Given a range of occupied clock region Y coordinates, get the preferred clock
* root Y coordinate.
*
* @param device The current device to target.
* @param minY The smallest Y coordinate of the clock region range.
* @param maxY The largest Y coordinate of the clock region range.
* @return The preferred clock region Y coordinate for the given range or null
* if none could be found.
*/
public static Integer getPreferredClockRootYCoord(Device device, int minY, int maxY) {
VersalClockTree clkTree = getVersalClockTree(device, minY, maxY);
return clkTree == null ? null : clkTree.getPreferredClockRootYCoord();
}
}
143 changes: 143 additions & 0 deletions src/com/xilinx/rapidwright/router/VersalClockTree.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright (c) 2025, Advanced Micro Devices, Inc.
* All rights reserved.
*
* Author: Chris Lavin, 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.router;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.xilinx.rapidwright.device.ClockRegion;
import com.xilinx.rapidwright.device.IntentCode;
import com.xilinx.rapidwright.util.Pair;

/**
* For a given clock region Y coordinate domain range (e.g. Y1-Y5), provides a
* set of clock root options with corresponding vertical distribution paths for
* each target clock region.
*/
public class VersalClockTree {

private int minMaxYKey;

private int preferredClockRootY;

/**
* The key to this map is the clock root y coordinate. The value is a list of
* vertical distribution paths for each target clock region.
*/
private Map<Integer, int[][]> clockRootPathSets;

/**
* Only used for serialization purposes
*/
public VersalClockTree() {

}

public VersalClockTree(int minY, int maxY) {
this.minMaxYKey = getMinMaxYRangeKey(minY, maxY);
}

public int getMinY() {
return minMaxYKey >>> 16;
}

public int getMaxY() {
return minMaxYKey & 0xffff;
}

public int getMinMaxYKey() {
return minMaxYKey;
}

public int getPreferredClockRootYCoord() {
return preferredClockRootY;
}

public void setPreferredClockRootYCoord(int clockRootYCoord) {
this.preferredClockRootY = clockRootYCoord;
}

public Map<Integer, int[][]> getClockRootPathSets() {
return clockRootPathSets;
}

public void setClockRootPathSets(Map<Integer, int[][]> clockRootPathSets) {
this.clockRootPathSets = clockRootPathSets;
}

public Set<Integer> getClockRootOptions() {
return clockRootPathSets.keySet();
}

/**
* Gets the vertical distribution path of the target clock region for the
* preferred clock root.
*
* @param target The destination clock region that needs to be routed via
* vertical distribution lines.
* @return A sequence of vertical distribution constraints (target IntentCode
* and ClockRegion).
*/
public List<Pair<IntentCode, ClockRegion>> getClockRegionVDistrPath(ClockRegion target) {
return getClockRegionVDistrPath(target, preferredClockRootY);
}

/**
* Gets the vertical distribution path of the target clock region for the
* provided clock root.
*
* @param target The destination clock region that needs to be routed
* via vertical distribution lines.
* @param clockRootYCoord The desired clock root Y coordinate path set to use.
* @return A sequence of vertical distribution constraints (target IntentCode
* and ClockRegion).
*/
public List<Pair<IntentCode, ClockRegion>> getClockRegionVDistrPath(ClockRegion target,
int clockRootYCoord) {
int[][] pathData = clockRootPathSets.get(clockRootYCoord);

if (pathData == null) {
System.err.println(
"Missing VDISTR tree for " + target.getDevice() + " targeting CR " + target);
Pair<IntentCode, ClockRegion> simple = new Pair<>(IntentCode.NODE_GLOBAL_VDISTR, target);
return Collections.singletonList(simple);
}

int[] crPathData = pathData[target.getInstanceY()];
List<Pair<IntentCode, ClockRegion>> vdistrPath = new ArrayList<>(crPathData.length);
for (int value : crPathData) {
IntentCode vdistrCode = IntentCode.values()[value >>> 16];
int crY = value & 0xffff;
ClockRegion cr = target.getDevice().getClockRegion(crY, target.getInstanceX());
vdistrPath.add(new Pair<>(vdistrCode, cr));
}
return vdistrPath;
}

public static int getMinMaxYRangeKey(int minY, int maxY) {
assert (minY >= 0 && maxY >= 0 && maxY >= minY);
return (minY << 16) | maxY;
}
}
Loading