From a532b0ce3c132c096d8275c2cbd97c41d0f48a27 Mon Sep 17 00:00:00 2001 From: max Date: Fri, 8 Dec 2023 00:16:35 -0600 Subject: [PATCH] fixed_connections working --- .github/workflows/lintAndFormat.yml | 2 - .pylintrc | 2 +- .../Dialect/AIE/Transforms/AIEPathFinder.h | 56 +- lib/Dialect/AIE/IR/AIEDialect.cpp | 41 +- lib/Dialect/AIE/Transforms/AIEPathFinder.cpp | 39 +- python/CMakeLists.txt | 6 + python/PybindTypes.cpp | 40 + python/PybindTypes.h | 20 + python/PythonPass.cpp | 2 +- python/RouterPass.cpp | 9 +- python/util.py | 193 ++-- test/python/python_passes.py | 980 +++++++++++++++++- 12 files changed, 1206 insertions(+), 184 deletions(-) diff --git a/.github/workflows/lintAndFormat.yml b/.github/workflows/lintAndFormat.yml index e486112d57..e14bf59da2 100644 --- a/.github/workflows/lintAndFormat.yml +++ b/.github/workflows/lintAndFormat.yml @@ -151,7 +151,6 @@ jobs: with: tool_name: clang-format level: error - fail_on_error: true cleanup: true - name: Run black format @@ -175,7 +174,6 @@ jobs: with: tool_name: black level: error - fail_on_error: true code-coverage: diff --git a/.pylintrc b/.pylintrc index 48805d217f..13c6be76ea 100644 --- a/.pylintrc +++ b/.pylintrc @@ -60,4 +60,4 @@ check-str-concat-over-line-jumps=yes # R0913: Too many arguments # R0914: Too many local variables # W1514: Using open without explicitly specifying an encoding (unspecified-encoding) -disable=C0116,C0114,E0402,C0115,C0415,R0913,R0914,W1514 \ No newline at end of file +disable=C0116,C0114,E0402,C0115,C0415,R0913,R0914,W1514 diff --git a/include/aie/Dialect/AIE/Transforms/AIEPathFinder.h b/include/aie/Dialect/AIE/Transforms/AIEPathFinder.h index 59200da92f..7fd9bde5f6 100644 --- a/include/aie/Dialect/AIE/Transforms/AIEPathFinder.h +++ b/include/aie/Dialect/AIE/Transforms/AIEPathFinder.h @@ -24,8 +24,9 @@ namespace xilinx::AIE { -typedef struct Switchbox : TileID { +using Switchbox = struct Switchbox : TileID { // Necessary for initializer construction? + Switchbox(TileID t) : TileID(t) {} Switchbox(int col, int row) : TileID{col, row} {} friend std::ostream &operator<<(std::ostream &os, const Switchbox &s) { os << "Switchbox(" << s.col << ", " << s.row << ")"; @@ -37,10 +38,9 @@ typedef struct Switchbox : TileID { bool operator==(const Switchbox &rhs) const { return static_cast(*this) == rhs; } +}; -} Switchbox; - -typedef struct Channel { +using Channel = struct Channel { Channel(Switchbox &src, Switchbox &target, WireBundle bundle, int maxCapacity) : src(src), target(target), bundle(bundle), maxCapacity(maxCapacity) {} @@ -65,7 +65,7 @@ typedef struct Channel { int usedCapacity = 0; // how many flows are actually using this Channel std::set fixedCapacity; // channels not available to the algorithm int overCapacityCount = 0; // history of Channel being over capacity -} Channel; +}; struct SwitchboxNode; struct ChannelEdge; @@ -73,17 +73,17 @@ using SwitchboxNodeBase = llvm::DGNode; using ChannelEdgeBase = llvm::DGEdge; using SwitchboxGraphBase = llvm::DirectedGraph; -typedef struct SwitchboxNode : SwitchboxNodeBase, Switchbox { +using SwitchboxNode = struct SwitchboxNode : SwitchboxNodeBase, Switchbox { using Switchbox::Switchbox; SwitchboxNode(int col, int row, int id) : Switchbox{col, row}, id{id} {} int id; -} SwitchboxNode; +}; // warning: 'xilinx::AIE::ChannelEdge::src' will be initialized after // SwitchboxNode &src; [-Wreorder] #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wreorder" -typedef struct ChannelEdge : ChannelEdgeBase, Channel { +using ChannelEdge = struct ChannelEdge : ChannelEdgeBase, Channel { using Channel::Channel; explicit ChannelEdge(SwitchboxNode &target) = delete; @@ -97,7 +97,7 @@ typedef struct ChannelEdge : ChannelEdgeBase, Channel { ChannelEdge &operator=(ChannelEdge &&E) = delete; SwitchboxNode &src; -} ChannelEdge; +}; #pragma GCC diagnostic pop class SwitchboxGraph : public SwitchboxGraphBase { @@ -107,9 +107,9 @@ class SwitchboxGraph : public SwitchboxGraphBase { }; // A SwitchSetting defines the required settings for a Switchbox for a flow -// SwitchSetting.first is the incoming signal -// SwitchSetting.second is the fanout -typedef struct SwitchSetting { +// SwitchSetting.src is the incoming signal +// SwitchSetting.dsts is the fanout +using SwitchSetting = struct SwitchSetting { SwitchSetting() = default; SwitchSetting(Port src) : src(src) {} SwitchSetting(Port src, std::set dsts) @@ -143,14 +143,13 @@ typedef struct SwitchSetting { } bool operator<(const SwitchSetting &rhs) const { return src < rhs.src; } +}; -} SwitchSetting; - -typedef std::map SwitchSettings; +using SwitchSettings = std::map; // A Flow defines source and destination vertices // Only one source, but any number of destinations (fanout) -typedef struct PathEndPoint { +using PathEndPoint = struct PathEndPoint { Switchbox sb; Port port; @@ -175,21 +174,20 @@ typedef struct PathEndPoint { bool operator==(const PathEndPoint &rhs) const { return std::tie(sb, port) == std::tie(rhs.sb, rhs.port); } - -} PathEndPoint; +}; // A Flow defines source and destination vertices // Only one source, but any number of destinations (fanout) -typedef struct PathEndPointNode : PathEndPoint { +using PathEndPointNode = struct PathEndPointNode : PathEndPoint { PathEndPointNode(SwitchboxNode *sb, Port port) : PathEndPoint{*sb, port}, sb(sb) {} SwitchboxNode *sb; -} PathEndPointNode; +}; -typedef struct FlowNode { +using FlowNode = struct FlowNode { PathEndPointNode src; std::vector dsts; -} FlowNode; +}; class Router { public: @@ -219,7 +217,7 @@ class Pathfinder : public Router { findPaths(int maxIterations) override; Switchbox *getSwitchbox(TileID coords) override { - auto sb = std::find_if(graph.begin(), graph.end(), [&](SwitchboxNode *sb) { + auto *sb = std::find_if(graph.begin(), graph.end(), [&](SwitchboxNode *sb) { return sb->col == coords.col && sb->row == coords.row; }); assert(sb != graph.end() && "couldn't find sb"); @@ -281,7 +279,8 @@ class DynamicTileAnalysis { // because one of the graph traits below is doing the comparison internally // (try moving this below the llvm namespace...) namespace std { -template <> struct less { +template <> +struct less { bool operator()(const xilinx::AIE::Switchbox *a, const xilinx::AIE::Switchbox *b) const { return *a < *b; @@ -291,7 +290,8 @@ template <> struct less { namespace llvm { -template <> struct GraphTraits { +template <> +struct GraphTraits { using NodeRef = xilinx::AIE::SwitchboxNode *; static xilinx::AIE::SwitchboxNode *SwitchboxGraphGetSwitchbox( @@ -349,13 +349,15 @@ inline raw_ostream &operator<<(raw_ostream &os, } // namespace llvm -template <> struct std::hash { +template <> +struct std::hash { std::size_t operator()(const xilinx::AIE::Switchbox &s) const noexcept { return std::hash{}(s); } }; -template <> struct std::hash { +template <> +struct std::hash { std::size_t operator()(const xilinx::AIE::PathEndPoint &pe) const noexcept { std::size_t h1 = std::hash{}(pe.port); std::size_t h2 = std::hash{}(pe.sb); diff --git a/lib/Dialect/AIE/IR/AIEDialect.cpp b/lib/Dialect/AIE/IR/AIEDialect.cpp index b62002469a..9ce9590289 100644 --- a/lib/Dialect/AIE/IR/AIEDialect.cpp +++ b/lib/Dialect/AIE/IR/AIEDialect.cpp @@ -90,7 +90,7 @@ const AIETargetModel &getTargetModel(Operation *op) { // Walk the operation hierarchy until we find a containing TileElement. // If no parent is a TileElement, then return null. static TileElement getParentTileElement(Operation *op) { - auto parent = op->getParentOp(); + auto *parent = op->getParentOp(); while (!llvm::isa_and_nonnull(parent)) { if (auto element = llvm::dyn_cast(parent)) return element; @@ -105,7 +105,7 @@ struct UsesAreAccessable { auto thisID = thisElement.getTileID(); auto users = op->getResult(0).getUsers(); const auto &targetModel = getTargetModel(op); - for (auto user : users) { + for (auto *user : users) { // AIE.useLock may be used in a device to set the lock's default value // Allow in a toplevel module for backward compatibility if (llvm::isa_and_nonnull(user->getParentOp())) @@ -660,8 +660,8 @@ ObjectFifoCreateOp ObjectFifoRegisterExternalBuffersOp::getObjectFifo() { Operation *parent = getOperation(); while ((parent = parent->getParentOp())) { if (parent->hasTrait()) { - if (auto st = SymbolTable::lookupSymbolIn(parent, getObjFifoName()); - st && isa(st)) + if (auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName()); + isa_and_nonnull(st)) return dyn_cast(st); } } @@ -705,8 +705,8 @@ ObjectFifoCreateOp ObjectFifoAcquireOp::getObjectFifo() { Operation *parent = getOperation(); while ((parent = parent->getParentOp())) { if (parent->hasTrait()) { - if (auto st = SymbolTable::lookupSymbolIn(parent, getObjFifoName()); - st && isa(st)) + if (auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName()); + isa_and_nonnull(st)) return dyn_cast(st); } } @@ -750,8 +750,8 @@ ObjectFifoCreateOp ObjectFifoReleaseOp::getObjectFifo() { Operation *parent = getOperation(); while ((parent = parent->getParentOp())) { if (parent->hasTrait()) { - if (auto st = SymbolTable::lookupSymbolIn(parent, getObjFifoName()); - st && isa(st)) + if (auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName()); + isa_and_nonnull(st)) return dyn_cast(st); } } @@ -795,8 +795,8 @@ ObjectFifoCreateOp ObjectFifoRegisterProcessOp::getObjectFifo() { Operation *parent = getOperation(); while ((parent = parent->getParentOp())) { if (parent->hasTrait()) { - if (auto st = SymbolTable::lookupSymbolIn(parent, getObjFifoName()); - st && isa(st)) + if (auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName()); + isa_and_nonnull(st)) return dyn_cast(st); } } @@ -934,10 +934,15 @@ LogicalResult SwitchboxOp::verify() { Port dest = {connectOp.getDestBundle(), connectOp.destIndex()}; if (destset.count(dest)) { - return connectOp.emitOpError("targets same destination ") - << to_string(dest) << " as another connect operation (from " - << to_string(source) - << ", tile: " << to_string(this->getTileOp().getTileID()) << ")"; + return connectOp.emitOpError() + << "; connecting " << to_string(source) << " to " + << to_string(dest) << " on " + << to_string(this->getTileOp().getTileID()) + << " targets same dst as another connect op; existing " + "destinations: " + << llvm::join(llvm::map_range( + destset, [](auto &p) { return to_string(p); }), + ", "); } destset.insert(dest); @@ -1008,7 +1013,7 @@ LogicalResult SwitchboxOp::verify() { } else if (auto amselOp = dyn_cast(ops)) { std::vector mstrs; std::vector slvs; - for (auto user : amselOp.getResult().getUsers()) { + for (auto *user : amselOp.getResult().getUsers()) { if (auto s = dyn_cast(user)) { auto pktRules = dyn_cast(s->getParentOp()); slvs.push_back(pktRules); @@ -1291,7 +1296,7 @@ LogicalResult MemTileDMAOp::verify() { if (block->empty()) continue; auto successors = block->getTerminator()->getSuccessors(); - for (auto i : successors) { + for (auto *i : successors) { if (!reachable.contains(i)) { reachable.insert(i); worklist.push_back(i); @@ -1463,7 +1468,7 @@ LogicalResult LockOp::verify() { struct UsesOneLockInDMABlock { static LogicalResult verifyTrait(Operation *op) { - auto block = op->getBlock(); + auto *block = op->getBlock(); int lockID = -1; for (auto op : block->getOps()) { if (auto lock = dyn_cast(op.getLock().getDefiningOp()); @@ -1479,7 +1484,7 @@ struct UsesOneLockInDMABlock { struct AcquireReleaseOneStateInDMABlock { static LogicalResult verifyTrait(Operation *op) { - auto block = op->getBlock(); + auto *block = op->getBlock(); int acqValue = -1, relValue = -1; for (auto op : block->getOps()) { if (op.acquire() || op.acquireGE()) { diff --git a/lib/Dialect/AIE/Transforms/AIEPathFinder.cpp b/lib/Dialect/AIE/Transforms/AIEPathFinder.cpp index 5772d8d8bb..cdd61b410b 100644 --- a/lib/Dialect/AIE/Transforms/AIEPathFinder.cpp +++ b/lib/Dialect/AIE/Transforms/AIEPathFinder.cpp @@ -176,34 +176,40 @@ void Pathfinder::initialize(int maxCol, int maxRow, SwitchboxNode &thisNode = grid.at({col, row}); if (row > 0) { // if not in row 0 add channel to North/South SwitchboxNode &southernNeighbor = grid.at({col, row - 1}); - if (uint32_t maxCapacity = targetModel.getNumSourceSwitchboxConnections( - col, row, WireBundle::South)) { - edges.emplace_back(southernNeighbor, thisNode, WireBundle::North, - maxCapacity); - (void)graph.connect(southernNeighbor, thisNode, edges.back()); - } + // get the number of outgoing connections on the south side - outgoing + // because these correspond to rhs of a connect op if (uint32_t maxCapacity = targetModel.getNumDestSwitchboxConnections( col, row, WireBundle::South)) { edges.emplace_back(thisNode, southernNeighbor, WireBundle::South, maxCapacity); (void)graph.connect(thisNode, southernNeighbor, edges.back()); } + // get the number of incoming connections on the south side - incoming + // because they correspond to connections on the southside that are then + // routed using internal connect ops through the switchbox (i.e., lhs of + // connect ops) + if (uint32_t maxCapacity = targetModel.getNumSourceSwitchboxConnections( + col, row, WireBundle::South)) { + edges.emplace_back(southernNeighbor, thisNode, WireBundle::North, + maxCapacity); + (void)graph.connect(southernNeighbor, thisNode, edges.back()); + } } if (col > 0) { // if not in col 0 add channel to East/West SwitchboxNode &westernNeighbor = grid.at({col - 1, row}); - if (uint32_t maxCapacity = targetModel.getNumSourceSwitchboxConnections( - col, row, WireBundle::West)) { - edges.emplace_back(westernNeighbor, thisNode, WireBundle::East, - maxCapacity); - (void)graph.connect(westernNeighbor, thisNode, edges.back()); - } if (uint32_t maxCapacity = targetModel.getNumDestSwitchboxConnections( col, row, WireBundle::West)) { edges.emplace_back(thisNode, westernNeighbor, WireBundle::West, maxCapacity); (void)graph.connect(thisNode, westernNeighbor, edges.back()); } + if (uint32_t maxCapacity = targetModel.getNumSourceSwitchboxConnections( + col, row, WireBundle::West)) { + edges.emplace_back(westernNeighbor, thisNode, WireBundle::East, + maxCapacity); + (void)graph.connect(westernNeighbor, thisNode, edges.back()); + } } } } @@ -221,7 +227,7 @@ void Pathfinder::addFlow(TileID srcCoords, Port srcPort, TileID dstCoords, existingSrc->row == srcCoords.row && existingPort == srcPort) { // find the vertex corresponding to the destination - auto matchingSb = std::find_if( + auto *matchingSb = std::find_if( graph.begin(), graph.end(), [&](const SwitchboxNode *sb) { return sb->col == dstCoords.col && sb->row == dstCoords.row; }); @@ -232,12 +238,12 @@ void Pathfinder::addFlow(TileID srcCoords, Port srcPort, TileID dstCoords, } // If no existing flow was found with this source, create a new flow. - auto matchingSrcSb = + auto *matchingSrcSb = std::find_if(graph.begin(), graph.end(), [&](const SwitchboxNode *sb) { return sb->col == srcCoords.col && sb->row == srcCoords.row; }); assert(matchingSrcSb != graph.end() && "didn't find flow source"); - auto matchingDstSb = + auto *matchingDstSb = std::find_if(graph.begin(), graph.end(), [&](const SwitchboxNode *sb) { return sb->col == dstCoords.col && sb->row == dstCoords.row; }); @@ -434,10 +440,11 @@ Pathfinder::findPaths(const int maxIterations) { // find the edge from the pred to curr by searching incident edges SmallVector channels; graph.findIncomingEdgesToNode(*curr, channels); - auto matchingCh = std::find_if( + auto *matchingCh = std::find_if( channels.begin(), channels.end(), [&](ChannelEdge *ch) { return ch->src == *preds[curr]; }); assert(matchingCh != channels.end() && "couldn't find ch"); + // incoming edge ChannelEdge *ch = *matchingCh; // don't use fixed channels diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 7b1878f9cc..d55dd2bc53 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -85,6 +85,8 @@ if (AIE_ENABLE_PYTHON_PASSES) # Missed one... get_target_property(RegisterEverythingSources MLIRPythonExtension.RegisterEverything INTERFACE_SOURCES) + set(PYBINDINGS_SRC "${MLIR_INSTALL_PREFIX}/src/python/MLIRPythonExtension.Core") + declare_mlir_python_extension(AIEPythonExtensions.MLIR MODULE_NAME _aie ADD_TO_PARENT AIEPythonExtensions @@ -109,6 +111,10 @@ if (AIE_ENABLE_PYTHON_PASSES) ${MLIR_TRANSLATION_LIBS} ${MLIR_UPSTREAM_CAPI_LIBS} ) + target_include_directories( + AIEPythonExtensions.MLIR + INTERFACE $ + ) # Kill all of the dependencies (mlir_python_DEPENDS) on cpp sources in the dialect build targets # so the C extension modules don't get built but the Python sources do get installed. diff --git a/python/PybindTypes.cpp b/python/PybindTypes.cpp index ceb50f30a2..7ca0ea9372 100644 --- a/python/PybindTypes.cpp +++ b/python/PybindTypes.cpp @@ -6,15 +6,18 @@ //===----------------------------------------------------------------------===// #include "PybindTypes.h" +#include "IRModule.h" #include "aie/Dialect/AIE/IR/AIEDialect.h" #include "aie/Dialect/AIE/Transforms/AIEPathFinder.h" #include "mlir/Bindings/Python/PybindAdaptors.h" +#include "mlir/CAPI/IR.h" #include using namespace mlir; +using namespace mlir::python; using namespace mlir::python::adaptors; using namespace xilinx::AIE; @@ -22,6 +25,20 @@ namespace py = pybind11; PYBIND11_MAKE_OPAQUE(std::set); +PyConnectOp &PyConnectOp::forOperation(ConnectOp connectOp) { + return static_cast( + PyOperation::forOperation(DefaultingPyMlirContext::resolve().getRef(), + wrap(connectOp)) + ->getOperation()); +} + +PySwitchboxOp &PySwitchboxOp::forOperation(SwitchboxOp switchboxOp) { + return static_cast( + PyOperation::forOperation(DefaultingPyMlirContext::resolve().getRef(), + wrap(switchboxOp.getOperation())) + ->getOperation()); +} + void xilinx::AIE::bindTypes(py::module_ &m) { // By default, stl containers aren't passed by reference but passed by value, // so they can't be mutated. @@ -79,6 +96,7 @@ void xilinx::AIE::bindTypes(py::module_ &m) { py::class_(m, "Switchbox") .def(py::init()) + .def(py::init()) .def(py::init()) // Implements __hash__ (magic?) .def(py::hash(py::self)) @@ -128,4 +146,26 @@ void xilinx::AIE::bindTypes(py::module_ &m) { .def("get_num_dest_switchbox_connections", &AIETargetModel::getNumDestSwitchboxConnections, py::return_value_policy::reference); + + py::class_(m, "SwitchboxOp") + .def("get_tileid", [](const PySwitchboxOp &p) { + return static_cast(unwrap(p.get())).getTileID(); + }); + + py::class_(m, "ConnectOp") + .def("get_src_port", + [](const PyConnectOp &p) { + auto op = static_cast(unwrap(p.get())); + return Port{op.getSourceBundle(), op.getSourceChannel()}; + }) + .def("get_dst_port", + [](const PyConnectOp &p) { + auto op = static_cast(unwrap(p.get())); + return Port{op.getDestBundle(), op.getDestChannel()}; + }) + .def("get_switchbox", [](const PyConnectOp &p) { + SwitchboxOp switchboxOp = static_cast(unwrap(p.get())) + ->getParentOfType(); + return PySwitchboxOp::forOperation(switchboxOp); + }); } diff --git a/python/PybindTypes.h b/python/PybindTypes.h index 79daa7c345..1bd4f8fd52 100644 --- a/python/PybindTypes.h +++ b/python/PybindTypes.h @@ -10,6 +10,9 @@ #include "aie/Dialect/AIE/Transforms/AIEPathFinder.h" +#include "IRModule.h" +#include "mlir/CAPI/IR.h" + #include namespace py = pybind11; @@ -38,6 +41,23 @@ using Flow = struct Flow { void bindTypes(py::module_ &m); +struct PyConnectOp : mlir::python::PyOperation { + ~PyConnectOp() override { + // this prevents pyoperation destrutor from being called + // which doesn't work anyway because the pointer doesn't get cast back + // correctly to Operation*. + setInvalid(); + } + using PyOperation::PyOperation; + static PyConnectOp &forOperation(ConnectOp connectOp); +}; + +struct PySwitchboxOp : mlir::python::PyOperation { + ~PySwitchboxOp() override { setInvalid(); } + using PyOperation::PyOperation; + static PySwitchboxOp &forOperation(SwitchboxOp switchboxOp); +}; + } // namespace xilinx::AIE #endif // AIE_PYBINDTYPES_H diff --git a/python/PythonPass.cpp b/python/PythonPass.cpp index 970d46a223..0c4cb75a9d 100644 --- a/python/PythonPass.cpp +++ b/python/PythonPass.cpp @@ -16,4 +16,4 @@ MlirPass mlirPythonCapsuleToPass(PyObject *capsule) { void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_PASS); MlirPass pass = {ptr}; return pass; -} \ No newline at end of file +} diff --git a/python/RouterPass.cpp b/python/RouterPass.cpp index 6ea1a22e0b..ba5e2ddfbc 100644 --- a/python/RouterPass.cpp +++ b/python/RouterPass.cpp @@ -13,11 +13,11 @@ #include "aie/Dialect/AIE/Transforms/AIEPathFinder.h" #include "mlir/Bindings/Python/PybindAdaptors.h" -#include "mlir/CAPI/IR.h" #include "mlir/CAPI/Pass.h" #include "mlir/Pass/Pass.h" using namespace mlir; +using namespace mlir::python; using namespace mlir::python::adaptors; using namespace xilinx::AIE; @@ -51,11 +51,8 @@ class PythonRouter : public Router { } bool addFixedConnection(ConnectOp connectOp) override { - auto sb = connectOp->getParentOfType(); - if (sb.getTileOp().isShimNOCTile()) - return true; - - return router.attr("add_fixed_connection")(wrap(connectOp)).cast(); + auto pyConnectOp = PyConnectOp::forOperation(connectOp); + return router.attr("add_fixed_connection")(pyConnectOp).cast(); } std::optional> diff --git a/python/util.py b/python/util.py index deca1e39e6..a805033df2 100644 --- a/python/util.py +++ b/python/util.py @@ -4,8 +4,7 @@ from collections import defaultdict from contextlib import ExitStack, contextmanager from dataclasses import dataclass -from pprint import pprint -from typing import List, Tuple, Dict +from typing import List, Tuple, Dict, Set from typing import Optional from typing import Union @@ -144,22 +143,28 @@ def build_graph(max_cols, max_rows, target_model): DG.add_node(this_switchbox) if r > 0: southern_neighbor = Switchbox(c, r - 1) - if max_capacity := target_model.get_num_source_switchbox_connections( + # Get the number of outgoing connections on the south side - outgoing + # because these correspond to rhs of a connect op. + if max_capacity := target_model.get_num_dest_switchbox_connections( c, r, WireBundle.South ): DG.add_edge( - southern_neighbor, this_switchbox, - bundle=WireBundle.North, + southern_neighbor, + bundle=WireBundle.South, capacity=max_capacity, ) - if max_capacity := target_model.get_num_dest_switchbox_connections( + # Get the number of incoming connections on the south side - incoming + # because they correspond to connections on the southside that are then + # routed using internal connect ops through the switchbox (i.e., lhs of + # connect ops). + if max_capacity := target_model.get_num_source_switchbox_connections( c, r, WireBundle.South ): DG.add_edge( - this_switchbox, southern_neighbor, - bundle=WireBundle.South, + this_switchbox, + bundle=WireBundle.North, capacity=max_capacity, ) if c > 0: @@ -214,6 +219,14 @@ def route_using_cp( for (src, tgt), flow_var in zip(flows, flat_flow_vars): src, tgt = src.sb, tgt.sb + # flow must leave src, and must not enter src + model.Add(sum(flow_var[src, j] for j in DG.neighbors(src)) == 1) + model.Add(sum(flow_var[i, src] for i in DG.neighbors(src)) == 0) + + # flow must enter tgt, and must not leave tgt + model.Add(sum(flow_var[tgt, j] for j in DG.neighbors(tgt)) == 0) + model.Add(sum(flow_var[i, tgt] for i in DG.neighbors(tgt)) == 1) + for n in DG.nodes: if n in {src, tgt}: continue @@ -224,15 +237,6 @@ def route_using_cp( == sum(flow_var[e] for e in DG.out_edges(nbunch=n)) ) - # flow must leave src, and must not enter src - model.Add(sum(flow_var[src, j] for j in DG.neighbors(src)) == 1) - model.Add(sum(flow_var[i, src] for i in DG.neighbors(src)) == 0) - - # flow must enter tgt, and must not leave tgt - model.Add(sum(flow_var[tgt, j] for j in DG.neighbors(tgt)) == 0) - model.Add(sum(flow_var[i, tgt] for i in DG.neighbors(tgt)) == 1) - - # Create demand variables total_demand = { (i, j): model.NewIntVar(0, len(flat_flow_vars), "") for i, j in DG.edges } @@ -241,17 +245,22 @@ def route_using_cp( } used_edges = {(i, j): model.NewIntVar(0, 1, "") for i, j in DG.edges} - # Add demand/flow relationship for i, j, attrs in DG.edges(data=True): model.Add(total_demand[i, j] == sum(f[i, j] for f in flat_flow_vars)) model.Add(total_demand[i, j] <= attrs["capacity"]) if min_edges: + # counts whether an edge is used by any flow model.AddMaxEquality(used_edges[i, j], [f[i, j] for f in flat_flow_vars]) else: + # counts the number of overlapping flows overlapping_flows = {} for k, f1 in enumerate(flat_flow_vars): for l, f2 in enumerate(flat_flow_vars[k + 1 :], start=k + 1): + # for each pair of flows k, l (at edge i,j) + # overlapping_flows[k, l] is 1 if both flows use edge i,j + # note this could also by a MinEquality i.e., + # model.AddMinEquality(overlapping_flows[k, l], [f1[i, j], f2[i, j]]) overlapping_flows[k, l] = model.NewIntVar( 0, len(flat_flow_vars), "" ) @@ -259,6 +268,9 @@ def route_using_cp( overlapping_flows[k, l], [f1[i, j], f2[i, j]] ) + # overlapping demands counts up all overlapping flows on the edge i,j + # by summing across all overlapping_flows[k, j], which as written above, count whether flows k, j + # overlap on edge i,j model.Add( overlapping_demands[i, j] == sum( @@ -280,7 +292,10 @@ def route_using_cp( flow_paths = {} for flow, flow_varss in flow_vars.items(): flow_paths[flow] = [ - (i, j) for i, j in DG.edges if solver.Value(flow_varss[i, j]) > 0.5 + # solver.Value > 0.5 means the edge is used + (i, j) + for i, j in DG.edges + if solver.Value(flow_varss[i, j]) > 0.5 ] return flow_paths @@ -296,19 +311,24 @@ def route_using_ilp( import gurobipy as gp from gurobipy import GRB - # Create model object m = gp.Model() m.setParam("TimeLimit", timeout) - # Create variable for each edge, for each path flow_vars = { flow: m.addVars(DG.edges, vtype=GRB.BINARY, name="flow") for flow in flows } flat_flow_vars = list(flow_vars.values()) - # Add flow-balance constraints at all nodes (besides sources and targets) for (src, tgt), flow_var in zip(flows, flat_flow_vars): src, tgt = src.sb, tgt.sb + # flow must leave src, and must not enter src + m.addConstr(gp.quicksum(flow_var[src, j] for j in DG.neighbors(src)) == 1) + m.addConstr(gp.quicksum(flow_var[i, src] for i in DG.neighbors(src)) == 0) + + # flow must enter tgt, and must not leave tgt + m.addConstr(gp.quicksum(flow_var[tgt, j] for j in DG.neighbors(tgt)) == 0) + m.addConstr(gp.quicksum(flow_var[i, tgt] for i in DG.neighbors(tgt)) == 1) + for n in DG.nodes: if n in {src, tgt}: continue @@ -319,14 +339,6 @@ def route_using_ilp( == gp.quicksum(flow_var[e] for e in DG.out_edges(nbunch=n)) ) - # flow must leave src, and must not enter src - m.addConstr(gp.quicksum(flow_var[src, j] for j in DG.neighbors(src)) == 1) - m.addConstr(gp.quicksum(flow_var[i, src] for i in DG.neighbors(src)) == 0) - - # flow must enter tgt, and must not leave tgt - m.addConstr(gp.quicksum(flow_var[tgt, j] for j in DG.neighbors(tgt)) == 0) - m.addConstr(gp.quicksum(flow_var[i, tgt] for i in DG.neighbors(tgt)) == 1) - # Create demand variables total_demand = m.addVars(DG.edges) overlapping_demands = m.addVars(DG.edges) @@ -335,6 +347,7 @@ def route_using_ilp( for i, j, attrs in DG.edges(data=True): m.addConstr(total_demand[i, j] == gp.quicksum(f[i, j] for f in flat_flow_vars)) m.addConstr(total_demand[i, j] <= attrs["capacity"]) + # See above for this counts up overlapping demands (gurobi just has a nicer API). m.addConstr( overlapping_demands[i, j] == gp.quicksum( @@ -354,8 +367,12 @@ def route_using_ilp( # Solve m.optimize() + if m.Status == GRB.INFEASIBLE: + raise RuntimeError("Couldn't route.") + flow_paths = {} for flow, flow_varss in flow_vars.items(): + # x > 0.5 means the edge is used flow_paths[flow] = [(i, j) for i, j in DG.edges if flow_varss[i, j].x > 0.5] return flow_paths @@ -398,54 +415,69 @@ def plot_src_paths(DG, flow_paths): plot_paths(DG, src, paths) -def get_routing_solution(DG, flow_paths): +MAX_NUM_CHANNELS = 12 + + +def get_routing_solution(DG, flow_paths, used_channels): from ._mlir_libs._aie_python_passes import ( SwitchSetting, Port, get_connecting_bundle, ) + def get_pred(path, curr_sb): + path_subgraph = DG.edge_subgraph(path) + pred_sb = list(path_subgraph.predecessors(curr_sb)) + assert len(pred_sb) == 1 + pred_sb = pred_sb[0] + incoming_edge = pred_sb, curr_sb + # outgoing bundle of pred, corresponding to rhs of connect op + outgoing_bundle = path_subgraph.get_edge_data(*incoming_edge)["bundle"] + return pred_sb, outgoing_bundle + flow_dsts = defaultdict(list) for flow, path in flow_paths.items(): src, tgt = flow flow_dsts[src].append((tgt, path)) + # endpoints function as "fixed connections" i.e., already + # assigned channels on some bundle + used_channels[tgt.sb, tgt.port.bundle].add(tgt.port.channel) - # I don't know what's going on here but AIECreatePathFindFlows has some assumptions - # hard-coded about matching channels on both the source and target port of a connection. - # So keep track on a "per-edge" basis and use the same channel for both port. - used_channel = defaultdict(lambda: 0) + def get_next_avail_channel(sb, bundle): + i = 0 + while i in used_channels[sb, bundle]: + i += 1 + used_channels[sb, bundle].add(i) + return i routing_solution = {} for src, dsts in flow_dsts.items(): switch_settings = defaultdict(SwitchSetting) - switch_settings[src.sb].src = src.port processed = {src.sb} # Trace backwards until a vertex already processed is reached for end_point, path in dsts: - path_subgraph = DG.edge_subgraph(path) curr_sb = end_point.sb + pred_sb, _ = get_pred(path, curr_sb) switch_settings[curr_sb].dsts.add(end_point.port) while curr_sb not in processed: - pred_sb = list(path_subgraph.predecessors(curr_sb)) - assert len(pred_sb) == 1 - pred_sb = pred_sb[0] - edge = pred_sb, curr_sb - bundle = path_subgraph.get_edge_data(*edge)["bundle"] - - # add the entrance port for this Switchbox - switch_settings[curr_sb].src = Port( - get_connecting_bundle(bundle), used_channel[edge] + pred_sb, outgoing_bundle = get_pred(path, curr_sb) + # connecting bundle on curr, lhs of connect op + incoming_bundle = get_connecting_bundle(outgoing_bundle) + # next avail channel on outgoing side on pred + matching_channel = get_next_avail_channel(pred_sb, outgoing_bundle) + + switch_settings[curr_sb].src = Port(incoming_bundle, matching_channel) + switch_settings[pred_sb].dsts.add( + Port(outgoing_bundle, matching_channel) ) - # add the current Switchbox to the map of the predecessor - switch_settings[pred_sb].dsts.add(Port(bundle, used_channel[edge])) - used_channel[edge] += 1 processed.add(curr_sb) curr_sb = pred_sb - routing_solution[src] = dict(switch_settings) + switch_settings[src.sb].src = src.port + routing_solution[src] = list(reversed(switch_settings.items())) return routing_solution @@ -474,46 +506,83 @@ class Router: # at module load time. target_model: "AIETargetModel" flows: List[Tuple["PathEndPoint", "PathEndPoint"]] - fixed_connections: List[Tuple["TileID", "Port"]] + used_channels: Dict[Tuple["Switchbox", "Switchbox"], Set[int]] routing_solution: Dict["PathEndPoint", "SwitchSettings"] def __init__(self, use_gurobi=False, timeout=600): self.flows = [] - self.fixed_connections = [] self.routing_solution = None self.use_gurobi = use_gurobi or pythonize_bool( os.getenv("ROUTER_USE_GUROBI", "False") ) self.timeout = timeout + self.used_channels = defaultdict(set) def initialize(self, max_col, max_row, target_model): self.max_col = max_col self.max_row = max_row self.target_model = target_model + self.DG = build_graph(self.max_col, self.max_row, self.target_model) def add_flow(self, src: "PathEndPoint", tgt: "PathEndPoint"): self.flows.append((src, tgt)) def add_fixed_connection(self, connect_op): - raise NotImplementedError("adding fixed connections not implemented yet.") + from ._mlir_libs._aie_python_passes import get_connecting_bundle + + tileid = connect_op.get_switchbox().get_tileid() + lhs_port = connect_op.get_src_port() + rhs_port = connect_op.get_dst_port() + + # find the correct Channel and indicate the fixed direction + + # outgoing connection + matching_outgoing_edges = [ + (u, v, e) + for u, v, e in self.DG.edges(data=True) + if e["bundle"] == rhs_port.bundle + and u == tileid # i.e., this tile is the source + ] + if len(matching_outgoing_edges): + assert len(matching_outgoing_edges) == 1 + u, v, e = matching_outgoing_edges[0] + e["capacity"] -= 1 + self.used_channels[u, rhs_port.bundle].add(rhs_port.channel) + return True + + # incoming connection + matching_incoming_edges = [ + (u, v, e) + for u, v, e in self.DG.edges(data=True) + if e["bundle"] == get_connecting_bundle(lhs_port.bundle) + and v == tileid # i.e., this tile is the target + ] + if len(matching_incoming_edges): + assert len(matching_incoming_edges) == 1 + u, v, e = matching_incoming_edges[0] + e["capacity"] -= 1 + # this is where the assumption that connection ports across + # tiles use the same channel comes in + assert e["bundle"] == get_connecting_bundle(lhs_port.bundle) + self.used_channels[u, e["bundle"]].add(lhs_port.channel) + return True + + return False def find_paths(self): if self.routing_solution is None: - DG = build_graph(self.max_col, self.max_row, self.target_model) if self.use_gurobi: - flow_paths = route_using_ilp(DG, self.flows, timeout=self.timeout) + flow_paths = route_using_ilp(self.DG, self.flows, timeout=self.timeout) else: flow_paths = route_using_cp( - DG, self.flows, num_workers=10, timeout=self.timeout + self.DG, self.flows, num_workers=10, timeout=self.timeout ) - self.routing_solution = get_routing_solution(DG, flow_paths) - - for pe, sws in self.routing_solution.items(): - print(pe) - pprint(sws, indent=2) + self.routing_solution = get_routing_solution( + self.DG, flow_paths, self.used_channels + ) - return self.routing_solution + return {k: dict(v) for k, v in self.routing_solution.items()} def is_legal(self): return True diff --git a/test/python/python_passes.py b/test/python/python_passes.py index b9a0f50995..1adafa4705 100644 --- a/test/python/python_passes.py +++ b/test/python/python_passes.py @@ -5,6 +5,7 @@ # REQUIRES: python_passes from pathlib import Path +from textwrap import dedent # noinspection PyUnresolvedReferences import aie.dialects.aie @@ -28,13 +29,56 @@ def run(f): THIS_FILE = __file__ +# CHECK-LABEL: TEST: test_broadcast +@run +def test_broadcast(): + with open(Path(THIS_FILE).parent.parent / "create-flows" / "broadcast.mlir") as f: + mlir_module = Module.parse(f.read()) + r = Router(timeout=TIMEOUT) + pass_ = create_python_router_pass(r) + pm = PassManager() + pass_manager_add_owned_pass(pm, pass_) + pm.add("aie-find-flows") + + device = mlir_module.body.operations[0] + pm.run(device.operation) + + # CHECK: %[[T03:.*]] = AIE.tile(0, 3) + # CHECK: %[[T02:.*]] = AIE.tile(0, 2) + # CHECK: %[[T00:.*]] = AIE.tile(0, 0) + # CHECK: %[[T13:.*]] = AIE.tile(1, 3) + # CHECK: %[[T11:.*]] = AIE.tile(1, 1) + # CHECK: %[[T10:.*]] = AIE.tile(1, 0) + # CHECK: %[[T20:.*]] = AIE.tile(2, 0) + # CHECK: %[[T30:.*]] = AIE.tile(3, 0) + # CHECK: %[[T22:.*]] = AIE.tile(2, 2) + # CHECK: %[[T31:.*]] = AIE.tile(3, 1) + # CHECK: %[[T60:.*]] = AIE.tile(6, 0) + # CHECK: %[[T70:.*]] = AIE.tile(7, 0) + # CHECK: %[[T71:.*]] = AIE.tile(7, 1) + # CHECK: %[[T72:.*]] = AIE.tile(7, 2) + # CHECK: %[[T73:.*]] = AIE.tile(7, 3) + # CHECK: %[[T80:.*]] = AIE.tile(8, 0) + # CHECK: %[[T82:.*]] = AIE.tile(8, 2) + # CHECK: %[[T83:.*]] = AIE.tile(8, 3) + # + # CHECK: AIE.flow(%[[T20]], DMA : 0, %[[T71]], DMA : 0) + # CHECK: AIE.flow(%[[T20]], DMA : 0, %[[T31]], DMA : 0) + # CHECK: AIE.flow(%[[T20]], DMA : 0, %[[T82]], DMA : 0) + # CHECK: AIE.flow(%[[T20]], DMA : 0, %[[T13]], DMA : 0) + # CHECK: AIE.flow(%[[T60]], DMA : 0, %[[T83]], DMA : 1) + # CHECK: AIE.flow(%[[T60]], DMA : 0, %[[T22]], DMA : 1) + # CHECK: AIE.flow(%[[T60]], DMA : 0, %[[T02]], DMA : 1) + # CHECK: AIE.flow(%[[T60]], DMA : 0, %[[T31]], DMA : 1) + print(mlir_module) + # CHECK-LABEL: TEST: test_flow_test_1 @run def test_flow_test_1(): with open(Path(THIS_FILE).parent.parent / "create-flows" / "flow_test_1.mlir") as f: mlir_module = Module.parse(f.read()) - r = Router(timeout=10) + r = Router(timeout=TIMEOUT) pass_ = create_python_router_pass(r) pm = PassManager() pass_manager_add_owned_pass(pm, pass_) @@ -353,6 +397,44 @@ def test_memtile_routing_constraints(): print(mlir_module) +# CHECK-LABEL: TEST: test_mmult +@run +def test_mmult(): + with open(Path(THIS_FILE).parent.parent / "create-flows" / "mmult.mlir") as f: + mlir_module = Module.parse(f.read()) + r = Router(timeout=TIMEOUT) + pass_ = create_python_router_pass(r) + pm = PassManager() + pass_manager_add_owned_pass(pm, pass_) + pm.add("aie-find-flows") + + device = mlir_module.body.operations[0] + pm.run(device.operation) + + # CHEC: %[[T1:.*]] = AIE.tile(7, 0) + # CHEC: %[[T3:.*]] = AIE.tile(8, 3) + # CHEC: %[[T15:.*]] = AIE.tile(6, 0) + # CHEC: %[[T17:.*]] = AIE.tile(7, 3) + # CHEC: %[[T29:.*]] = AIE.tile(3, 0) + # CHEC: %[[T31:.*]] = AIE.tile(8, 2) + # CHEC: %[[T43:.*]] = AIE.tile(2, 0) + # CHEC: %[[T45:.*]] = AIE.tile(7, 2) + + # CHEC: AIE.flow(%[[T1]], DMA : 0, %[[T3]], DMA : 0) + # CHEC: AIE.flow(%[[T1]], DMA : 1, %[[T3]], DMA : 1) + # CHEC: AIE.flow(%[[T3]], DMA : 0, %[[T29]], DMA : 1) + # CHEC: AIE.flow(%[[T15]], DMA : 0, %[[T17]], DMA : 0) + # CHEC: AIE.flow(%[[T15]], DMA : 1, %[[T17]], DMA : 1) + # CHEC: AIE.flow(%[[T17]], DMA : 0, %[[T29]], DMA : 0) + # CHEC: AIE.flow(%[[T29]], DMA : 0, %[[T31]], DMA : 0) + # CHEC: AIE.flow(%[[T29]], DMA : 1, %[[T31]], DMA : 1) + # CHEC: AIE.flow(%[[T31]], DMA : 0, %[[T43]], DMA : 1) + # CHEC: AIE.flow(%[[T43]], DMA : 0, %[[T45]], DMA : 0) + # CHEC: AIE.flow(%[[T43]], DMA : 1, %[[T45]], DMA : 1) + # CHEC: AIE.flow(%[[T45]], DMA : 0, %[[T43]], DMA : 0) + print(mlir_module) + + # CHECK-LABEL: TEST: test_more_flows_shim @run def test_more_flows_shim(): @@ -361,7 +443,7 @@ def test_more_flows_shim(): ) as f: for mlir_module in f.read().split("// -----"): mlir_module = Module.parse(mlir_module) - r = Router(timeout=10) + r = Router(timeout=TIMEOUT) pass_ = create_python_router_pass(r) pm = PassManager() pass_manager_add_owned_pass(pm, pass_) @@ -429,7 +511,7 @@ def test_more_flows_shim(): def test_over_flows(): with open(Path(THIS_FILE).parent.parent / "create-flows" / "over_flows.mlir") as f: mlir_module = Module.parse(f.read()) - r = Router(timeout=10) + r = Router(timeout=TIMEOUT) pass_ = create_python_router_pass(r) pm = PassManager() pass_manager_add_owned_pass(pm, pass_) @@ -467,12 +549,852 @@ def test_over_flows(): print(mlir_module) +# this test just tests that there's no error for multiple connections to a single target bundle/channel +# CHECK-LABEL: TEST: test_overlap +@run +def test_overlap(): + src = dedent( + """\ + module @aie.herd_0 { + AIE.device(xcvc1902) { + %tile_3_0 = AIE.tile(3, 0) + %tile_3_1 = AIE.tile(3, 1) + %tile_6_1 = AIE.tile(6, 1) + %tile_7_3 = AIE.tile(7, 3) + %tile_8_2 = AIE.tile(8, 2) + %tile_8_3 = AIE.tile(8, 3) + %switchbox_3_0 = AIE.switchbox(%tile_3_0) { + AIE.connect + AIE.connect + AIE.connect + AIE.connect + } + AIE.flow(%tile_3_1, South : 0, %tile_8_2, DMA : 0) + AIE.flow(%tile_3_1, South : 1, %tile_8_2, DMA : 1) + AIE.flow(%tile_6_1, South : 0, %tile_7_3, DMA : 0) + AIE.flow(%tile_6_1, South : 1, %tile_7_3, DMA : 1) + } + } + """ + ) + mlir_module = Module.parse(src) + r = Router(timeout=TIMEOUT) + pass_ = create_python_router_pass(r) + pm = PassManager() + pass_manager_add_owned_pass(pm, pass_) + pm.add("aie-find-flows") + + device = mlir_module.body.operations[0] + pm.run(device.operation) + + +# CHECK-LABEL: TEST: test_routed_herd_3x1_mine_1 +@run +def test_routed_herd_3x1_mine_1(): + src = dedent( + """\ + module { + AIE.device(xcvc1902) { + %tile_0_0 = AIE.tile(0, 0) + %tile_0_1 = AIE.tile(0, 1) + %tile_0_2 = AIE.tile(0, 2) + %tile_0_3 = AIE.tile(0, 3) + %tile_0_4 = AIE.tile(0, 4) + %tile_1_0 = AIE.tile(1, 0) + %tile_1_1 = AIE.tile(1, 1) + %tile_1_2 = AIE.tile(1, 2) + %tile_1_3 = AIE.tile(1, 3) + %tile_1_4 = AIE.tile(1, 4) + %tile_2_0 = AIE.tile(2, 0) + %tile_2_1 = AIE.tile(2, 1) + %tile_2_2 = AIE.tile(2, 2) + %tile_2_3 = AIE.tile(2, 3) + %tile_2_4 = AIE.tile(2, 4) + %tile_3_0 = AIE.tile(3, 0) + %tile_3_1 = AIE.tile(3, 1) + %tile_3_2 = AIE.tile(3, 2) + %tile_3_3 = AIE.tile(3, 3) + %tile_3_4 = AIE.tile(3, 4) + %tile_4_0 = AIE.tile(4, 0) + %tile_4_1 = AIE.tile(4, 1) + %tile_4_2 = AIE.tile(4, 2) + %tile_4_3 = AIE.tile(4, 3) + %tile_4_4 = AIE.tile(4, 4) + %tile_5_0 = AIE.tile(5, 0) + %tile_5_1 = AIE.tile(5, 1) + %tile_5_2 = AIE.tile(5, 2) + %tile_5_3 = AIE.tile(5, 3) + %tile_5_4 = AIE.tile(5, 4) + %tile_6_0 = AIE.tile(6, 0) + %tile_6_1 = AIE.tile(6, 1) + %tile_6_2 = AIE.tile(6, 2) + %tile_6_3 = AIE.tile(6, 3) + %tile_6_4 = AIE.tile(6, 4) + %tile_7_0 = AIE.tile(7, 0) + %tile_7_1 = AIE.tile(7, 1) + %tile_7_2 = AIE.tile(7, 2) + %tile_7_3 = AIE.tile(7, 3) + %tile_7_4 = AIE.tile(7, 4) + %tile_8_0 = AIE.tile(8, 0) + %tile_8_1 = AIE.tile(8, 1) + %tile_8_2 = AIE.tile(8, 2) + %tile_8_3 = AIE.tile(8, 3) + %tile_8_4 = AIE.tile(8, 4) + %tile_9_0 = AIE.tile(9, 0) + %tile_9_1 = AIE.tile(9, 1) + %tile_9_2 = AIE.tile(9, 2) + %tile_9_3 = AIE.tile(9, 3) + %tile_9_4 = AIE.tile(9, 4) + %tile_10_0 = AIE.tile(10, 0) + %tile_10_1 = AIE.tile(10, 1) + %tile_10_2 = AIE.tile(10, 2) + %tile_10_3 = AIE.tile(10, 3) + %tile_10_4 = AIE.tile(10, 4) + %tile_11_0 = AIE.tile(11, 0) + %tile_11_1 = AIE.tile(11, 1) + %tile_11_2 = AIE.tile(11, 2) + %tile_11_3 = AIE.tile(11, 3) + %tile_11_4 = AIE.tile(11, 4) + %tile_12_1 = AIE.tile(12, 1) + %tile_12_2 = AIE.tile(12, 2) + %tile_12_3 = AIE.tile(12, 3) + %tile_12_4 = AIE.tile(12, 4) + %tile_18_0 = AIE.tile(18, 0) + %tile_19_0 = AIE.tile(19, 0) + + %switchbox_0_1 = AIE.switchbox(%tile_0_1) { + AIE.connect + } + %switchbox_0_2 = AIE.switchbox(%tile_0_2) { + AIE.connect + } + %switchbox_0_3 = AIE.switchbox(%tile_0_3) { + AIE.connect + AIE.connect + } + %switchbox_1_1 = AIE.switchbox(%tile_1_1) { + AIE.connect + } + %switchbox_1_2 = AIE.switchbox(%tile_1_2) { + AIE.connect + } + %switchbox_1_3 = AIE.switchbox(%tile_1_3) { + AIE.connect + } + %switchbox_1_4 = AIE.switchbox(%tile_1_4) { + AIE.connect + } + %switchbox_2_1 = AIE.switchbox(%tile_2_1) { + AIE.connect + } + %switchbox_2_2 = AIE.switchbox(%tile_2_2) { + AIE.connect + } + %switchbox_2_3 = AIE.switchbox(%tile_2_3) { + AIE.connect + } + %switchbox_2_4 = AIE.switchbox(%tile_2_4) { + AIE.connect + } + %switchbox_3_1 = AIE.switchbox(%tile_3_1) { + AIE.connect + } + %switchbox_3_2 = AIE.switchbox(%tile_3_2) { + AIE.connect + } + %switchbox_3_3 = AIE.switchbox(%tile_3_3) { + AIE.connect + } + %switchbox_3_4 = AIE.switchbox(%tile_3_4) { + } + %switchbox_4_1 = AIE.switchbox(%tile_4_1) { + AIE.connect + } + %switchbox_4_2 = AIE.switchbox(%tile_4_2) { + AIE.connect + } + %switchbox_5_1 = AIE.switchbox(%tile_5_1) { + AIE.connect + } + %switchbox_5_2 = AIE.switchbox(%tile_5_2) { + AIE.connect + } + %switchbox_5_3 = AIE.switchbox(%tile_5_3) { + AIE.connect + } + %switchbox_6_1 = AIE.switchbox(%tile_6_1) { + AIE.connect + AIE.connect + } + %switchbox_6_2 = AIE.switchbox(%tile_6_2) { + AIE.connect + AIE.connect + } + %switchbox_6_3 = AIE.switchbox(%tile_6_3) { + AIE.connect + AIE.connect + } + %switchbox_7_1 = AIE.switchbox(%tile_7_1) { + AIE.connect + AIE.connect + } + %switchbox_7_2 = AIE.switchbox(%tile_7_2) { + AIE.connect + AIE.connect + } + %switchbox_7_3 = AIE.switchbox(%tile_7_3) { + AIE.connect + AIE.connect + } + %switchbox_7_4 = AIE.switchbox(%tile_7_4) { + AIE.connect + AIE.connect + } + %switchbox_9_1 = AIE.switchbox(%tile_9_1) { + AIE.connect + } + %switchbox_9_2 = AIE.switchbox(%tile_9_2) { + AIE.connect + } + %switchbox_10_1 = AIE.switchbox(%tile_10_1) { + AIE.connect + } + %switchbox_10_2 = AIE.switchbox(%tile_10_2) { + AIE.connect + } + %switchbox_11_1 = AIE.switchbox(%tile_11_1) { + AIE.connect + AIE.connect + } + %switchbox_11_2 = AIE.switchbox(%tile_11_2) { + AIE.connect + AIE.connect + } + %switchbox_11_3 = AIE.switchbox(%tile_11_3) { + AIE.connect + AIE.connect + } + AIE.flow(%tile_6_0, DMA : 1, %tile_4_0, North : 0) + AIE.flow(%tile_7_0, DMA : 0, %tile_1_0, North : 0) + AIE.flow(%tile_7_0, DMA : 1, %tile_5_0, North : 0) + AIE.flow(%tile_19_0, DMA : 0, %tile_7_0, North : 0) + } + } + """ + ) + + mlir_module = Module.parse(src) + r = Router(timeout=TIMEOUT) + pass_ = create_python_router_pass(r) + pm = PassManager() + pass_manager_add_owned_pass(pm, pass_) + pm.add("aie-find-flows") + + device = mlir_module.body.operations[0] + pm.run(device.operation) + + # CHECK: AIE.flow(%tile_6_0, DMA : 1, %tile_4_2, DMA : 0) + # CHECK: AIE.flow(%tile_7_0, DMA : 0, %tile_0_3, DMA : 1) + # AIE.flow(%tile_7_0, DMA : 0, %tile_7_4, DMA : 1) + # CHECK: AIE.flow(%tile_7_0, DMA : 1, %tile_5_3, DMA : 0) + # CHECK: AIE.flow(%tile_19_0, DMA : 0, %tile_7_4, DMA : 0) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_routed_herd_3x1_mine_2 +@run +def test_routed_herd_3x1_mine_2(): + src = dedent( + """\ + module { + AIE.device(xcvc1902) { + %tile_0_0 = AIE.tile(0, 0) + %tile_1_0 = AIE.tile(1, 0) + %tile_2_0 = AIE.tile(2, 0) + %tile_3_0 = AIE.tile(3, 0) + %tile_4_0 = AIE.tile(4, 0) + %tile_5_0 = AIE.tile(5, 0) + %tile_6_0 = AIE.tile(6, 0) + %tile_7_0 = AIE.tile(7, 0) + %tile_8_0 = AIE.tile(8, 0) + %tile_9_0 = AIE.tile(9, 0) + %tile_10_0 = AIE.tile(10, 0) + %tile_11_0 = AIE.tile(11, 0) + %tile_18_0 = AIE.tile(18, 0) + %tile_19_0 = AIE.tile(19, 0) + %tile_0_1 = AIE.tile(0, 1) + %tile_0_2 = AIE.tile(0, 2) + %tile_0_3 = AIE.tile(0, 3) + %tile_0_4 = AIE.tile(0, 4) + %tile_1_1 = AIE.tile(1, 1) + %tile_1_2 = AIE.tile(1, 2) + %tile_1_3 = AIE.tile(1, 3) + %tile_1_4 = AIE.tile(1, 4) + %tile_2_1 = AIE.tile(2, 1) + %tile_2_2 = AIE.tile(2, 2) + %tile_2_3 = AIE.tile(2, 3) + %tile_2_4 = AIE.tile(2, 4) + %tile_3_1 = AIE.tile(3, 1) + %tile_3_2 = AIE.tile(3, 2) + %tile_3_3 = AIE.tile(3, 3) + %tile_3_4 = AIE.tile(3, 4) + %tile_4_1 = AIE.tile(4, 1) + %tile_4_2 = AIE.tile(4, 2) + %tile_4_3 = AIE.tile(4, 3) + %tile_4_4 = AIE.tile(4, 4) + %tile_5_1 = AIE.tile(5, 1) + %tile_5_2 = AIE.tile(5, 2) + %tile_5_3 = AIE.tile(5, 3) + %tile_5_4 = AIE.tile(5, 4) + %tile_6_1 = AIE.tile(6, 1) + %tile_6_2 = AIE.tile(6, 2) + %tile_6_3 = AIE.tile(6, 3) + %tile_6_4 = AIE.tile(6, 4) + %tile_7_1 = AIE.tile(7, 1) + %tile_7_2 = AIE.tile(7, 2) + %tile_7_3 = AIE.tile(7, 3) + %tile_7_4 = AIE.tile(7, 4) + %tile_8_1 = AIE.tile(8, 1) + %tile_8_2 = AIE.tile(8, 2) + %tile_8_3 = AIE.tile(8, 3) + %tile_8_4 = AIE.tile(8, 4) + %tile_9_1 = AIE.tile(9, 1) + %tile_9_2 = AIE.tile(9, 2) + %tile_9_3 = AIE.tile(9, 3) + %tile_9_4 = AIE.tile(9, 4) + %tile_10_1 = AIE.tile(10, 1) + %tile_10_2 = AIE.tile(10, 2) + %tile_10_3 = AIE.tile(10, 3) + %tile_10_4 = AIE.tile(10, 4) + %tile_11_1 = AIE.tile(11, 1) + %tile_11_2 = AIE.tile(11, 2) + %tile_11_3 = AIE.tile(11, 3) + %tile_11_4 = AIE.tile(11, 4) + %tile_12_1 = AIE.tile(12, 1) + %tile_12_2 = AIE.tile(12, 2) + %tile_12_3 = AIE.tile(12, 3) + %tile_12_4 = AIE.tile(12, 4) + %switchbox_0_1 = AIE.switchbox(%tile_0_1) { + AIE.connect + } + %switchbox_0_2 = AIE.switchbox(%tile_0_2) { + AIE.connect + } + %switchbox_0_3 = AIE.switchbox(%tile_0_3) { + AIE.connect + AIE.connect + } + %switchbox_0_4 = AIE.switchbox(%tile_0_4) { + } + %switchbox_1_1 = AIE.switchbox(%tile_1_1) { + AIE.connect + } + %switchbox_1_2 = AIE.switchbox(%tile_1_2) { + AIE.connect + } + %switchbox_1_3 = AIE.switchbox(%tile_1_3) { + AIE.connect + } + %switchbox_1_4 = AIE.switchbox(%tile_1_4) { + AIE.connect + } + %switchbox_2_1 = AIE.switchbox(%tile_2_1) { + AIE.connect + } + %switchbox_2_2 = AIE.switchbox(%tile_2_2) { + AIE.connect + } + %switchbox_2_3 = AIE.switchbox(%tile_2_3) { + AIE.connect + } + %switchbox_2_4 = AIE.switchbox(%tile_2_4) { + AIE.connect + } + %switchbox_3_1 = AIE.switchbox(%tile_3_1) { + AIE.connect + } + %switchbox_3_2 = AIE.switchbox(%tile_3_2) { + AIE.connect + } + %switchbox_3_3 = AIE.switchbox(%tile_3_3) { + AIE.connect + } + %switchbox_3_4 = AIE.switchbox(%tile_3_4) { + } + %switchbox_4_1 = AIE.switchbox(%tile_4_1) { + AIE.connect + } + %switchbox_4_2 = AIE.switchbox(%tile_4_2) { + AIE.connect + } + %switchbox_4_3 = AIE.switchbox(%tile_4_3) { + } + %switchbox_4_4 = AIE.switchbox(%tile_4_4) { + } + %switchbox_5_1 = AIE.switchbox(%tile_5_1) { + AIE.connect + } + %switchbox_5_2 = AIE.switchbox(%tile_5_2) { + AIE.connect + } + %switchbox_5_3 = AIE.switchbox(%tile_5_3) { + AIE.connect + } + %switchbox_5_4 = AIE.switchbox(%tile_5_4) { + } + %switchbox_6_1 = AIE.switchbox(%tile_6_1) { + AIE.connect + AIE.connect + } + %switchbox_6_2 = AIE.switchbox(%tile_6_2) { + AIE.connect + AIE.connect + } + %switchbox_6_3 = AIE.switchbox(%tile_6_3) { + AIE.connect + AIE.connect + } + %switchbox_6_4 = AIE.switchbox(%tile_6_4) { + } + %switchbox_7_1 = AIE.switchbox(%tile_7_1) { + AIE.connect + AIE.connect + } + %switchbox_7_2 = AIE.switchbox(%tile_7_2) { + AIE.connect + AIE.connect + } + %switchbox_7_3 = AIE.switchbox(%tile_7_3) { + AIE.connect + AIE.connect + } + %switchbox_7_4 = AIE.switchbox(%tile_7_4) { + AIE.connect + AIE.connect + } + %switchbox_8_1 = AIE.switchbox(%tile_8_1) { + } + %switchbox_8_2 = AIE.switchbox(%tile_8_2) { + } + %switchbox_8_3 = AIE.switchbox(%tile_8_3) { + } + %switchbox_8_4 = AIE.switchbox(%tile_8_4) { + } + %switchbox_9_1 = AIE.switchbox(%tile_9_1) { + AIE.connect + } + %switchbox_9_2 = AIE.switchbox(%tile_9_2) { + AIE.connect + } + %switchbox_9_3 = AIE.switchbox(%tile_9_3) { + } + %switchbox_9_4 = AIE.switchbox(%tile_9_4) { + } + %switchbox_10_1 = AIE.switchbox(%tile_10_1) { + AIE.connect + } + %switchbox_10_2 = AIE.switchbox(%tile_10_2) { + AIE.connect + } + %switchbox_10_3 = AIE.switchbox(%tile_10_3) { + } + %switchbox_10_4 = AIE.switchbox(%tile_10_4) { + } + %switchbox_11_1 = AIE.switchbox(%tile_11_1) { + AIE.connect + AIE.connect + } + %switchbox_11_2 = AIE.switchbox(%tile_11_2) { + AIE.connect + AIE.connect + } + %switchbox_11_3 = AIE.switchbox(%tile_11_3) { + AIE.connect + AIE.connect + } + %switchbox_11_4 = AIE.switchbox(%tile_11_4) { + } + // AIE.flow(%tile_2_0, DMA : 0, %tile_2_0, North : 0) + AIE.flow(%tile_2_0, DMA : 1, %tile_6_0, North : 1) + // AIE.flow(%tile_3_0, DMA : 0, %tile_3_0, North : 0) + AIE.flow(%tile_3_0, DMA : 1, %tile_7_0, North : 1) + AIE.flow(%tile_6_0, DMA : 0, %tile_0_0, North : 0) + AIE.flow(%tile_6_0, DMA : 1, %tile_4_0, North : 0) + AIE.flow(%tile_7_0, DMA : 0, %tile_1_0, North : 0) + AIE.flow(%tile_7_0, DMA : 1, %tile_5_0, North : 0) + // AIE.flow(%tile_10_0, DMA : 0, %tile_10_0, North : 0) + // AIE.flow(%tile_11_0, DMA : 0, %tile_11_0, North : 0) + AIE.flow(%tile_18_0, DMA : 0, %tile_6_0, North : 0) + AIE.flow(%tile_18_0, DMA : 1, %tile_9_0, North : 0) + AIE.flow(%tile_19_0, DMA : 0, %tile_7_0, North : 0) + AIE.flow(%tile_19_0, DMA : 1, %tile_11_0, North : 1) + } + } + """ + ) + + mlir_module = Module.parse(src) + r = Router(timeout=TIMEOUT) + pass_ = create_python_router_pass(r) + pm = PassManager() + pass_manager_add_owned_pass(pm, pass_) + pm.add("aie-find-flows") + + device = mlir_module.body.operations[0] + pm.run(device.operation) + # CHECK: AIE.flow(%tile_2_0, DMA : 1, %tile_6_3, DMA : 1) + # CHECK: AIE.flow(%tile_3_0, DMA : 1, %tile_7_4, DMA : 1) + # AIE.flow(%tile_3_0, DMA : 1, %tile_3_3, DMA : 0) + # CHECK: AIE.flow(%tile_6_0, DMA : 0, %tile_0_3, DMA : 0) + # CHECK: AIE.flow(%tile_6_0, DMA : 1, %tile_4_2, DMA : 0) + # CHECK: AIE.flow(%tile_7_0, DMA : 0, %tile_0_3, DMA : 1) + # CHECK: AIE.flow(%tile_7_0, DMA : 1, %tile_5_3, DMA : 0) + # CHECK: AIE.flow(%tile_18_0, DMA : 0, %tile_6_3, DMA : 0) + # CHECK: AIE.flow(%tile_18_0, DMA : 1, %tile_9_2, DMA : 0) + # CHECK: AIE.flow(%tile_19_0, DMA : 0, %tile_7_4, DMA : 0) + # CHECK: AIE.flow(%tile_19_0, DMA : 1, %tile_11_3, DMA : 1) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_routed_herd_3x2_mine_1 +@run +def test_routed_herd_3x2_mine_1(): + src = dedent( + """\ + module { + AIE.device(xcvc1902) { + %tile_0_0 = AIE.tile(0, 0) + %tile_1_0 = AIE.tile(1, 0) + %tile_2_0 = AIE.tile(2, 0) + %tile_3_0 = AIE.tile(3, 0) + %tile_4_0 = AIE.tile(4, 0) + %tile_5_0 = AIE.tile(5, 0) + %tile_6_0 = AIE.tile(6, 0) + %tile_7_0 = AIE.tile(7, 0) + %tile_8_0 = AIE.tile(8, 0) + %tile_9_0 = AIE.tile(9, 0) + %tile_10_0 = AIE.tile(10, 0) + %tile_11_0 = AIE.tile(11, 0) + %tile_18_0 = AIE.tile(18, 0) + %tile_19_0 = AIE.tile(19, 0) + %tile_0_1 = AIE.tile(0, 1) + %tile_0_2 = AIE.tile(0, 2) + %tile_0_3 = AIE.tile(0, 3) + %tile_0_4 = AIE.tile(0, 4) + %tile_0_5 = AIE.tile(0, 5) + %tile_0_6 = AIE.tile(0, 6) + %tile_0_7 = AIE.tile(0, 7) + %tile_0_8 = AIE.tile(0, 8) + %tile_1_1 = AIE.tile(1, 1) + %tile_1_2 = AIE.tile(1, 2) + %tile_1_3 = AIE.tile(1, 3) + %tile_1_4 = AIE.tile(1, 4) + %tile_1_5 = AIE.tile(1, 5) + %tile_1_6 = AIE.tile(1, 6) + %tile_1_7 = AIE.tile(1, 7) + %tile_1_8 = AIE.tile(1, 8) + %tile_2_1 = AIE.tile(2, 1) + %tile_2_2 = AIE.tile(2, 2) + %tile_2_3 = AIE.tile(2, 3) + %tile_2_4 = AIE.tile(2, 4) + %tile_2_5 = AIE.tile(2, 5) + %tile_2_6 = AIE.tile(2, 6) + %tile_2_7 = AIE.tile(2, 7) + %tile_2_8 = AIE.tile(2, 8) + %tile_3_1 = AIE.tile(3, 1) + %tile_3_2 = AIE.tile(3, 2) + %tile_3_3 = AIE.tile(3, 3) + %tile_3_4 = AIE.tile(3, 4) + %tile_3_5 = AIE.tile(3, 5) + %tile_3_6 = AIE.tile(3, 6) + %tile_3_7 = AIE.tile(3, 7) + %tile_3_8 = AIE.tile(3, 8) + %tile_4_1 = AIE.tile(4, 1) + %tile_4_2 = AIE.tile(4, 2) + %tile_4_3 = AIE.tile(4, 3) + %tile_4_4 = AIE.tile(4, 4) + %tile_4_5 = AIE.tile(4, 5) + %tile_4_6 = AIE.tile(4, 6) + %tile_4_7 = AIE.tile(4, 7) + %tile_4_8 = AIE.tile(4, 8) + %tile_5_1 = AIE.tile(5, 1) + %tile_5_2 = AIE.tile(5, 2) + %tile_5_3 = AIE.tile(5, 3) + %tile_5_4 = AIE.tile(5, 4) + %tile_5_5 = AIE.tile(5, 5) + %tile_5_6 = AIE.tile(5, 6) + %tile_5_7 = AIE.tile(5, 7) + %tile_5_8 = AIE.tile(5, 8) + %tile_6_1 = AIE.tile(6, 1) + %tile_6_2 = AIE.tile(6, 2) + %tile_6_3 = AIE.tile(6, 3) + %tile_6_4 = AIE.tile(6, 4) + %tile_6_5 = AIE.tile(6, 5) + %tile_6_6 = AIE.tile(6, 6) + %tile_6_7 = AIE.tile(6, 7) + %tile_6_8 = AIE.tile(6, 8) + %tile_7_1 = AIE.tile(7, 1) + %tile_7_2 = AIE.tile(7, 2) + %tile_7_3 = AIE.tile(7, 3) + %tile_7_4 = AIE.tile(7, 4) + %tile_7_5 = AIE.tile(7, 5) + %tile_7_6 = AIE.tile(7, 6) + %tile_7_7 = AIE.tile(7, 7) + %tile_7_8 = AIE.tile(7, 8) + %tile_8_1 = AIE.tile(8, 1) + %tile_8_2 = AIE.tile(8, 2) + %tile_8_3 = AIE.tile(8, 3) + %tile_8_4 = AIE.tile(8, 4) + %tile_8_5 = AIE.tile(8, 5) + %tile_8_6 = AIE.tile(8, 6) + %tile_8_7 = AIE.tile(8, 7) + %tile_8_8 = AIE.tile(8, 8) + %tile_9_1 = AIE.tile(9, 1) + %tile_9_2 = AIE.tile(9, 2) + %tile_9_3 = AIE.tile(9, 3) + %tile_9_4 = AIE.tile(9, 4) + %tile_9_5 = AIE.tile(9, 5) + %tile_9_6 = AIE.tile(9, 6) + %tile_9_7 = AIE.tile(9, 7) + %tile_9_8 = AIE.tile(9, 8) + %tile_10_1 = AIE.tile(10, 1) + %tile_10_2 = AIE.tile(10, 2) + %tile_10_3 = AIE.tile(10, 3) + %tile_10_4 = AIE.tile(10, 4) + %tile_10_5 = AIE.tile(10, 5) + %tile_10_6 = AIE.tile(10, 6) + %tile_10_7 = AIE.tile(10, 7) + %tile_10_8 = AIE.tile(10, 8) + %tile_11_1 = AIE.tile(11, 1) + %tile_11_2 = AIE.tile(11, 2) + %tile_11_3 = AIE.tile(11, 3) + %tile_11_4 = AIE.tile(11, 4) + %tile_11_5 = AIE.tile(11, 5) + %tile_11_6 = AIE.tile(11, 6) + %tile_11_7 = AIE.tile(11, 7) + %tile_11_8 = AIE.tile(11, 8) + %tile_12_1 = AIE.tile(12, 1) + %tile_12_2 = AIE.tile(12, 2) + %tile_12_3 = AIE.tile(12, 3) + %tile_12_4 = AIE.tile(12, 4) + %tile_12_5 = AIE.tile(12, 5) + %tile_12_6 = AIE.tile(12, 6) + %tile_12_7 = AIE.tile(12, 7) + %tile_12_8 = AIE.tile(12, 8) + %tile_13_0 = AIE.tile(13, 0) + %tile_13_1 = AIE.tile(13, 1) + %tile_13_2 = AIE.tile(13, 2) + %tile_13_3 = AIE.tile(13, 3) + %tile_13_4 = AIE.tile(13, 4) + %tile_13_5 = AIE.tile(13, 5) + %tile_13_6 = AIE.tile(13, 6) + %tile_13_7 = AIE.tile(13, 7) + %tile_13_8 = AIE.tile(13, 8) + %tile_14_1 = AIE.tile(14, 1) + %tile_14_2 = AIE.tile(14, 2) + %tile_14_3 = AIE.tile(14, 3) + %tile_14_4 = AIE.tile(14, 4) + %tile_14_5 = AIE.tile(14, 5) + %tile_14_6 = AIE.tile(14, 6) + %tile_14_7 = AIE.tile(14, 7) + %tile_14_8 = AIE.tile(14, 8) + %switchbox_0_1 = AIE.switchbox(%tile_0_1) { + } + %switchbox_0_2 = AIE.switchbox(%tile_0_2) { + } + %switchbox_0_3 = AIE.switchbox(%tile_0_3) { + } + %switchbox_0_4 = AIE.switchbox(%tile_0_4) { + } + %switchbox_1_1 = AIE.switchbox(%tile_1_1) { + } + %switchbox_1_2 = AIE.switchbox(%tile_1_2) { + } + %switchbox_1_3 = AIE.switchbox(%tile_1_3) { + } + %switchbox_1_4 = AIE.switchbox(%tile_1_4) { + } + %switchbox_2_1 = AIE.switchbox(%tile_2_1) { + } + %switchbox_2_2 = AIE.switchbox(%tile_2_2) { + } + %switchbox_2_3 = AIE.switchbox(%tile_2_3) { + } + %switchbox_2_4 = AIE.switchbox(%tile_2_4) { + AIE.connect + } + %switchbox_2_5 = AIE.switchbox(%tile_2_5) { + AIE.connect + AIE.connect + } + %switchbox_3_1 = AIE.switchbox(%tile_3_1) { + AIE.connect + AIE.connect + } + %switchbox_3_2 = AIE.switchbox(%tile_3_2) { + AIE.connect + } + %switchbox_3_3 = AIE.switchbox(%tile_3_3) { + AIE.connect + } + %switchbox_3_4 = AIE.switchbox(%tile_3_4) { + AIE.connect + } + %switchbox_3_5 = AIE.switchbox(%tile_3_5) { + AIE.connect + } + %switchbox_4_1 = AIE.switchbox(%tile_4_1) { + } + %switchbox_4_2 = AIE.switchbox(%tile_4_2) { + } + %switchbox_4_3 = AIE.switchbox(%tile_4_3) { + } + %switchbox_4_4 = AIE.switchbox(%tile_4_4) { + } + %switchbox_5_1 = AIE.switchbox(%tile_5_1) { + } + %switchbox_5_2 = AIE.switchbox(%tile_5_2) { + } + %switchbox_5_3 = AIE.switchbox(%tile_5_3) { + } + %switchbox_5_4 = AIE.switchbox(%tile_5_4) { + } + %switchbox_5_5 = AIE.switchbox(%tile_5_5) { + } + %switchbox_5_6 = AIE.switchbox(%tile_5_6) { + AIE.connect + } + %switchbox_6_1 = AIE.switchbox(%tile_6_1) { + } + %switchbox_6_2 = AIE.switchbox(%tile_6_2) { + } + %switchbox_6_3 = AIE.switchbox(%tile_6_3) { + } + %switchbox_6_4 = AIE.switchbox(%tile_6_4) { + } + %switchbox_6_5 = AIE.switchbox(%tile_6_5) { + } + %switchbox_6_6 = AIE.switchbox(%tile_6_6) { + AIE.connect + AIE.connect + } + %switchbox_7_1 = AIE.switchbox(%tile_7_1) { + } + %switchbox_7_2 = AIE.switchbox(%tile_7_2) { + } + %switchbox_7_3 = AIE.switchbox(%tile_7_3) { + AIE.connect + AIE.connect + } + %switchbox_7_4 = AIE.switchbox(%tile_7_4) { + AIE.connect + } + %switchbox_7_5 = AIE.switchbox(%tile_7_5) { + AIE.connect + } + %switchbox_7_6 = AIE.switchbox(%tile_7_6) { + AIE.connect + } + %switchbox_8_1 = AIE.switchbox(%tile_8_1) { + } + %switchbox_8_2 = AIE.switchbox(%tile_8_2) { + } + %switchbox_8_3 = AIE.switchbox(%tile_8_3) { + AIE.connect + } + %switchbox_8_4 = AIE.switchbox(%tile_8_4) { + } + %switchbox_9_1 = AIE.switchbox(%tile_9_1) { + } + %switchbox_9_2 = AIE.switchbox(%tile_9_2) { + } + %switchbox_9_3 = AIE.switchbox(%tile_9_3) { + } + %switchbox_9_4 = AIE.switchbox(%tile_9_4) { + } + %switchbox_10_1 = AIE.switchbox(%tile_10_1) { + } + %switchbox_10_2 = AIE.switchbox(%tile_10_2) { + } + %switchbox_10_3 = AIE.switchbox(%tile_10_3) { + } + %switchbox_10_4 = AIE.switchbox(%tile_10_4) { + } + %switchbox_11_1 = AIE.switchbox(%tile_11_1) { + } + %switchbox_11_2 = AIE.switchbox(%tile_11_2) { + } + %switchbox_11_3 = AIE.switchbox(%tile_11_3) { + } + %switchbox_11_4 = AIE.switchbox(%tile_11_4) { + } + %switchbox_12_1 = AIE.switchbox(%tile_12_1) { + } + %switchbox_12_2 = AIE.switchbox(%tile_12_2) { + } + %switchbox_12_3 = AIE.switchbox(%tile_12_3) { + } + %switchbox_12_4 = AIE.switchbox(%tile_12_4) { + } + %switchbox_12_5 = AIE.switchbox(%tile_12_5) { + AIE.connect + AIE.connect + } + %switchbox_13_1 = AIE.switchbox(%tile_13_1) { + AIE.connect + } + %switchbox_13_2 = AIE.switchbox(%tile_13_2) { + AIE.connect + } + %switchbox_13_3 = AIE.switchbox(%tile_13_3) { + AIE.connect + AIE.connect + } + %switchbox_13_4 = AIE.switchbox(%tile_13_4) { + AIE.connect + } + %switchbox_13_5 = AIE.switchbox(%tile_13_5) { + AIE.connect + AIE.connect + } + // AIE.flow(%tile_3_0, DMA : 0, %tile_3_0, North : 0) + AIE.flow(%tile_4_5, West : 0, %tile_6_0, DMA : 0) + AIE.flow(%tile_10_0, DMA : 0, %tile_9_3, West : 0) + AIE.flow(%tile_4_6, East : 0, %tile_2_0, DMA : 0) + AIE.flow(%tile_11_0, DMA : 0, %tile_13_0, North : 0) + AIE.flow(%tile_14_5, West : 0, %tile_18_0, DMA : 0) + } + } + + """ + ) + + mlir_module = Module.parse(src) + r = Router(timeout=TIMEOUT) + pass_ = create_python_router_pass(r) + pm = PassManager() + pass_manager_add_owned_pass(pm, pass_) + pm.add("aie-find-flows") + + device = mlir_module.body.operations[0] + pm.run(device.operation) + # CHECK: AIE.flow(%tile_10_0, DMA : 0, %tile_7_3, DMA : 0) + # CHECK: AIE.flow(%tile_11_0, DMA : 0, %tile_13_3, DMA : 0) + # CHECK: AIE.flow(%tile_2_5, DMA : 0, %tile_6_0, DMA : 0) + # CHECK: AIE.flow(%tile_3_1, Core : 0, %tile_2_5, Core : 0) + # CHECK: AIE.flow(%tile_6_6, DMA : 0, %tile_2_0, DMA : 0) + # CHECK: AIE.flow(%tile_7_3, Core : 0, %tile_6_6, Core : 0) + # CHECK: AIE.flow(%tile_12_5, DMA : 0, %tile_18_0, DMA : 0) + # CHECK: AIE.flow(%tile_13_3, Core : 0, %tile_12_5, Core : 0) + print(mlir_module) + + # CHECK-LABEL: TEST: test_simple @run def test_simple(): with open(Path(THIS_FILE).parent.parent / "create-flows" / "simple.mlir") as f: mlir_module = Module.parse(f.read()) - r = Router(timeout=10) + r = Router(timeout=TIMEOUT) pass_ = create_python_router_pass(r) pm = PassManager() pass_manager_add_owned_pass(pm, pass_) @@ -492,7 +1414,7 @@ def test_simple(): def test_simple2(): with open(Path(THIS_FILE).parent.parent / "create-flows" / "simple2.mlir") as f: mlir_module = Module.parse(f.read()) - r = Router(timeout=10) + r = Router(timeout=TIMEOUT) pass_ = create_python_router_pass(r) pm = PassManager() pass_manager_add_owned_pass(pm, pass_) @@ -514,7 +1436,7 @@ def test_simple_flows2(): Path(THIS_FILE).parent.parent / "create-flows" / "simple_flows2.mlir" ) as f: mlir_module = Module.parse(f.read()) - r = Router(timeout=10) + r = Router(timeout=TIMEOUT) pass_ = create_python_router_pass(r) pm = PassManager() pass_manager_add_owned_pass(pm, pass_) @@ -539,7 +1461,7 @@ def test_simple_flows_shim(): ) as f: for mlir_module in f.read().split("// -----"): mlir_module = Module.parse(mlir_module) - r = Router(timeout=10) + r = Router(timeout=TIMEOUT) pass_ = create_python_router_pass(r) pm = PassManager() pass_manager_add_owned_pass(pm, pass_) @@ -585,47 +1507,3 @@ def test_simple_flows_shim(): # CHECK: %{{.*}} = AIE.shimmux(%[[T30]]) { # CHECK: AIE.connect # CHECK: } - - -# CHECK-LABEL: TEST: test_broadcast -@run -def test_broadcast(): - with open(Path(THIS_FILE).parent.parent / "create-flows" / "broadcast.mlir") as f: - mlir_module = Module.parse(f.read()) - r = Router(timeout=10) - pass_ = create_python_router_pass(r) - pm = PassManager() - pass_manager_add_owned_pass(pm, pass_) - pm.add("aie-find-flows") - - device = mlir_module.body.operations[0] - pm.run(device.operation) - - # CHECK: %[[T03:.*]] = AIE.tile(0, 3) - # CHECK: %[[T02:.*]] = AIE.tile(0, 2) - # CHECK: %[[T00:.*]] = AIE.tile(0, 0) - # CHECK: %[[T13:.*]] = AIE.tile(1, 3) - # CHECK: %[[T11:.*]] = AIE.tile(1, 1) - # CHECK: %[[T10:.*]] = AIE.tile(1, 0) - # CHECK: %[[T20:.*]] = AIE.tile(2, 0) - # CHECK: %[[T30:.*]] = AIE.tile(3, 0) - # CHECK: %[[T22:.*]] = AIE.tile(2, 2) - # CHECK: %[[T31:.*]] = AIE.tile(3, 1) - # CHECK: %[[T60:.*]] = AIE.tile(6, 0) - # CHECK: %[[T70:.*]] = AIE.tile(7, 0) - # CHECK: %[[T71:.*]] = AIE.tile(7, 1) - # CHECK: %[[T72:.*]] = AIE.tile(7, 2) - # CHECK: %[[T73:.*]] = AIE.tile(7, 3) - # CHECK: %[[T80:.*]] = AIE.tile(8, 0) - # CHECK: %[[T82:.*]] = AIE.tile(8, 2) - # CHECK: %[[T83:.*]] = AIE.tile(8, 3) - # - # CHECK: AIE.flow(%[[T20]], DMA : 0, %[[T71]], DMA : 0) - # CHECK: AIE.flow(%[[T20]], DMA : 0, %[[T31]], DMA : 0) - # CHECK: AIE.flow(%[[T20]], DMA : 0, %[[T82]], DMA : 0) - # CHECK: AIE.flow(%[[T20]], DMA : 0, %[[T13]], DMA : 0) - # CHECK: AIE.flow(%[[T60]], DMA : 0, %[[T83]], DMA : 1) - # CHECK: AIE.flow(%[[T60]], DMA : 0, %[[T22]], DMA : 1) - # CHECK: AIE.flow(%[[T60]], DMA : 0, %[[T02]], DMA : 1) - # CHECK: AIE.flow(%[[T60]], DMA : 0, %[[T31]], DMA : 1) - print(mlir_module)