Skip to content

Commit

Permalink
[Ctrl Pkt Reconfig E2E] Enforcing determinism in control network rout…
Browse files Browse the repository at this point in the history
…ing and packet arbitration (#1752)
  • Loading branch information
erwei-xilinx authored Sep 6, 2024
1 parent bd5fafa commit 1864807
Show file tree
Hide file tree
Showing 11 changed files with 249 additions and 69 deletions.
12 changes: 10 additions & 2 deletions include/aie/Dialect/AIE/IR/AIEOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,14 @@ def AIE_PacketFlowOp: AIE_Op<"packet_flow", [SingleBlockImplicitTerminator<"EndO
let description = [{
A logical packet-switched flow between tiles. During place and
route, this is replaced by MasterSets and PacketRules inside
switchboxes.
switchboxes.

The optional attribute keep_pkt_header indicates whether each
data packet's packet header gets preserved at the flow's
destination. The optional attribute priority_route indicates
whether the packet flow is routed in priority over other flows,
so that they always get allocated with the same master, slave
ports, arbiters and master selects (msel).

Example:
```
Expand All @@ -653,7 +660,8 @@ def AIE_PacketFlowOp: AIE_Op<"packet_flow", [SingleBlockImplicitTerminator<"EndO

let arguments = (
ins AIEI8Attr:$ID,
OptionalAttr<BoolAttr>:$keep_pkt_header
OptionalAttr<BoolAttr>:$keep_pkt_header,
OptionalAttr<BoolAttr>:$priority_route
);
let regions = (region AnyRegion:$ports);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,16 @@ using namespace xilinx::AIE;
// Populate column control streaming interconnect overlay
void populateAIEColumnControlOverlay(DeviceOp &device);

// AIE arch-specific row id to shim dma mm2s channel mapping. All shim mm2s
// channels were assumed to be available for control packet flow routing (i.e.
// not reserved by any aie.flow circuit-switched routing).
DenseMap<int, int> getRowToShimChanMap(const AIETargetModel &targetModel,
WireBundle bundle);

// AIE arch-specific tile id to controller id mapping. Users can use those
// packet ids for design but run into risk of deadlocking control packet flows.
DenseMap<AIE::TileID, int>
getTileToControllerIdMap(bool clColumnWiseUniqueIDs,
const AIETargetModel &targetModel);

#endif
8 changes: 6 additions & 2 deletions include/aie/Dialect/AIE/Transforms/AIEPathFinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ using PathEndPoint = struct PathEndPoint {

using Flow = struct Flow {
int packetGroupId;
bool isPriorityFlow;
PathEndPoint src;
std::vector<PathEndPoint> dsts;
};
Expand Down Expand Up @@ -181,7 +182,9 @@ class Router {
virtual void initialize(int maxCol, int maxRow,
const AIETargetModel &targetModel) = 0;
virtual void addFlow(TileID srcCoords, Port srcPort, TileID dstCoords,
Port dstPort, bool isPacketFlow) = 0;
Port dstPort, bool isPacketFlow,
bool isPriorityFlow) = 0;
virtual void sortFlows(const int maxCol, const int maxRow) = 0;
virtual bool addFixedConnection(SwitchboxOp switchboxOp) = 0;
virtual std::optional<std::map<PathEndPoint, SwitchSettings>>
findPaths(int maxIterations) = 0;
Expand All @@ -193,7 +196,8 @@ class Pathfinder : public Router {
void initialize(int maxCol, int maxRow,
const AIETargetModel &targetModel) override;
void addFlow(TileID srcCoords, Port srcPort, TileID dstCoords, Port dstPort,
bool isPacketFlow) override;
bool isPacketFlow, bool isPriorityFlow) override;
void sortFlows(const int maxCol, const int maxRow) override;
bool addFixedConnection(SwitchboxOp switchboxOp) override;
std::optional<std::map<PathEndPoint, SwitchSettings>>
findPaths(int maxIterations) override;
Expand Down
134 changes: 117 additions & 17 deletions lib/Dialect/AIE/Transforms/AIECreatePathFindFlows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,13 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder) {

// Map from a port and flowID to
DenseMap<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>> packetFlows;
DenseMap<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>> ctrlPacketFlows;
SmallVector<std::pair<PhysPort, int>, 4> slavePorts;
DenseMap<std::pair<PhysPort, int>, int> slaveAMSels;
// Map from a port to
// Flag to keep packet header at packet flow destination
DenseMap<PhysPort, BoolAttr> keepPktHeaderAttr;
// Map from tileID and master ports to flags labelling control packet flows
DenseMap<std::pair<PhysPort, int>, bool> ctrlPktFlows;

for (auto tileOp : device.getOps<TileOp>()) {
int col = tileOp.colIndex();
Expand Down Expand Up @@ -343,6 +346,12 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder) {
std::pair{connect, flowID}) ==
switchboxes[currTile].end())
switchboxes[currTile].push_back({connect, flowID});
// Assign "control packet flows" flag per switchbox, based on
// packet flow op attribute
auto ctrlPkt = pktFlowOp.getPriorityRoute();
ctrlPktFlows[{
{analyzer.getTile(builder, curr.col, curr.row), dest},
flowID}] = ctrlPkt ? *ctrlPkt : false;
}
}
}
Expand All @@ -363,7 +372,10 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder) {
Port destPort = conn.dst;
auto sourceFlow =
std::make_pair(std::make_pair(tileOp, sourcePort), flowID);
packetFlows[sourceFlow].push_back({tileOp, destPort});
if (ctrlPktFlows[{{tileOp, destPort}, flowID}])
ctrlPacketFlows[sourceFlow].push_back({tileOp, destPort});
else
packetFlows[sourceFlow].push_back({tileOp, destPort});
slavePorts.push_back(sourceFlow);
LLVM_DEBUG(llvm::dbgs() << "flowID " << flowID << ':'
<< stringifyWireBundle(sourcePort.bundle) << " "
Expand Down Expand Up @@ -404,11 +416,18 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder) {
auto getNewUniqueAmsel = [&](DenseMap<std::pair<Operation *, int>,
SmallVector<Port, 4>>
masterAMSels,
Operation *tileOp) {
for (int i = 0; i < numMsels; i++)
for (int a = 0; a < numArbiters; a++)
if (!masterAMSels.count({tileOp, getAmselFromArbiterIDAndMsel(a, i)}))
return getAmselFromArbiterIDAndMsel(a, i);
Operation *tileOp, bool isCtrlPkt) {
if (isCtrlPkt) { // Higher AMsel first
for (int i = numMsels - 1; i >= 0; i--)
for (int a = numArbiters - 1; a >= 0; a--)
if (!masterAMSels.count({tileOp, getAmselFromArbiterIDAndMsel(a, i)}))
return getAmselFromArbiterIDAndMsel(a, i);
} else { // Lower AMsel first
for (int i = 0; i < numMsels; i++)
for (int a = 0; a < numArbiters; a++)
if (!masterAMSels.count({tileOp, getAmselFromArbiterIDAndMsel(a, i)}))
return getAmselFromArbiterIDAndMsel(a, i);
}
tileOp->emitOpError("tile op has used up all arbiter-msel combinations");
return -1;
};
Expand All @@ -426,16 +445,87 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder) {
return -1;
};

std::vector<std::pair<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>>
sortedPacketFlows(packetFlows.begin(), packetFlows.end());

// To get determinsitic behaviour
std::sort(sortedPacketFlows.begin(), sortedPacketFlows.end(),
[](const auto &lhs, const auto &rhs) {
auto lhsFlowID = lhs.first.second;
auto rhsFlowID = rhs.first.second;
return lhsFlowID < rhsFlowID;
// To get determinsitic behaviour; allocate amsels for control packet flows
// before others
auto getWireBundleAsInt = [](WireBundle bundle) {
switch (bundle) {
case WireBundle::Core:
return 0;
case WireBundle::DMA:
return 1;
case WireBundle::FIFO:
return 2;
case WireBundle::South:
return 3;
case WireBundle::West:
return 4;
case WireBundle::North:
return 5;
case WireBundle::East:
return 6;
case WireBundle::PLIO:
return 7;
case WireBundle::NOC:
return 8;
case WireBundle::Trace:
return 9;
case WireBundle::Ctrl:
return 10;
}
return -1;
};
auto getUniqueIdPerFlowPerSB =
[getWireBundleAsInt](int flowID, WireBundle srcBundle,
SmallVector<PhysPort, 4> dests) {
int totalNumOfWireBundles = AIE::getMaxEnumValForWireBundle();
int currMultiplier = totalNumOfWireBundles;
int uniqueId = flowID;
uniqueId += currMultiplier + getWireBundleAsInt(srcBundle);
currMultiplier += totalNumOfWireBundles;
for (auto dst : dests) {
uniqueId += currMultiplier;
uniqueId += getWireBundleAsInt(dst.second.bundle);
currMultiplier += totalNumOfWireBundles;
}
return uniqueId;
};
auto getSortedPacketFlows =
[&](DenseMap<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>> pktFlows,
DenseMap<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>
ctrlPktFlows) {
std::vector<
std::pair<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>>
sortedpktFlows(pktFlows.begin(), pktFlows.end());
std::sort(
sortedpktFlows.begin(), sortedpktFlows.end(),
[getUniqueIdPerFlowPerSB](const auto &lhs, const auto &rhs) {
int lhsUniqueID = getUniqueIdPerFlowPerSB(
lhs.first.second, lhs.first.first.second.bundle, lhs.second);
int rhsUniqueID = getUniqueIdPerFlowPerSB(
rhs.first.second, rhs.first.first.second.bundle, rhs.second);
return lhsUniqueID < rhsUniqueID;
});
std::vector<
std::pair<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>>
sortedctrlpktFlows(ctrlPktFlows.begin(), ctrlPktFlows.end());
std::sort(
sortedctrlpktFlows.begin(), sortedctrlpktFlows.end(),
[getUniqueIdPerFlowPerSB](const auto &lhs, const auto &rhs) {
int lhsUniqueID = getUniqueIdPerFlowPerSB(
lhs.first.second, lhs.first.first.second.bundle, lhs.second);
int rhsUniqueID = getUniqueIdPerFlowPerSB(
rhs.first.second, rhs.first.first.second.bundle, rhs.second);
return lhsUniqueID < rhsUniqueID;
});
sortedctrlpktFlows.insert(sortedctrlpktFlows.end(),
sortedpktFlows.begin(), sortedpktFlows.end());
return sortedctrlpktFlows;
};

std::vector<std::pair<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>>
sortedPacketFlows = getSortedPacketFlows(packetFlows, ctrlPacketFlows);

packetFlows.insert(ctrlPacketFlows.begin(), ctrlPacketFlows.end());

// Check all multi-cast flows (same source, same ID). They should be
// assigned the same arbiter and msel so that the flow can reach all the
Expand Down Expand Up @@ -503,7 +593,17 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder) {
if (!foundMatchedDest) {
// This packet flow switchbox's output ports completely mismatches with
// any existing amsel. Creating a new amsel.
amselValue = getNewUniqueAmsel(masterAMSels, tileOp);

// Check if any of the master ports have ever been used for ctrl pkts.
// Ctrl pkt (i.e. prioritized packet flow) amsel assignment follows a
// different strategy (see method below).
bool ctrlPktAMsel =
llvm::any_of(packetFlow.second, [&](PhysPort destPhysPort) {
Port port = destPhysPort.second;
return ctrlPktFlows[{{tileOp, port}, packetFlow.first.second}];
});

amselValue = getNewUniqueAmsel(masterAMSels, tileOp, ctrlPktAMsel);
// Update masterAMSels with new amsel
for (auto dest : packetFlow.second) {
Port port = dest.second;
Expand Down
4 changes: 2 additions & 2 deletions lib/Dialect/AIE/Transforms/AIEFindFlows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ static void findFlowsFrom(TileOp op, ConnectivityAnalysis &analysis,
destOp->getResult(0), destPort.bundle,
destPort.channel);
} else {
auto flowOp = rewriter.create<PacketFlowOp>(Op->getLoc(),
maskValue.value, nullptr);
auto flowOp = rewriter.create<PacketFlowOp>(
Op->getLoc(), maskValue.value, nullptr, nullptr);
PacketFlowOp::ensureTerminator(flowOp.getPorts(), rewriter,
Op->getLoc());
OpBuilder::InsertPoint ip = rewriter.saveInsertionPoint();
Expand Down
23 changes: 14 additions & 9 deletions lib/Dialect/AIE/Transforms/AIEGenerateColumnControlOverlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,14 +239,16 @@ struct AIEGenerateColumnControlOverlayPass
}
}

AIE::PacketFlowOp
createPacketFlowOp(OpBuilder &builder, int &flowID, Value source,
xilinx::AIE::WireBundle sourceBundle,
uint32_t sourceChannel, Value dest,
xilinx::AIE::WireBundle destBundle, uint32_t destChannel,
mlir::BoolAttr keep_pkt_header = nullptr) {
AIE::PacketFlowOp createPacketFlowOp(OpBuilder &builder, int &flowID,
Value source,
xilinx::AIE::WireBundle sourceBundle,
uint32_t sourceChannel, Value dest,
xilinx::AIE::WireBundle destBundle,
uint32_t destChannel,
mlir::BoolAttr keep_pkt_header = nullptr,
mlir::BoolAttr ctrl_pkt_flow = nullptr) {
AIE::PacketFlowOp pktFlow = builder.create<AIE::PacketFlowOp>(
builder.getUnknownLoc(), flowID++, keep_pkt_header);
builder.getUnknownLoc(), flowID++, keep_pkt_header, ctrl_pkt_flow);
Region &r_pktFlow = pktFlow.getPorts();
Block *b_pktFlow = builder.createBlock(&r_pktFlow);
builder.setInsertionPointToStart(b_pktFlow);
Expand Down Expand Up @@ -307,6 +309,8 @@ struct AIEGenerateColumnControlOverlayPass
auto availableShimChans =
getAvailableShimChans(device, shimTile, shimWireBundle, isShimMM2S);
SmallVector<AIE::ShimDMAAllocationOp> shimAllocs;
device.walk(
[&](AIE::ShimDMAAllocationOp salloc) { shimAllocs.push_back(salloc); });
for (auto tOp : ctrlTiles) {
if (tOp->hasAttr("controller_id"))
ctrlPktFlowID =
Expand All @@ -323,16 +327,17 @@ struct AIEGenerateColumnControlOverlayPass
"from routing control packets.");
builder.setInsertionPointToEnd(device.getBody());
auto keep_pkt_header = builder.getBoolAttr(true);
auto ctrl_pkt_flow = builder.getBoolAttr(true);
if (isShimMM2S)
(void)createPacketFlowOp(
builder, ctrlPktFlowID, shimTile, shimWireBundle,
rowToShimChanMap[tOp.rowIndex()], tOp, ctrlWireBundle,
coreOrMemChanId, keep_pkt_header);
coreOrMemChanId, keep_pkt_header, ctrl_pkt_flow);
else
(void)createPacketFlowOp(builder, ctrlPktFlowID, tOp, ctrlWireBundle,
coreOrMemChanId, shimTile, shimWireBundle,
rowToShimChanMap[tOp.rowIndex()],
keep_pkt_header);
keep_pkt_header, ctrl_pkt_flow);

// Generate shim dma alloc ops as handle for runtime sequence to pickup,
// when issuing control packets
Expand Down
Loading

0 comments on commit 1864807

Please sign in to comment.