MLIR-AIE
AIEFlowsToJSON.cpp
Go to the documentation of this file.
1//===- AIEFlowsToJSON.cpp ---------------------------------------*- 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/*
12 * Takes as input the mlir after AIECreateFlows and AIEFindFlows.
13 * Converts the flows into a JSON file to be read by other tools.
14 */
15
17
19
20#include "mlir/IR/Attributes.h"
21#include "mlir/Target/LLVMIR/Import.h"
22#include "mlir/Tools/mlir-translate/MlirTranslateMain.h"
23
24#include <queue>
25#include <set>
26
27using namespace mlir;
28using namespace xilinx;
29using namespace xilinx::AIE;
30
31namespace xilinx::AIE {
32
33// returns coordinates in the direction indicated by bundle
34TileID getNextCoords(int col, int row, WireBundle bundle) {
35 switch (bundle) {
36 case WireBundle::North:
37 return {col, row + 1};
38 case WireBundle::South:
39 return {col, row - 1};
40 case WireBundle::East:
41 return {col + 1, row};
42 case WireBundle::West:
43 return {col - 1, row};
44 default:
45 return {col, row};
46 }
47}
48
49void translateSwitchboxes(DeviceOp targetOp, raw_ostream &output) {
50 // count flow sources and destinations
51 std::map<TileID, int> sourceCounts;
52 std::map<TileID, int> destinationCounts;
53 for (FlowOp flowOp : targetOp.getOps<FlowOp>()) {
54 TileOp source = cast<TileOp>(flowOp.getSource().getDefiningOp());
55 TileOp dest = cast<TileOp>(flowOp.getDest().getDefiningOp());
56 TileID srcID = {source.colIndex(), source.rowIndex()};
57 TileID dstID = {dest.colIndex(), dest.rowIndex()};
58 sourceCounts[srcID]++;
59 destinationCounts[dstID]++;
60 }
61 for (PacketFlowOp pktFlowOp : targetOp.getOps<PacketFlowOp>()) {
62 Region &r = pktFlowOp.getPorts();
63 Block &b = r.front();
64 for (Operation &Op : b.getOperations()) {
65 if (auto pktSource = dyn_cast<PacketSourceOp>(Op)) {
66 TileOp source = dyn_cast<TileOp>(pktSource.getTile().getDefiningOp());
67 TileID srcID = {source.colIndex(), source.rowIndex()};
68 sourceCounts[srcID]++;
69 } else if (auto pktDest = dyn_cast<PacketDestOp>(Op)) {
70 TileOp dest = dyn_cast<TileOp>(pktDest.getTile().getDefiningOp());
71 TileID dstID = {dest.colIndex(), dest.rowIndex()};
72 destinationCounts[dstID]++;
73 }
74 }
75 }
76
77 std::map<TileID, ShimMuxOp> shimMuxes;
78 for (ShimMuxOp shimMuxOp : targetOp.getOps<ShimMuxOp>()) {
79 shimMuxes[{shimMuxOp.colIndex(), shimMuxOp.rowIndex()}] = shimMuxOp;
80 }
81
82 int totalPathLength = 0;
83 // for each switchbox, write name, coordinates, and routing demand info
84 for (SwitchboxOp switchboxOp : targetOp.getOps<SwitchboxOp>()) {
85 int col = switchboxOp.colIndex();
86 int row = switchboxOp.rowIndex();
87 std::string switchString = "\"switchbox" + std::to_string(col) +
88 std::to_string(row) + "\": {\n" +
89 "\"col\": " + std::to_string(col) + ",\n" +
90 "\"row\": " + std::to_string(row) + ",\n";
91
92 // write source and destination info
93 switchString +=
94 "\"source_count\": " + std::to_string(sourceCounts[{col, row}]) + ",\n";
95 switchString += "\"destination_count\": " +
96 std::to_string(destinationCounts[{col, row}]) + ",\n";
97
98 // write routing demand info
99 uint32_t connectCounts[10];
100 for (auto &connectCount : connectCounts)
101 connectCount = 0;
102
103 std::set<Port> ports;
104 for (ConnectOp connectOp : switchboxOp.getOps<ConnectOp>())
105 ports.insert(connectOp.destPort());
106 for (MasterSetOp masterSetOp : switchboxOp.getOps<MasterSetOp>())
107 ports.insert(masterSetOp.destPort());
108 for (Port port : ports) {
109 connectCounts[int(port.bundle)]++;
110 }
111
112 switchString += "\"northbound\": " +
113 std::to_string(connectCounts[int(WireBundle::North)]) +
114 ",\n";
115 switchString += "\"eastbound\": " +
116 std::to_string(connectCounts[int(WireBundle::East)]) +
117 ",\n";
118 switchString += "\"southbound\": " +
119 std::to_string(connectCounts[int(WireBundle::South)]) +
120 ",\n";
121 switchString += "\"westbound\": " +
122 std::to_string(connectCounts[int(WireBundle::West)]) + "\n";
123 switchString += "},\n";
124 output << switchString;
125
126 // calculate total path length
127 totalPathLength += connectCounts[int(WireBundle::North)];
128 totalPathLength += connectCounts[int(WireBundle::East)];
129 totalPathLength += connectCounts[int(WireBundle::South)];
130 totalPathLength += connectCounts[int(WireBundle::West)];
131
132 // deduct shim muxes from total path length
133 if (shimMuxes.count({col, row})) {
134 auto shimMuxOp = shimMuxes[{col, row}];
135 std::set<Port> ports;
136 for (ConnectOp connectOp : shimMuxOp.getOps<ConnectOp>()) {
137 Port port = connectOp.sourcePort();
138 if (port.bundle == WireBundle::North) {
139 ports.insert(port);
140 }
141 }
142 for (PacketRulesOp packetRulesOp : shimMuxOp.getOps<PacketRulesOp>()) {
143 Port port = packetRulesOp.sourcePort();
144 if (port.bundle == WireBundle::North) {
145 ports.insert(port);
146 }
147 }
148 totalPathLength -= ports.size();
149 }
150 }
151
152 // write total path length to JSON
153 output << "\"total_path_length\": " << totalPathLength << ",\n";
154}
155
156void translateCircuitFlows(DeviceOp targetOp, int &flowCount,
157 raw_ostream &output) {
158 // for each flow, trace it through switchboxes and write the route to JSON
159 std::set<std::pair<TileOp, Port>> flowSources;
160 for (FlowOp flowOp : targetOp.getOps<FlowOp>()) {
161 // objects used to trace through the flow
162 Port currPort = {flowOp.getSourceBundle(), flowOp.getSourceChannel()};
163 SwitchboxOp currSwitchbox;
164
165 TileOp source = cast<TileOp>(flowOp.getSource().getDefiningOp());
166 // TileOp dest = cast<TileOp>(flowOp.dest().getDefiningOp());
167
168 // track flow sources to avoid duplicate routes
169 std::pair<TileOp, Port> flowSource = {source, currPort};
170 if (flowSources.count(flowSource)) {
171 continue;
172 }
173 flowSources.insert(flowSource);
174
175 std::string routeString =
176 "\"route" + std::to_string(flowCount++) + "\": [ ";
177
178 // FIFO to handle fanouts
179 std::queue<Port> nextPorts;
180 std::queue<SwitchboxOp> nextSwitches;
181
182 // find the starting switchbox
183 for (SwitchboxOp switchboxOp : targetOp.getOps<SwitchboxOp>()) {
184 if (switchboxOp.colIndex() == source.colIndex() &&
185 switchboxOp.rowIndex() == source.rowIndex()) {
186 currSwitchbox = switchboxOp;
187 break;
188 }
189 }
190
191 // if the flow starts in a shim, handle seperately
192 for (ShimMuxOp shimMuxOp : targetOp.getOps<ShimMuxOp>()) {
193 if (shimMuxOp.colIndex() == source.colIndex() &&
194 shimMuxOp.rowIndex() == source.rowIndex()) {
195 for (ConnectOp connectOp : shimMuxOp.getOps<ConnectOp>()) {
196 if (connectOp.getSourceBundle() == currPort.bundle &&
197 connectOp.getSourceChannel() == currPort.channel) {
198 currPort.bundle = getConnectingBundle(connectOp.getDestBundle());
199 currPort.channel = connectOp.getDestChannel();
200 break;
201 }
202 }
203 break;
204 }
205 }
206
207 // trace through the flow and add switchbox coordinates to JSON
208 bool done = false;
209 do {
210 // get the coordinates for the next switchbox in the flow
211 for (ConnectOp connectOp : currSwitchbox.getOps<ConnectOp>()) {
212 // if this connectOp is the end of a flow, skip
213 if ((currSwitchbox.rowIndex() == 0 &&
214 connectOp.getDestBundle() == WireBundle::South) ||
215 connectOp.getDestBundle() == WireBundle::DMA ||
216 connectOp.getDestBundle() == WireBundle::Core)
217 continue;
218
219 if (connectOp.getSourceBundle() == currPort.bundle &&
220 connectOp.getSourceChannel() == currPort.channel) {
221 nextPorts.push({getConnectingBundle(connectOp.getDestBundle()),
222 connectOp.getDestChannel()});
223
224 TileID nextCoords =
225 getNextCoords(currSwitchbox.colIndex(), currSwitchbox.rowIndex(),
226 connectOp.getDestBundle());
227
228 // search for next switchbox to connect to
229 for (SwitchboxOp switchboxOp : targetOp.getOps<SwitchboxOp>()) {
230 if (switchboxOp.colIndex() == nextCoords.col &&
231 switchboxOp.rowIndex() == nextCoords.row) {
232 nextSwitches.push(switchboxOp);
233 break;
234 }
235 }
236 }
237 }
238
239 // add switchbox to the routeString
240 std::string dirString = std::string("[[") +
241 std::to_string(currSwitchbox.colIndex()) + ", " +
242 std::to_string(currSwitchbox.rowIndex()) + "], [";
243 int opCount = 0, dirCount = 0;
244 for (ConnectOp connectOp : currSwitchbox.getOps<ConnectOp>()) {
245 if (connectOp.getSourceBundle() == currPort.bundle &&
246 connectOp.getSourceChannel() == currPort.channel) {
247 if (opCount++ > 0)
248 dirString += ", ";
249 dirCount++;
250 dirString +=
251 "\"" +
252 (std::string)stringifyWireBundle(connectOp.getDestBundle()) +
253 "\"";
254 }
255 }
256 dirString += "]], ";
257 if (dirCount > 0)
258 routeString += dirString;
259
260 if (nextPorts.empty() || nextSwitches.empty()) {
261 done = true;
262 routeString += "[]";
263 } else {
264 currPort = nextPorts.front();
265 currSwitchbox = nextSwitches.front();
266 nextPorts.pop();
267 nextSwitches.pop();
268 }
269 } while (!done);
270 // write string to JSON
271 routeString += std::string(" ],\n");
272 output << routeString;
273 }
274}
275
276void translatePacketFlows(DeviceOp targetOp, int &flowCount,
277 raw_ostream &output) {
278 // for each flow, trace it through switchboxes and write the route to JSON
279 std::set<std::pair<TileOp, Port>> flowSources;
280 for (PacketFlowOp pktFlowOp : targetOp.getOps<PacketFlowOp>()) {
281 Region &r = pktFlowOp.getPorts();
282 Block &b = r.front();
283 Port sourcePort, destPort;
284 TileOp source, dest;
285 for (Operation &Op : b.getOperations()) {
286 if (auto pktSource = dyn_cast<PacketSourceOp>(Op)) {
287 source = dyn_cast<TileOp>(pktSource.getTile().getDefiningOp());
288 sourcePort = pktSource.port();
289 } else if (auto pktDest = dyn_cast<PacketDestOp>(Op)) {
290 dest = dyn_cast<TileOp>(pktDest.getTile().getDefiningOp());
291 destPort = pktDest.port();
292 }
293 }
294
295 // objects used to trace through the flow
296 Port currPort = sourcePort;
297 SwitchboxOp currSwitchbox;
298
299 // track flow sources to avoid duplicate routes
300 std::pair<TileOp, Port> flowSource = {source, currPort};
301 if (flowSources.count(flowSource)) {
302 continue;
303 }
304 flowSources.insert(flowSource);
305
306 std::string routeString =
307 "\"route" + std::to_string(flowCount++) + "\": [ ";
308
309 // FIFO to handle fanouts
310 std::queue<Port> nextPorts;
311 std::queue<SwitchboxOp> nextSwitches;
312
313 // find the starting switchbox
314 for (SwitchboxOp switchboxOp : targetOp.getOps<SwitchboxOp>()) {
315 if (switchboxOp.colIndex() == source.colIndex() &&
316 switchboxOp.rowIndex() == source.rowIndex()) {
317 currSwitchbox = switchboxOp;
318 break;
319 }
320 }
321
322 // if the flow starts in a shim, handle seperately
323 for (ShimMuxOp shimMuxOp : targetOp.getOps<ShimMuxOp>()) {
324 if (shimMuxOp.colIndex() == source.colIndex() &&
325 shimMuxOp.rowIndex() == source.rowIndex()) {
326 for (ConnectOp connectOp : shimMuxOp.getOps<ConnectOp>()) {
327 if (connectOp.getSourceBundle() == currPort.bundle &&
328 connectOp.getSourceChannel() == currPort.channel) {
329 currPort.bundle = getConnectingBundle(connectOp.getDestBundle());
330 currPort.channel = connectOp.getDestChannel();
331 break;
332 }
333 }
334 break;
335 }
336 }
337
338 // trace through the flow and add switchbox coordinates to JSON
339 bool done = false;
340 do {
341 for (auto packetRulesOp : currSwitchbox.getOps<PacketRulesOp>()) {
342 Port destPort;
343 for (auto masterSetOp : currSwitchbox.getOps<MasterSetOp>()) {
344 for (Value amsel : masterSetOp.getAmsels()) {
345 for (auto ruleOp :
346 packetRulesOp.getRules().front().getOps<PacketRuleOp>()) {
347 if (ruleOp.getAmsel() == amsel) {
348 destPort = masterSetOp.destPort();
349 }
350 }
351 }
352 }
353
354 // if this packetRulesOp is the end of a flow, skip
355 if ((currSwitchbox.rowIndex() == 0 &&
356 destPort.bundle == WireBundle::South) ||
357 destPort.bundle == WireBundle::DMA ||
358 destPort.bundle == WireBundle::Core)
359 continue;
360
361 if (packetRulesOp.sourcePort() == currPort) {
362 nextPorts.push(
363 {getConnectingBundle(destPort.bundle), destPort.channel});
364
365 TileID nextCoords =
366 getNextCoords(currSwitchbox.colIndex(), currSwitchbox.rowIndex(),
367 destPort.bundle);
368
369 // search for next switchbox to connect to
370 for (SwitchboxOp switchboxOp : targetOp.getOps<SwitchboxOp>()) {
371 if (switchboxOp.colIndex() == nextCoords.col &&
372 switchboxOp.rowIndex() == nextCoords.row) {
373 nextSwitches.push(switchboxOp);
374 break;
375 }
376 }
377 }
378 }
379
380 // add switchbox to the routeString
381 std::string dirString = std::string("[[") +
382 std::to_string(currSwitchbox.colIndex()) + ", " +
383 std::to_string(currSwitchbox.rowIndex()) + "], [";
384 int opCount = 0, dirCount = 0;
385 for (auto packetRulesOp : currSwitchbox.getOps<PacketRulesOp>()) {
386 if (packetRulesOp.sourcePort() == currPort) {
387 Port destPort;
388 for (auto masterSetOp : currSwitchbox.getOps<MasterSetOp>()) {
389 for (Value amsel : masterSetOp.getAmsels()) {
390 for (auto ruleOp :
391 packetRulesOp.getRules().front().getOps<PacketRuleOp>()) {
392 if (ruleOp.getAmsel() == amsel) {
393 destPort = masterSetOp.destPort();
394 }
395 }
396 }
397 }
398 if (opCount++ > 0)
399 dirString += ", ";
400 dirCount++;
401 dirString +=
402 "\"" + (std::string)stringifyWireBundle(destPort.bundle) + "\"";
403 }
404 }
405 dirString += "]], ";
406 if (dirCount > 0)
407 routeString += dirString;
408
409 if (nextPorts.empty() || nextSwitches.empty()) {
410 done = true;
411 routeString += "[]";
412 } else {
413 currPort = nextPorts.front();
414 currSwitchbox = nextSwitches.front();
415 nextPorts.pop();
416 nextSwitches.pop();
417 }
418 } while (!done);
419 // write string to JSON
420 routeString += std::string(" ],\n");
421 output << routeString;
422 }
423}
424
425mlir::LogicalResult AIEFlowsToJSON(ModuleOp module, raw_ostream &output) {
426 output << "{\n";
427 if (module.getOps<DeviceOp>().empty()) {
428 module.emitOpError("expected AIE.device operation at toplevel");
429 }
430
431 DeviceOp targetOp = *(module.getOps<DeviceOp>().begin());
432 int flowCount = 0;
433
434 translateSwitchboxes(targetOp, output);
435 translateCircuitFlows(targetOp, flowCount, output);
436 translatePacketFlows(targetOp, flowCount, output);
437
438 output << "\"route_all\": [],\n";
439 output << "\n\"end json\": 0\n"; // dummy line to avoid errors from commas
440 output << "}";
441 return success();
442} // end AIETranslateToJSON
443} // namespace xilinx::AIE
Include the generated interface declarations.
void translateCircuitFlows(DeviceOp targetOp, int &flowCount, raw_ostream &output)
TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")" TileID
Port { WireBundle bundle Port
Definition AIEDialect.h:118
TileID getNextCoords(int col, int row, WireBundle bundle)
void translatePacketFlows(DeviceOp targetOp, int &flowCount, raw_ostream &output)
mlir::LogicalResult AIEFlowsToJSON(mlir::ModuleOp module, llvm::raw_ostream &output)
void translateSwitchboxes(DeviceOp targetOp, raw_ostream &output)
WireBundle getConnectingBundle(WireBundle dir)