MLIR-AIE
AIEPathFinder.h
Go to the documentation of this file.
1//===- AIEPathfinder.h ------------------------------------------*- C++ -*-===//
2//
3// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7// (c) Copyright 2021 Xilinx Inc.
8//
9//===----------------------------------------------------------------------===//
10
11#ifndef AIE_PATHFINDER_H
12#define AIE_PATHFINDER_H
13
16
17#include <algorithm>
18#include <iostream>
19#include <list>
20#include <set>
21
22namespace xilinx::AIE {
23
24#define OVER_CAPACITY_COEFF 0.1
25#define USED_CAPACITY_COEFF 0.02
26#define DEMAND_COEFF 1.1
27#define DEMAND_BASE 1.0
28#define MAX_CIRCUIT_STREAM_CAPACITY 1
29#define MAX_PACKET_STREAM_CAPACITY 32
30
31enum class Connectivity { INVALID = 0, AVAILABLE = 1 };
32
34 SwitchboxConnect() = default;
38
40 std::vector<Port> srcPorts;
41 std::vector<Port> dstPorts;
42 // connectivity between ports
43 std::vector<std::vector<Connectivity>> connectivity;
44 // weights of Dijkstra's shortest path
45 std::vector<std::vector<double>> demand;
46 // history of Channel being over capacity
47 std::vector<std::vector<int>> overCapacity;
48 // how many circuit streams are actually using this Channel
49 std::vector<std::vector<int>> usedCapacity;
50 // how many packet streams are actually using this Channel
51 std::vector<std::vector<int>> packetFlowCount;
52 // only sharing the channel with the same packet group id
53 std::vector<std::vector<int>> packetGroupId;
54 // flags indicating priority routings
55 std::vector<std::vector<bool>> isPriority;
56
57 // resize the matrices to the size of srcPorts and dstPorts
58 void resize() {
59 connectivity.resize(
60 srcPorts.size(),
61 std::vector<Connectivity>(dstPorts.size(), Connectivity::INVALID));
62 demand.resize(srcPorts.size(), std::vector<double>(dstPorts.size(), 0.0));
63 overCapacity.resize(srcPorts.size(), std::vector<int>(dstPorts.size(), 0));
64 usedCapacity.resize(srcPorts.size(), std::vector<int>(dstPorts.size(), 0));
65 packetFlowCount.resize(srcPorts.size(),
66 std::vector<int>(dstPorts.size(), 0));
67 packetGroupId.resize(srcPorts.size(), std::vector<int>(dstPorts.size(), 0));
68 isPriority.resize(srcPorts.size(),
69 std::vector<bool>(dstPorts.size(), false));
70 }
71
72 // update demand at the beginning of each dijkstraShortestPaths iteration
73 void updateDemand() {
74 for (size_t i = 0; i < srcPorts.size(); i++) {
75 for (size_t j = 0; j < dstPorts.size(); j++) {
76 double history = DEMAND_BASE + OVER_CAPACITY_COEFF * overCapacity[i][j];
77 double congestion =
79 demand[i][j] = history * congestion;
80 }
81 }
82 }
83
84 // Inside each dijkstraShortestPaths interation, bump demand when exceeds
85 // capacity. If isPriority is true, then set demand to INF to ensure routing
86 // consistency for prioritized flows
87 void bumpDemand(size_t i, size_t j) {
89 demand[i][j] *=
90 isPriority[i][j] ? std::numeric_limits<int>::max() : DEMAND_COEFF;
91 }
92 }
93};
94
95using PathEndPoint = struct PathEndPoint {
96 PathEndPoint() = default;
98
101
102 friend std::ostream &operator<<(std::ostream &os, const PathEndPoint &s) {
103 os << "PathEndPoint(" << s.coords << ": " << s.port << ")";
104 return os;
105 }
106
108
109 friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
110 const PathEndPoint &s) {
111 os << to_string(s);
112 return os;
113 }
114
115 // Needed for the std::maps that store PathEndPoint.
116 bool operator<(const PathEndPoint &rhs) const {
117 return std::tie(coords, port) < std::tie(rhs.coords, rhs.port);
118 }
119
120 bool operator==(const PathEndPoint &rhs) const {
121 return std::tie(coords, port) == std::tie(rhs.coords, rhs.port);
122 }
123};
124
125using Flow = struct Flow {
126 int packetGroupId;
129 std::vector<PathEndPoint> dsts;
130};
131
132// A SwitchSetting defines the required settings for a Switchbox for a flow
133// SwitchSetting.srcs is the fanin
134// SwitchSetting.dsts is the fanout
136 SwitchSetting() = default;
137 SwitchSetting(std::vector<Port> srcs) : srcs(std::move(srcs)) {}
138 SwitchSetting(std::vector<Port> srcs, std::vector<Port> dsts)
139 : srcs(std::move(srcs)), dsts(std::move(dsts)) {}
140
141 std::vector<Port> srcs;
142 std::vector<Port> dsts;
143
144 // friend definition (will define the function as a non-member function of
145 // the namespace surrounding the class).
146 friend std::ostream &operator<<(std::ostream &os,
147 const SwitchSetting &setting) {
148 os << "{"
149 << join(llvm::map_range(setting.srcs,
150 [](const Port &port) {
151 std::ostringstream ss;
152 ss << port;
153 return ss.str();
154 }),
155 ", ")
156 << " -> "
157 << "{"
158 << join(llvm::map_range(setting.dsts,
159 [](const Port &port) {
160 std::ostringstream ss;
161 ss << port;
162 return ss.str();
163 }),
164 ", ")
165 << "}";
166 return os;
167 }
168
170
171 friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
172 const SwitchSetting &s) {
173 os << to_string(s);
174 return os;
175 }
176
177 bool operator<(const SwitchSetting &rhs) const { return srcs < rhs.srcs; }
178};
179
180using SwitchSettings = std::map<TileID, SwitchSetting>;
181
182class Router {
183public:
184 Router() = default;
185 // This has to go first so it can serve as a key function.
186 // https://lld.llvm.org/missingkeyfunction
187 virtual ~Router() = default;
188 virtual void initialize(int maxCol, int maxRow,
189 const AIETargetModel &targetModel) = 0;
190 virtual void addFlow(TileID srcCoords, Port srcPort, TileID dstCoords,
191 Port dstPort, bool isPacketFlow,
192 bool isPriorityFlow) = 0;
193 virtual void sortFlows(const int maxCol, const int maxRow) = 0;
194 virtual bool addFixedConnection(SwitchboxOp switchboxOp) = 0;
195 virtual std::optional<std::map<PathEndPoint, SwitchSettings>>
196 findPaths(int maxIterations) = 0;
197};
198
199class Pathfinder : public Router {
200public:
201 Pathfinder() = default;
202 void initialize(int maxCol, int maxRow,
203 const AIETargetModel &targetModel) override;
204 void addFlow(TileID srcCoords, Port srcPort, TileID dstCoords, Port dstPort,
205 bool isPacketFlow, bool isPriorityFlow) override;
206 void sortFlows(const int maxCol, const int maxRow) override;
207 bool addFixedConnection(SwitchboxOp switchboxOp) override;
208 std::optional<std::map<PathEndPoint, SwitchSettings>>
209 findPaths(int maxIterations) override;
210 std::map<PathEndPoint, PathEndPoint> dijkstraShortestPaths(PathEndPoint src);
211
212private:
213 // Flows to be routed
214 std::vector<Flow> flows;
215 // Represent all routable paths as a graph
216 // The key is a pair of TileIDs representing the connectivity from srcTile to
217 // dstTile If srcTile == dstTile, it represents connections inside the same
218 // switchbox otherwise, it represents connections (South, North, West, East)
219 // accross two switchboxes
220 std::map<std::pair<TileID, TileID>, SwitchboxConnect> graph;
221 // Channels available in the network
222 // The key is a PathEndPoint representing the start of a path
223 // The value is a vector of PathEndPoints representing the possible ends of
224 // the path
225 std::map<PathEndPoint, std::vector<PathEndPoint>> channels;
226};
227
228// DynamicTileAnalysis integrates the Pathfinder class into the MLIR
229// environment. It passes flows to the Pathfinder as ordered pairs of ints.
230// Detailed routing is received as SwitchboxSettings
231// It then converts these settings to MLIR operations
233public:
235 std::shared_ptr<Router> pathfinder;
236 std::map<PathEndPoint, SwitchSettings> flowSolutions;
237 std::map<PathEndPoint, bool> processedFlows;
238
239 llvm::DenseMap<TileID, TileOp> coordToTile;
240 llvm::DenseMap<TileID, SwitchboxOp> coordToSwitchbox;
241 llvm::DenseMap<TileID, ShimMuxOp> coordToShimMux;
242 llvm::DenseMap<int, PLIOOp> coordToPLIO;
243
244 const int maxIterations = 1000; // how long until declared unroutable
245
246 DynamicTileAnalysis() : pathfinder(std::make_shared<Pathfinder>()) {}
247 DynamicTileAnalysis(std::shared_ptr<Router> p) : pathfinder(std::move(p)) {}
248
249 mlir::LogicalResult runAnalysis(DeviceOp &device);
250
251 int getMaxCol() const { return maxCol; }
252 int getMaxRow() const { return maxRow; }
253
254 TileOp getTile(mlir::OpBuilder &builder, int col, int row);
255
256 SwitchboxOp getSwitchbox(mlir::OpBuilder &builder, int col, int row);
257
258 ShimMuxOp getShimMux(mlir::OpBuilder &builder, int col);
259};
260
261// Get enum int value from WireBundle.
262int getWireBundleAsInt(WireBundle bundle);
263
264} // namespace xilinx::AIE
265
266namespace llvm {
267
268inline raw_ostream &operator<<(raw_ostream &os,
269 const xilinx::AIE::SwitchSettings &ss) {
270 std::stringstream s;
271 s << "\tSwitchSettings: ";
272 for (const auto &[coords, setting] : ss) {
273 s << coords << ": " << setting << " | ";
274 }
275 s << "\n";
276 os << s.str();
277 return os;
278}
279
280} // namespace llvm
281
282#endif
#define GENERATE_TO_STRING(TYPE_WITH_INSERTION_OP)
Definition AIEDialect.h:110
#define MAX_CIRCUIT_STREAM_CAPACITY
#define OVER_CAPACITY_COEFF
#define USED_CAPACITY_COEFF
#define DEMAND_COEFF
#define DEMAND_BASE
ShimMuxOp getShimMux(mlir::OpBuilder &builder, int col)
SwitchboxOp getSwitchbox(mlir::OpBuilder &builder, int col, int row)
llvm::DenseMap< TileID, SwitchboxOp > coordToSwitchbox
TileOp getTile(mlir::OpBuilder &builder, int col, int row)
DynamicTileAnalysis(std::shared_ptr< Router > p)
llvm::DenseMap< TileID, ShimMuxOp > coordToShimMux
std::shared_ptr< Router > pathfinder
std::map< PathEndPoint, bool > processedFlows
mlir::LogicalResult runAnalysis(DeviceOp &device)
llvm::DenseMap< TileID, TileOp > coordToTile
std::map< PathEndPoint, SwitchSettings > flowSolutions
llvm::DenseMap< int, PLIOOp > coordToPLIO
std::map< PathEndPoint, PathEndPoint > dijkstraShortestPaths(PathEndPoint src)
bool addFixedConnection(SwitchboxOp switchboxOp) override
void initialize(int maxCol, int maxRow, const AIETargetModel &targetModel) override
std::optional< std::map< PathEndPoint, SwitchSettings > > findPaths(int maxIterations) override
void sortFlows(const int maxCol, const int maxRow) override
void addFlow(TileID srcCoords, Port srcPort, TileID dstCoords, Port dstPort, bool isPacketFlow, bool isPriorityFlow) override
virtual bool addFixedConnection(SwitchboxOp switchboxOp)=0
virtual void sortFlows(const int maxCol, const int maxRow)=0
virtual std::optional< std::map< PathEndPoint, SwitchSettings > > findPaths(int maxIterations)=0
virtual void initialize(int maxCol, int maxRow, const AIETargetModel &targetModel)=0
virtual ~Router()=default
virtual void addFlow(TileID srcCoords, Port srcPort, TileID dstCoords, Port dstPort, bool isPacketFlow, bool isPriorityFlow)=0
raw_ostream & operator<<(raw_ostream &os, const xilinx::AIE::SwitchSettings &ss)
Include the generated interface declarations.
std::vector< std::vector< int > > packetFlowCount
std::vector< std::vector< int > > overCapacity
SwitchSetting { SwitchSetting()=default SwitchSetting
int getWireBundleAsInt(WireBundle bundle)
void updateDemand()
std::vector< Port > srcs
friend std::ostream & operator<<(std::ostream &os, const Port &port)
Definition AIEDialect.h:131
SwitchboxConnect { SwitchboxConnect()=default SwitchboxConnect
std::vector< PathEndPoint > dsts
void bumpDemand(size_t i, size_t j)
TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")" TileID
friend std::string to_string(const TileID &s)
std::vector< std::vector< int > > packetGroupId
std::map< TileID, SwitchSetting > SwitchSettings
TileID dstCoords
Port { WireBundle bundle Port
Definition AIEDialect.h:118
PathEndPoint src
std::vector< std::vector< double > > demand
std::vector< Port > dstPorts
std::vector< std::vector< bool > > isPriority
bool operator==(const Port &rhs) const
Definition AIEDialect.h:121
std::vector< std::vector< Connectivity > > connectivity
std::vector< Port > srcPorts
Flow { int packetGroupId Flow
TileID srcCoords
PathEndPoint { PathEndPoint()=default PathEndPoint
std::vector< std::vector< int > > usedCapacity
bool operator<(const Port &rhs) const
Definition AIEDialect.h:127