diff --git a/.github/workflows/buildAndTest.yml b/.github/workflows/buildAndTest.yml index 800fedb7f1..625de85fa1 100644 --- a/.github/workflows/buildAndTest.yml +++ b/.github/workflows/buildAndTest.yml @@ -13,6 +13,13 @@ env: # Otherwise, on Ubuntu 20.04 the installation of tzdata asking question DEBIAN_FRONTEND: noninteractive +concurrency: + # A PR number if a pull request and otherwise the commit hash. This cancels + # queued and in-progress runs for the same PR (presubmit) or commit + # (postsubmit). + group: ci-build-test-cpp-linux-${{ github.event.number || github.sha }} + cancel-in-progress: true + jobs: build-repo: name: Build and Test @@ -45,6 +52,7 @@ jobs: - name: Install Python packages run: | pip install cmake numpy psutil pybind11 rich pkginfo lit PyYAML + pip install -r python/requirements.txt - name: Install packages run: sudo apt-get install -y ninja-build clang lld diff --git a/.github/workflows/lintAndFormat.yml b/.github/workflows/lintAndFormat.yml index e5006012db..e14bf59da2 100644 --- a/.github/workflows/lintAndFormat.yml +++ b/.github/workflows/lintAndFormat.yml @@ -40,6 +40,7 @@ jobs: - name: Install Python packages run: | pip install cmake numpy psutil pybind11 rich pkginfo lit PyYAML requests + pip install -r python/requirements.txt - name: Get MLIR id: mlir-wheels @@ -150,7 +151,6 @@ jobs: with: tool_name: clang-format level: error - fail_on_error: true cleanup: true - name: Run black format @@ -174,7 +174,6 @@ jobs: with: tool_name: black level: error - fail_on_error: true code-coverage: @@ -200,6 +199,7 @@ jobs: - name: Install Python and other packages run: | pip install cmake numpy psutil pybind11 rich lit + pip install -r python/requirements.txt - name: Install Ninja run: sudo apt-get install -y ninja-build clang lld llvm diff --git a/.pylintrc b/.pylintrc index c4b1380cf1..13c6be76ea 100644 --- a/.pylintrc +++ b/.pylintrc @@ -56,4 +56,8 @@ check-str-concat-over-line-jumps=yes # C0114: Missing module docstring (missing-module-docstring) # E0402: Attempted relative import beyond top-level package (relative-beyond-top-level) # C0115: Missing class docstring (missing-class-docstring) -disable=C0116,C0114,E0402,C0115 \ No newline at end of file +# C0415: Import outside toplevel +# 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 diff --git a/include/aie/Dialect/AIE/IR/AIETargetModel.h b/include/aie/Dialect/AIE/IR/AIETargetModel.h index 9849c70b2c..b524e97578 100644 --- a/include/aie/Dialect/AIE/IR/AIETargetModel.h +++ b/include/aie/Dialect/AIE/IR/AIETargetModel.h @@ -19,7 +19,7 @@ namespace xilinx::AIE { -typedef struct TileID { +using TileID = struct TileID { // friend definition (will define the function as a non-member function in the // namespace surrounding the class). friend std::ostream &operator<<(std::ostream &os, const TileID &s) { @@ -50,7 +50,7 @@ typedef struct TileID { bool operator!=(const TileID &rhs) const { return !(*this == rhs); } int col, row; -} TileID; +}; class AIETargetModel { public: @@ -155,7 +155,7 @@ class AIETargetModel { virtual bool isLegalMemAffinity(int coreCol, int coreRow, int memCol, int memRow) const = 0; - /// Return the base address in the local address map of differnet memories. + /// Return the base address in the local address map of different memories. virtual uint32_t getMemInternalBaseAddress(TileID src) const = 0; virtual uint32_t getMemSouthBaseAddress() const = 0; virtual uint32_t getMemWestBaseAddress() const = 0; @@ -321,7 +321,7 @@ class AIE2TargetModel : public AIETargetModel { }; class VC1902TargetModel : public AIE1TargetModel { - llvm::SmallDenseSet noc_columns = { + llvm::SmallDenseSet nocColumns = { 2, 3, 6, 7, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 46, 47}; public: @@ -332,11 +332,11 @@ class VC1902TargetModel : public AIE1TargetModel { int rows() const override { return 9; /* One Shim row and 8 Core rows. */ } bool isShimNOCTile(int col, int row) const override { - return row == 0 && noc_columns.contains(col); + return row == 0 && nocColumns.contains(col); } bool isShimPLTile(int col, int row) const override { - return row == 0 && !noc_columns.contains(col); + return row == 0 && !nocColumns.contains(col); } bool isShimNOCorPLTile(int col, int row) const override { @@ -504,7 +504,8 @@ class IPUTargetModel : public AIE2TargetModel { } // namespace xilinx::AIE namespace llvm { -template <> struct DenseMapInfo { +template <> +struct DenseMapInfo { using FirstInfo = DenseMapInfo; using SecondInfo = DenseMapInfo; @@ -528,7 +529,8 @@ template <> struct DenseMapInfo { }; } // namespace llvm -template <> struct std::hash { +template <> +struct std::hash { std::size_t operator()(const xilinx::AIE::TileID &s) const noexcept { std::size_t h1 = std::hash{}(s.col); std::size_t h2 = std::hash{}(s.row); diff --git a/include/aie/Dialect/AIE/Transforms/AIEPathFinder.h b/include/aie/Dialect/AIE/Transforms/AIEPathFinder.h index a524f89e5f..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,11 +107,13 @@ 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) + : src(src), dsts(std::move(dsts)) {} Port src; std::set dsts; @@ -141,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; @@ -173,48 +174,63 @@ 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 Pathfinder { - SwitchboxGraph graph; - std::vector flows; - std::map grid; - // Use a list instead of a vector because nodes have an edge list of raw - // pointers to edges (so growing a vector would invalidate the pointers). - std::list edges; +}; +class Router { public: - Pathfinder() = default; + Router() = default; + // This has to go first so it can serve as a key function. + // https://lld.llvm.org/missingkeyfunction + virtual ~Router() = default; virtual void initialize(int maxCol, int maxRow, - const AIETargetModel &targetModel); + const AIETargetModel &targetModel) = 0; virtual void addFlow(TileID srcCoords, Port srcPort, TileID dstCoords, - Port dstPort); - virtual bool addFixedConnection(ConnectOp connectOp); + Port dstPort) = 0; + virtual bool addFixedConnection(ConnectOp connectOp) = 0; virtual std::optional> - findPaths(int maxIterations); + findPaths(int maxIterations) = 0; + virtual Switchbox *getSwitchbox(TileID coords) = 0; +}; - virtual Switchbox *getSwitchbox(TileID coords) { - auto sb = std::find_if(graph.begin(), graph.end(), [&](SwitchboxNode *sb) { +class Pathfinder : public Router { +public: + Pathfinder() = default; + void initialize(int maxCol, int maxRow, + const AIETargetModel &targetModel) override; + void addFlow(TileID srcCoords, Port srcPort, TileID dstCoords, + Port dstPort) override; + bool addFixedConnection(ConnectOp connectOp) override; + std::optional> + findPaths(int maxIterations) override; + + Switchbox *getSwitchbox(TileID coords) override { + 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"); return *sb; } - virtual ~Pathfinder() = default; + +private: + SwitchboxGraph graph; + std::vector flows; + std::map grid; + // Use a list instead of a vector because nodes have an edge list of raw + // pointers to edges (so growing a vector would invalidate the pointers). + std::list edges; }; WireBundle getConnectingBundle(WireBundle dir); @@ -226,7 +242,7 @@ WireBundle getConnectingBundle(WireBundle dir); class DynamicTileAnalysis { public: int maxCol, maxRow; - std::shared_ptr pathfinder; + std::shared_ptr pathfinder; std::map flowSolutions; std::map processedFlows; @@ -238,8 +254,7 @@ class DynamicTileAnalysis { const int maxIterations = 1000; // how long until declared unroutable DynamicTileAnalysis() : pathfinder(std::make_shared()) {} - DynamicTileAnalysis(std::shared_ptr p) - : pathfinder(std::move(p)) {} + DynamicTileAnalysis(std::shared_ptr p) : pathfinder(std::move(p)) {} mlir::LogicalResult runAnalysis(DeviceOp &device); @@ -264,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; @@ -274,7 +290,8 @@ template <> struct less { namespace llvm { -template <> struct GraphTraits { +template <> +struct GraphTraits { using NodeRef = xilinx::AIE::SwitchboxNode *; static xilinx::AIE::SwitchboxNode *SwitchboxGraphGetSwitchbox( @@ -332,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 ae13360d6e..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())) @@ -341,7 +341,8 @@ void AIEDialect::initialize() { // Check that the operation only contains terminators in // TerminatorOpTypes. -template struct HasSomeTerminator { +template +struct HasSomeTerminator { static LogicalResult verifyTrait(Operation *op) { for (auto ®ion : op->getRegions()) { for (auto &block : region) { @@ -659,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); } } @@ -704,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); } } @@ -749,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); } } @@ -794,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); } } @@ -871,7 +872,7 @@ LogicalResult TileOp::verify() { auto users = getResult().getUsers(); bool found = false; - for (auto user : users) { + for (auto *user : users) { if (llvm::isa(*user)) { if (found) return emitOpError("can only have one switchbox"); @@ -932,10 +933,17 @@ LogicalResult SwitchboxOp::verify() { sourceset.insert(source); Port dest = {connectOp.getDestBundle(), connectOp.destIndex()}; - if (destset.count(dest)) - return connectOp.emitOpError("targets same destination ") - << stringifyWireBundle(dest.bundle) << ": " << dest.channel - << " as another connect operation"; + if (destset.count(dest)) { + 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); if (connectOp.sourceIndex() < 0) @@ -1005,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); @@ -1288,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); @@ -1422,7 +1430,8 @@ int SwitchboxOp::colIndex() { return getTileOp().colIndex(); } int SwitchboxOp::rowIndex() { return getTileOp().rowIndex(); } -template struct HasSomeParent { +template +struct HasSomeParent { static LogicalResult verifyTrait(Operation *op) { Operation *operation = op->getParentOp(); while (operation) { @@ -1459,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()); @@ -1475,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/AIEMLIRModule.cpp b/python/AIEMLIRModule.cpp index 30ef484e7b..fbc2261761 100644 --- a/python/AIEMLIRModule.cpp +++ b/python/AIEMLIRModule.cpp @@ -15,7 +15,7 @@ using namespace mlir::python::adaptors; PYBIND11_MODULE(_aie, m) { - ::aieRegisterAllPasses(); + aieRegisterAllPasses(); m.doc() = R"pbdoc( AIE MLIR Python bindings @@ -30,12 +30,12 @@ PYBIND11_MODULE(_aie, m) { m.def( "register_dialect", [](MlirDialectRegistry registry) { - MlirDialectHandle aie_handle = mlirGetDialectHandle__aie__(); - MlirDialectHandle aiex_handle = mlirGetDialectHandle__aiex__(); - MlirDialectHandle aievec_handle = mlirGetDialectHandle__aievec__(); - mlirDialectHandleInsertDialect(aie_handle, registry); - mlirDialectHandleInsertDialect(aiex_handle, registry); - mlirDialectHandleInsertDialect(aievec_handle, registry); + MlirDialectHandle aieHandle = mlirGetDialectHandle__aie__(); + MlirDialectHandle aiexHandle = mlirGetDialectHandle__aiex__(); + MlirDialectHandle aievecHandle = mlirGetDialectHandle__aievec__(); + mlirDialectHandleInsertDialect(aieHandle, registry); + mlirDialectHandleInsertDialect(aiexHandle, registry); + mlirDialectHandleInsertDialect(aievecHandle, registry); }, py::arg("registry")); @@ -43,7 +43,7 @@ PYBIND11_MODULE(_aie, m) { mlir_type_subclass(m, "ObjectFifoType", aieTypeIsObjectFifoType) .def_classmethod( "get", - [](py::object cls, MlirType type) { + [](const py::object &cls, const MlirType type) { return cls(aieObjectFifoTypeGet(type)); }, "Get an instance of ObjectFifoType with given element type.", @@ -52,7 +52,7 @@ PYBIND11_MODULE(_aie, m) { mlir_type_subclass(m, "ObjectFifoSubviewType", aieTypeIsObjectFifoSubviewType) .def_classmethod( "get", - [](py::object cls, MlirType type) { + [](const py::object &cls, const MlirType type) { return cls(aieObjectFifoSubviewTypeGet(type)); }, "Get an instance of ObjectFifoSubviewType with given element type.", diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index a3bdcb3101..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 @@ -93,7 +95,9 @@ if (AIE_ENABLE_PYTHON_PASSES) SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/AIEMLIRModule.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/PybindTypes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PythonPass.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/RouterPass.cpp ${_mlir_python_cpp_sources} ${RegisterEverythingSources} @@ -107,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 new file mode 100644 index 0000000000..7ca0ea9372 --- /dev/null +++ b/python/PybindTypes.cpp @@ -0,0 +1,171 @@ +//===- PybindTypes.cpp ------------------------------------------*- C++ -*-===// +// +// Copyright (C) 2022, Advanced Micro Devices, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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; + +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. + py::class_>(m, "PortSet") + .def(py::init<>()) + .def("add", [](std::set &set, const Port p) { set.insert(p); }) + .def("__repr__", + [](const std::set &set) { + std::stringstream ss; + ss << "{" + << join(llvm::map_range( + set, + [](const Port &port) { return to_string(port); }), + ", ") + << "}"; + + return ss.str(); + }) + .def("__contains__", [](const std::set &self, const Port &p) { + return static_cast(self.count(p)); + }); + + py::enum_(m, "WireBundle", py::arithmetic()) + .value("Core", WireBundle::Core) + .value("DMA", WireBundle::DMA) + .value("FIFO", WireBundle::FIFO) + .value("South", WireBundle::South) + .value("West", WireBundle::West) + .value("North", WireBundle::North) + .value("East", WireBundle::East) + .value("PLIO", WireBundle::PLIO) + .value("NOC", WireBundle::NOC) + .value("Trace", WireBundle::Trace) + .export_values(); + + py::class_(m, "Port") + .def(py::init()) + .def(py::init()) + .def_readonly("bundle", &Port::bundle) + .def_readwrite("channel", &Port::channel) + .def(py::hash(py::self)) + .def(py::self == py::self) + .def(py::self < py::self) + .def("__repr__", [](const Port &port) { return to_string(port); }); + + py::class_(m, "TileID") + .def(py::init()) + .def_readonly("col", &TileID::col) + .def_readonly("row", &TileID::row) + // Implements __hash__ (magic?) + .def(py::hash(py::self)) + .def(py::self == py::self) + .def(py::self < py::self) + .def("__repr__", [](const TileID &tile) { return to_string(tile); }); + + py::class_(m, "Switchbox") + .def(py::init()) + .def(py::init()) + .def(py::init()) + // Implements __hash__ (magic?) + .def(py::hash(py::self)) + .def(py::self == py::self) + .def(py::self < py::self) + .def("__repr__", [](const Switchbox &sb) { return to_string(sb); }); + + py::class_(m, "SwitchSetting") + .def(py::init()) + .def(py::init()) + .def(py::init<>()) + .def_readwrite("src", &SwitchSetting::src) + .def_readwrite("dsts", &SwitchSetting::dsts) + .def(py::self < py::self) + .def("__repr__", [](const SwitchSetting &sb) { return to_string(sb); }); + + py::class_(m, "Channel") + .def(py::init()) + .def_property_readonly("src", [](const Channel &c) { return c.src; }) + .def_property_readonly("target", + [](const Channel &c) { return c.target; }) + .def_readonly("bundle", &Channel::bundle) + .def("__repr__", [](const Channel &c) { return to_string(c); }); + + py::class_(m, "PathEndPoint") + .def(py::init()) + .def_readonly("sb", &PathEndPoint::sb) + .def_readonly("port", &PathEndPoint::port) + // Implements __hash__ (magic?) + .def(py::hash(py::self)) + .def(py::self == py::self) + .def(py::self < py::self) + .def("__repr__", [](const PathEndPoint &pe) { return to_string(pe); }); + + py::class_(m, "Flow") + .def(py::init()) + .def_readonly("src", &Flow::src) + .def_readonly("dsts", &Flow::dsts) + .def("__repr__", [](const Flow &flow) { return to_string(flow); }); + + py::class_(m, "AIETargetModel") + .def("get_num_source_switchbox_connections", + &AIETargetModel::getNumSourceSwitchboxConnections, + // Something about the factor that AIETargetModel is virtual + // (and thus always a pointer) necessitates this. + py::return_value_policy::reference) + .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 new file mode 100644 index 0000000000..1bd4f8fd52 --- /dev/null +++ b/python/PybindTypes.h @@ -0,0 +1,63 @@ +//===- PybindTypes.h --------------------------------------------*- C++ -*-===// +// +// Copyright (C) 2022, Advanced Micro Devices, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef AIE_PYBINDTYPES_H +#define AIE_PYBINDTYPES_H + +#include "aie/Dialect/AIE/Transforms/AIEPathFinder.h" + +#include "IRModule.h" +#include "mlir/CAPI/IR.h" + +#include + +namespace py = pybind11; + +namespace xilinx::AIE { +using Flow = struct Flow { + PathEndPoint src; + std::vector dsts; + + friend std::ostream &operator<<(std::ostream &os, const Flow &s) { + os << "Flow(" << s.src << ": {" + << join(map_range(llvm::ArrayRef(s.dsts), + [](const PathEndPoint &pe) { return to_string(pe); }), + ", ") + << "})"; + return os; + } + + GENERATE_TO_STRING(Flow) + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Flow &s) { + os << to_string(s); + return os; + } +}; + +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 5e1d9d6ca0..0c4cb75a9d 100644 --- a/python/PythonPass.cpp +++ b/python/PythonPass.cpp @@ -1,48 +1,19 @@ -//===- PythonPassDemo.cpp ---------------------------------------*- C++ -*-===// +//===- PythonPass.cpp -------------------------------------------*- C++ -*-===// // // Copyright (C) 2022, Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -#include "aie/Dialect/AIE/IR/AIEDialect.h" +#include "PythonPass.h" -#include "mlir/Bindings/Python/PybindAdaptors.h" -#include "mlir/CAPI/IR.h" -#include "mlir/IR/BuiltinDialect.h" -#include "mlir/Pass/Pass.h" - -using namespace mlir; -using namespace mlir::python::adaptors; -using namespace xilinx::AIE; - -namespace py = pybind11; - -struct PythonPassDemo - : public PassWrapper> { - MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(PythonPassDemo) - - PythonPassDemo(py::function func) : func(func) {} - StringRef getArgument() const final { return "python-pass-demo"; } - void runOnOperation() override { - this->getOperation()->walk([this](Operation *op) { func(wrap(op)); }); - } - - py::function func; -}; - -std::unique_ptr> -createPythonPassDemoPassWithFunc(py::function func) { - return std::make_unique(func); -} - -void registerPythonPassDemoPassWithFunc(py::function func) { - registerPass([func]() { return createPythonPassDemoPassWithFunc(func); }); +PyObject *mlirPassToPythonCapsule(MlirPass pass) { + return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(pass), + MLIR_PYTHON_CAPSULE_PASS, nullptr); } -PYBIND11_MODULE(_aie_python_passes, m) { - - m.def("register_python_pass_demo_pass", [](py::function func) { - registerPythonPassDemoPassWithFunc(std::move(func)); - }); +MlirPass mlirPythonCapsuleToPass(PyObject *capsule) { + void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_PASS); + MlirPass pass = {ptr}; + return pass; } diff --git a/python/PythonPass.h b/python/PythonPass.h new file mode 100644 index 0000000000..078fdb5cbb --- /dev/null +++ b/python/PythonPass.h @@ -0,0 +1,19 @@ +//===- PythonPass.h ---------------------------------------------*- C++ -*-===// +// +// Copyright (C) 2022, Advanced Micro Devices, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef AIE_PYTHONPASS_H +#define AIE_PYTHONPASS_H + +#include "mlir/Bindings/Python/PybindAdaptors.h" + +#define MLIR_PYTHON_CAPSULE_PASS MAKE_MLIR_PYTHON_QUALNAME("ir.Pass._CAPIPtr") + +PyObject *mlirPassToPythonCapsule(MlirPass pass); + +MlirPass mlirPythonCapsuleToPass(PyObject *capsule); + +#endif // AIE_PYTHONPASS_H diff --git a/python/RouterPass.cpp b/python/RouterPass.cpp new file mode 100644 index 0000000000..ba5e2ddfbc --- /dev/null +++ b/python/RouterPass.cpp @@ -0,0 +1,107 @@ +//===- PythonRouter.cpp ----------------------------*- C++ -*-===// +// +// Copyright (C) 2022, Advanced Micro Devices, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PybindTypes.h" +#include "PythonPass.h" + +#include "aie/Dialect/AIE/IR/AIEDialect.h" +#include "aie/Dialect/AIE/Transforms/AIEPasses.h" +#include "aie/Dialect/AIE/Transforms/AIEPathFinder.h" + +#include "mlir/Bindings/Python/PybindAdaptors.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; + +namespace py = pybind11; + +// ┌─────┐ ┌─────┐ +// │ 1,0 ├ ┤ 1,1 │ +// │ │ N │ │ +// └─────┘ └─────┘ +// W E +// ┌─────┐ ┌─────┐ +// │ 0,0 │ S │ 0,1 │ +// │ │ │ │ +// └─────┘ └─────┘ + +class PythonRouter : public Router { +public: + explicit PythonRouter(py::object router) : router(std::move(router)) {} + + void initialize(const int maxCol, const int maxRow, + const AIETargetModel &targetModel) override { + // Here we're copying a pointer to targetModel, which is a static somewhere. + router.attr("initialize")(maxCol, maxRow, &targetModel); + } + + void addFlow(TileID srcCoords, const Port srcPort, TileID dstCoords, + const Port dstPort) override { + router.attr("add_flow")( + PathEndPoint{{srcCoords.col, srcCoords.row}, srcPort}, + PathEndPoint{{dstCoords.col, dstCoords.row}, dstPort}); + } + + bool addFixedConnection(ConnectOp connectOp) override { + auto pyConnectOp = PyConnectOp::forOperation(connectOp); + return router.attr("add_fixed_connection")(pyConnectOp).cast(); + } + + std::optional> + findPaths(const int maxIterations) override { + return router.attr("find_paths")() + .cast>(); + } + Switchbox *getSwitchbox(TileID coords) override { return nullptr; } + + py::object router; +}; + +struct PythonRouterPass : AIEPathfinderPass { + using AIEPathfinderPass::AIEPathfinderPass; + + StringRef getArgument() const final { return "aie-create-python-router"; } +}; + +std::unique_ptr> +createPythonRouterPass(py::object router) { + return std::make_unique( + DynamicTileAnalysis(std::make_shared(std::move(router)))); +} + +MlirPass mlircreatePythonRouterPass(py::object router) { + return wrap(createPythonRouterPass(std::move(router)).release()); +} + +void registerPythonRouterPassWithRouter(const py::object &router) { + registerPass([router] { return createPythonRouterPass(router); }); +} + +PYBIND11_MODULE(_aie_python_passes, m) { + + bindTypes(m); + + m.def("create_python_router_pass", [](const py::object &router) { + MlirPass pass = mlircreatePythonRouterPass(router); + auto capsule = + py::reinterpret_steal(mlirPassToPythonCapsule(pass)); + return capsule; + }); + + m.def("pass_manager_add_owned_pass", + [](MlirPassManager passManager, py::handle passHandle) { + py::object passCapsule = mlirApiObjectToCapsule(passHandle); + MlirPass pass = mlirPythonCapsuleToPass(passCapsule.ptr()); + mlirPassManagerAddOwnedPass(passManager, pass); + }); + + m.def("get_connecting_bundle", &getConnectingBundle); +} diff --git a/python/requirements.txt b/python/requirements.txt index f1b2272717..2a990cfdc4 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,3 +1,9 @@ numpy>=1.19.5, <=1.26 +pybind11>=2.9.0, <=2.10.3 PyYAML>=5.3.1, <=6.0.1 -dataclasses>=0.6, <=0.8 \ No newline at end of file +dataclasses>=0.6, <=0.8 +networkx[default] +ortools +gurobipy +matplotlib +ortools \ No newline at end of file diff --git a/python/util.py b/python/util.py index 0fd8382125..a805033df2 100644 --- a/python/util.py +++ b/python/util.py @@ -1,8 +1,16 @@ +import multiprocessing +import numbers +import os +from collections import defaultdict from contextlib import ExitStack, contextmanager from dataclasses import dataclass +from typing import List, Tuple, Dict, Set from typing import Optional from typing import Union +import matplotlib as mpl +import matplotlib.pyplot as plt +import networkx as nx import numpy as np from .extras import types as T @@ -52,18 +60,22 @@ def infer_mlir_type( """ if isinstance(py_val, bool): return T.bool() - elif isinstance(py_val, int): - if -(2**31) <= py_val < 2**31: + + if isinstance(py_val, int): + # no clue why but black can't decide which it wants the ** + # fmt: off + if -(2 ** 31) <= py_val < 2 ** 31: return T.i32() - elif 2**31 <= py_val < 2**32: + elif 2 ** 31 <= py_val < 2 ** 32: return T.ui32() - elif -(2**63) <= py_val < 2**63: + elif -(2 ** 63) <= py_val < 2 ** 63: return T.i64() - elif 2**63 <= py_val < 2**64: + elif 2 ** 63 <= py_val < 2 ** 64: return T.ui64() - else: - raise RuntimeError(f"Nonrepresentable integer {py_val}.") - elif isinstance(py_val, float): + raise RuntimeError(f"Nonrepresentable integer {py_val}.") + # fmt: on + + if isinstance(py_val, float): if ( abs(py_val) == float("inf") or abs(py_val) == 0.0 @@ -71,15 +83,15 @@ def infer_mlir_type( or np.finfo(np.float32).min <= abs(py_val) <= np.finfo(np.float32).max ): return T.f32() - else: - return T.f64() - elif isinstance(py_val, np.ndarray): + return T.f64() + + if isinstance(py_val, np.ndarray): dtype = np_dtype_to_mlir_type(py_val.dtype.type) return RankedTensorType.get(py_val.shape, dtype) - else: - raise NotImplementedError( - f"Unsupported Python value {py_val=} with type {type(py_val)}" - ) + + raise NotImplementedError( + f"Unsupported Python value {py_val=} with type {type(py_val)}" + ) def mlir_type_to_np_dtype(mlir_type): @@ -119,3 +131,458 @@ def mlir_mod_ctx( ip = InsertionPoint(module.body) stack.enter_context(ip) yield MLIRContext(context, module) + + +def build_graph(max_cols, max_rows, target_model): + from ._mlir_libs._aie_python_passes import WireBundle, Switchbox + + DG = nx.DiGraph() + for c in range(max_cols + 1): + for r in range(max_rows + 1): + this_switchbox = Switchbox(c, r) + DG.add_node(this_switchbox) + if r > 0: + southern_neighbor = Switchbox(c, r - 1) + # 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( + this_switchbox, + southern_neighbor, + bundle=WireBundle.South, + capacity=max_capacity, + ) + # 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( + southern_neighbor, + this_switchbox, + bundle=WireBundle.North, + capacity=max_capacity, + ) + if c > 0: + western_neighbor = Switchbox(c - 1, r) + if max_capacity := target_model.get_num_source_switchbox_connections( + c, r, WireBundle.West + ): + DG.add_edge( + western_neighbor, + this_switchbox, + bundle=WireBundle.East, + capacity=max_capacity, + ) + if max_capacity := target_model.get_num_dest_switchbox_connections( + c, r, WireBundle.West + ): + DG.add_edge( + this_switchbox, + western_neighbor, + bundle=WireBundle.West, + capacity=max_capacity, + ) + + return DG + + +def route_using_cp( + DG, + flows, + min_edges=False, + seed=42, + num_workers=multiprocessing.cpu_count() // 2, + timeout=600, +): + from ortools.sat.python import cp_model + + # Create model object + model = cp_model.CpModel() + solver = cp_model.CpSolver() + # For determinism + solver.parameters.random_seed = seed + solver.parameters.num_workers = num_workers + solver.parameters.max_time_in_seconds = timeout + + # Create variable for each edge, for each path + flow_vars = { + flow: {(i, j): model.NewIntVar(0, 1, "") for i, j in DG.edges} 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 + 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 + + # what goes in must come out + model.Add( + sum(flow_var[e] for e in DG.in_edges(nbunch=n)) + == sum(flow_var[e] for e in DG.out_edges(nbunch=n)) + ) + + total_demand = { + (i, j): model.NewIntVar(0, len(flat_flow_vars), "") for i, j in DG.edges + } + overlapping_demands = { + (i, j): model.NewIntVar(0, len(flat_flow_vars), "") for i, j in DG.edges + } + used_edges = {(i, j): model.NewIntVar(0, 1, "") for i, j in DG.edges} + + 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), "" + ) + model.AddMultiplicationEquality( + 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( + overlapping_flows[k, l] + for k, f1 in enumerate(flat_flow_vars) + for l, f2 in enumerate(flat_flow_vars[k + 1 :], start=k + 1) + ) + ) + + obj = sum(total_demand[i, j] for i, j in DG.edges) + if min_edges: + obj += sum(used_edges[i, j] for i, j in DG.edges) + else: + obj += sum(overlapping_demands[i, j] for i, j in DG.edges) + model.Minimize(obj) + + status = solver.Solve(model) + if status in {cp_model.OPTIMAL, cp_model.FEASIBLE}: + flow_paths = {} + for flow, flow_varss in flow_vars.items(): + flow_paths[flow] = [ + # 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 + + raise RuntimeError("Couldn't route.") + + +def route_using_ilp( + DG, + flows, + timeout=600, +): + import gurobipy as gp + from gurobipy import GRB + + m = gp.Model() + m.setParam("TimeLimit", timeout) + + flow_vars = { + flow: m.addVars(DG.edges, vtype=GRB.BINARY, name="flow") for flow in flows + } + flat_flow_vars = list(flow_vars.values()) + + 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 + + # what goes in must come out + m.addConstr( + gp.quicksum(flow_var[e] for e in DG.in_edges(nbunch=n)) + == gp.quicksum(flow_var[e] for e in DG.out_edges(nbunch=n)) + ) + + # Create demand variables + total_demand = m.addVars(DG.edges) + overlapping_demands = m.addVars(DG.edges) + + # Add demand/flow relationship + 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( + (f1[i, j] * f2[i, j]) + for k, f1 in enumerate(flat_flow_vars) + for f2 in flat_flow_vars[k + 1 :] + ) + ) + + m.setObjective( + gp.quicksum( + total_demand[i, j] + overlapping_demands[i, j] for i, j in DG.edges + ), + GRB.MINIMIZE, + ) + + # 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 + + +def rgb2hex(r, g, b, a): + return f"#{int(r * 255):02x}{int(g * 255):02x}{int(b * 255):02x}{int(a * 255):02x}" + + +def plot_paths(DG, src, paths): + pos = dict((n, (n.col, n.row)) for n in DG.nodes()) + labels = dict((n, f"{n.col},{n.row}") for n in DG.nodes()) + + _fig, ax = plt.subplots() + nx.draw( + DG, + with_labels=True, + edge_color="white", + node_color=["green" if n == src else "gray" for n in DG.nodes], + pos=pos, + labels=labels, + ax=ax, + ) + + colors = lambda x: mpl.colormaps["prism"](x / len(paths)) + for j, path in enumerate(paths): + nx.draw_networkx_edges( + DG, pos=pos, edgelist=path, edge_color=rgb2hex(*colors(j)), width=4, ax=ax + ) + + plt.show() + + +def plot_src_paths(DG, flow_paths): + src_paths = defaultdict(list) + for (src, _), path in flow_paths.items(): + src_paths[src.sb.col, src.sb.row].append(path) + + for src, paths in src_paths.items(): + plot_paths(DG, src, 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) + + 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) + processed = {src.sb} + + # Trace backwards until a vertex already processed is reached + for end_point, path in dsts: + 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, 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) + ) + + processed.add(curr_sb) + curr_sb = pred_sb + + switch_settings[src.sb].src = src.port + routing_solution[src] = list(reversed(switch_settings.items())) + + return routing_solution + + +def pythonize_bool(value): + if value is None: + return False + if isinstance(value, bool): + return value + if isinstance(value, numbers.Number): + return value != 0 + if isinstance(value, str): + if value.lower() in ("1", "true", "on", "yes"): + return True + if value.lower() in ("", "0", "false", "off", "no"): + return False + raise ValueError(f'"{value}" is not a valid boolean') + + +class Router: + max_col: int + max_row: int + timeout: int + use_gurobi: bool = False + # Don't use actual binding here to prevent a blow up since class bodies are executed + # at module load time. + target_model: "AIETargetModel" + flows: List[Tuple["PathEndPoint", "PathEndPoint"]] + used_channels: Dict[Tuple["Switchbox", "Switchbox"], Set[int]] + routing_solution: Dict["PathEndPoint", "SwitchSettings"] + + def __init__(self, use_gurobi=False, timeout=600): + self.flows = [] + 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): + 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: + if self.use_gurobi: + flow_paths = route_using_ilp(self.DG, self.flows, timeout=self.timeout) + else: + flow_paths = route_using_cp( + self.DG, self.flows, num_workers=10, timeout=self.timeout + ) + + self.routing_solution = get_routing_solution( + self.DG, flow_paths, self.used_channels + ) + + 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 c94b1fde92..1adafa4705 100644 --- a/test/python/python_passes.py +++ b/test/python/python_passes.py @@ -4,45 +4,1506 @@ # RUN: %PYTHON %s | FileCheck %s # REQUIRES: python_passes +from pathlib import Path +from textwrap import dedent +# noinspection PyUnresolvedReferences import aie.dialects.aie -from aie._mlir_libs import _aie_python_passes -from aie.ir import Context, Location, Module, InsertionPoint +from aie._mlir_libs._aie_python_passes import ( + create_python_router_pass, + pass_manager_add_owned_pass, +) + +from aie.ir import Context, Location, Module from aie.passmanager import PassManager +from aie.util import Router + +TIMEOUT = 10 + + +def run(f): + with Context(), Location.unknown(): + print("\nTEST:", f.__name__) + 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=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: %[[t20:.*]] = AIE.tile(2, 0) + # CHECK: %[[t30:.*]] = AIE.tile(3, 0) + # CHECK: %[[t34:.*]] = AIE.tile(3, 4) + # CHECK: %[[t43:.*]] = AIE.tile(4, 3) + # CHECK: %[[t44:.*]] = AIE.tile(4, 4) + # CHECK: %[[t54:.*]] = AIE.tile(5, 4) + # CHECK: %[[t60:.*]] = AIE.tile(6, 0) + # CHECK: %[[t63:.*]] = AIE.tile(6, 3) + # CHECK: %[[t70:.*]] = AIE.tile(7, 0) + # CHECK: %[[t72:.*]] = AIE.tile(7, 2) + # CHECK: %[[t83:.*]] = AIE.tile(8, 3) + # CHECK: %[[t84:.*]] = AIE.tile(8, 4) + # + # CHECK: AIE.flow(%[[t20]], DMA : 0, %[[t63]], DMA : 0) + # CHECK: AIE.flow(%[[t20]], DMA : 1, %[[t83]], DMA : 0) + # CHECK: AIE.flow(%[[t30]], DMA : 0, %[[t72]], DMA : 0) + # CHECK: AIE.flow(%[[t30]], DMA : 1, %[[t54]], DMA : 0) + # + # CHECK: AIE.flow(%[[t34]], Core : 0, %[[t63]], Core : 1) + # CHECK: AIE.flow(%[[t34]], DMA : 1, %[[t70]], DMA : 0) + # CHECK: AIE.flow(%[[t43]], Core : 0, %[[t84]], Core : 1) + # CHECK: AIE.flow(%[[t43]], DMA : 1, %[[t60]], DMA : 1) + # + # CHECK: AIE.flow(%[[t44]], Core : 0, %[[t54]], Core : 1) + # CHECK: AIE.flow(%[[t44]], DMA : 1, %[[t60]], DMA : 0) + # CHECK: AIE.flow(%[[t54]], Core : 0, %[[t43]], Core : 1) + # CHECK: AIE.flow(%[[t54]], DMA : 1, %[[t30]], DMA : 1) + # + # CHECK: AIE.flow(%[[t60]], DMA : 0, %[[t44]], DMA : 0) + # CHECK: AIE.flow(%[[t60]], DMA : 1, %[[t43]], DMA : 0) + # CHECK: AIE.flow(%[[t63]], Core : 0, %[[t34]], Core : 1) + # CHECK: AIE.flow(%[[t63]], DMA : 1, %[[t20]], DMA : 1) + # + # CHECK: AIE.flow(%[[t70]], DMA : 0, %[[t34]], DMA : 0) + # CHECK: AIE.flow(%[[t70]], DMA : 1, %[[t84]], DMA : 0) + # CHECK: AIE.flow(%[[t72]], Core : 0, %[[t83]], Core : 1) + # CHECK: AIE.flow(%[[t72]], DMA : 1, %[[t30]], DMA : 0) + # + # CHECK: AIE.flow(%[[t83]], Core : 0, %[[t44]], Core : 1) + # CHECK: AIE.flow(%[[t83]], DMA : 1, %[[t20]], DMA : 0) + # CHECK: AIE.flow(%[[t84]], Core : 0, %[[t72]], Core : 1) + # CHECK: AIE.flow(%[[t84]], DMA : 1, %[[t70]], DMA : 1) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_flow_test_2 +@run +def test_flow_test_2(): + with open(Path(THIS_FILE).parent.parent / "create-flows" / "flow_test_2.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: %[[t01:.*]] = AIE.tile(0, 1) + # CHECK: %[[t02:.*]] = AIE.tile(0, 2) + # CHECK: %[[t03:.*]] = AIE.tile(0, 3) + # CHECK: %[[t04:.*]] = AIE.tile(0, 4) + # CHECK: %[[t11:.*]] = AIE.tile(1, 1) + # CHECK: %[[t12:.*]] = AIE.tile(1, 2) + # CHECK: %[[t13:.*]] = AIE.tile(1, 3) + # CHECK: %[[t14:.*]] = AIE.tile(1, 4) + # CHECK: %[[t20:.*]] = AIE.tile(2, 0) + # CHECK: %[[t21:.*]] = AIE.tile(2, 1) + # CHECK: %[[t22:.*]] = AIE.tile(2, 2) + # CHECK: %[[t23:.*]] = AIE.tile(2, 3) + # CHECK: %[[t24:.*]] = AIE.tile(2, 4) + # CHECK: %[[t30:.*]] = AIE.tile(3, 0) + # CHECK: %[[t31:.*]] = AIE.tile(3, 1) + # CHECK: %[[t32:.*]] = AIE.tile(3, 2) + # CHECK: %[[t33:.*]] = AIE.tile(3, 3) + # CHECK: %[[t34:.*]] = AIE.tile(3, 4) + + # CHECK: AIE.flow(%[[t01]], Core : 0, %[[t12]], Core : 0) + # CHECK: AIE.flow(%[[t02]], DMA : 0, %[[t20]], DMA : 0) + # CHECK: AIE.flow(%[[t04]], Core : 0, %[[t13]], Core : 0) + # CHECK: AIE.flow(%[[t11]], Core : 0, %[[t01]], Core : 0) + # CHECK: AIE.flow(%[[t12]], Core : 0, %[[t02]], Core : 0) + # CHECK: AIE.flow(%[[t13]], DMA : 0, %[[t20]], DMA : 1) + # CHECK: AIE.flow(%[[t14]], Core : 0, %[[t04]], Core : 0) + # CHECK: AIE.flow(%[[t20]], DMA : 0, %[[t11]], DMA : 0) + # CHECK: AIE.flow(%[[t20]], DMA : 1, %[[t14]], DMA : 0) + # CHECK: AIE.flow(%[[t21]], Core : 0, %[[t33]], Core : 0) + # CHECK: AIE.flow(%[[t22]], Core : 0, %[[t34]], Core : 0) + # CHECK: AIE.flow(%[[t23]], Core : 1, %[[t34]], Core : 1) + # CHECK: AIE.flow(%[[t23]], DMA : 0, %[[t30]], DMA : 0) + # CHECK: AIE.flow(%[[t24]], Core : 0, %[[t23]], Core : 0) + # CHECK: AIE.flow(%[[t24]], Core : 1, %[[t33]], Core : 1) + # CHECK: AIE.flow(%[[t30]], DMA : 0, %[[t21]], DMA : 0) + # CHECK: AIE.flow(%[[t30]], DMA : 1, %[[t31]], DMA : 1) + # CHECK: AIE.flow(%[[t31]], Core : 1, %[[t23]], Core : 1) + # CHECK: AIE.flow(%[[t32]], DMA : 1, %[[t30]], DMA : 1) + # CHECK: AIE.flow(%[[t33]], Core : 0, %[[t22]], Core : 0) + # CHECK: AIE.flow(%[[t33]], Core : 1, %[[t32]], Core : 1) + # CHECK: AIE.flow(%[[t34]], Core : 0, %[[t24]], Core : 0) + # CHECK: AIE.flow(%[[t34]], Core : 1, %[[t24]], Core : 1) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_flow_test_3 +@run +def test_flow_test_3(): + with open(Path(THIS_FILE).parent.parent / "create-flows" / "flow_test_3.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: %[[t01:.*]] = AIE.tile(0, 1) + # CHECK: %[[t02:.*]] = AIE.tile(0, 2) + # CHECK: %[[t03:.*]] = AIE.tile(0, 3) + # CHECK: %[[t04:.*]] = AIE.tile(0, 4) + # CHECK: %[[t11:.*]] = AIE.tile(1, 1) + # CHECK: %[[t12:.*]] = AIE.tile(1, 2) + # CHECK: %[[t13:.*]] = AIE.tile(1, 3) + # CHECK: %[[t14:.*]] = AIE.tile(1, 4) + # CHECK: %[[t20:.*]] = AIE.tile(2, 0) + # CHECK: %[[t21:.*]] = AIE.tile(2, 1) + # CHECK: %[[t22:.*]] = AIE.tile(2, 2) + # CHECK: %[[t23:.*]] = AIE.tile(2, 3) + # CHECK: %[[t24:.*]] = AIE.tile(2, 4) + # CHECK: %[[t30:.*]] = AIE.tile(3, 0) + # CHECK: %[[t71:.*]] = AIE.tile(7, 1) + # CHECK: %[[t72:.*]] = AIE.tile(7, 2) + # CHECK: %[[t73:.*]] = AIE.tile(7, 3) + # CHECK: %[[t74:.*]] = AIE.tile(7, 4) + # CHECK: %[[t81:.*]] = AIE.tile(8, 1) + # CHECK: %[[t82:.*]] = AIE.tile(8, 2) + # CHECK: %[[t83:.*]] = AIE.tile(8, 3) + # CHECK: %[[t84:.*]] = AIE.tile(8, 4) + # + # CHECK: AIE.flow(%[[t01]], Core : 0, %[[t83]], Core : 0) + # CHECK: AIE.flow(%[[t01]], Core : 1, %[[t72]], Core : 1) + # CHECK: AIE.flow(%[[t02]], Core : 1, %[[t24]], Core : 1) + # CHECK: AIE.flow(%[[t03]], Core : 0, %[[t71]], Core : 0) + # CHECK: AIE.flow(%[[t11]], Core : 0, %[[t24]], Core : 0) + # CHECK: AIE.flow(%[[t14]], Core : 0, %[[t01]], Core : 0) + # CHECK: AIE.flow(%[[t20]], DMA : 0, %[[t03]], DMA : 0) + # CHECK: AIE.flow(%[[t20]], DMA : 1, %[[t83]], DMA : 1) + # CHECK: AIE.flow(%[[t21]], Core : 0, %[[t73]], Core : 0) + # CHECK: AIE.flow(%[[t24]], Core : 1, %[[t71]], Core : 1) + # CHECK: AIE.flow(%[[t24]], DMA : 0, %[[t20]], DMA : 0) + # CHECK: AIE.flow(%[[t30]], DMA : 0, %[[t14]], DMA : 0) + # CHECK: AIE.flow(%[[t71]], Core : 0, %[[t84]], Core : 0) + # CHECK: AIE.flow(%[[t71]], Core : 1, %[[t84]], Core : 1) + # CHECK: AIE.flow(%[[t72]], Core : 1, %[[t02]], Core : 1) + # CHECK: AIE.flow(%[[t73]], Core : 0, %[[t82]], Core : 0) + # CHECK: AIE.flow(%[[t82]], DMA : 0, %[[t30]], DMA : 0) + # CHECK: AIE.flow(%[[t83]], Core : 0, %[[t21]], Core : 0) + # CHECK: AIE.flow(%[[t83]], Core : 1, %[[t01]], Core : 1) + # CHECK: AIE.flow(%[[t84]], Core : 0, %[[t11]], Core : 0) + # CHECK: AIE.flow(%[[t84]], DMA : 1, %[[t20]], DMA : 1) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_many_flows +@run +def test_many_flows(): + with open(Path(THIS_FILE).parent.parent / "create-flows" / "many_flows.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: %[[T02:.*]] = AIE.tile(0, 2) + # CHECK: %[[T03:.*]] = AIE.tile(0, 3) + # CHECK: %[[T11:.*]] = AIE.tile(1, 1) + # CHECK: %[[T13:.*]] = AIE.tile(1, 3) + # CHECK: %[[T20:.*]] = AIE.tile(2, 0) + # CHECK: %[[T22:.*]] = AIE.tile(2, 2) + # CHECK: %[[T30:.*]] = AIE.tile(3, 0) + # CHECK: %[[T31:.*]] = AIE.tile(3, 1) + # CHECK: %[[T60:.*]] = AIE.tile(6, 0) + # CHECK: %[[T70:.*]] = AIE.tile(7, 0) + # CHECK: %[[T73:.*]] = AIE.tile(7, 3) + # CHECK: AIE.flow(%[[T02]], Core : 1, %[[T22]], Core : 1) + # CHECK: AIE.flow(%[[T02]], DMA : 0, %[[T60]], DMA : 0) + # CHECK: AIE.flow(%[[T03]], Core : 0, %[[T13]], Core : 0) + # CHECK: AIE.flow(%[[T03]], Core : 1, %[[T02]], Core : 0) + # CHECK: AIE.flow(%[[T03]], DMA : 0, %[[T70]], DMA : 0) + # CHECK: AIE.flow(%[[T13]], Core : 1, %[[T22]], Core : 0) + # CHECK: AIE.flow(%[[T13]], DMA : 0, %[[T70]], DMA : 1) + # CHECK: AIE.flow(%[[T22]], DMA : 0, %[[T60]], DMA : 1) + # CHECK: AIE.flow(%[[T31]], DMA : 0, %[[T20]], DMA : 1) + # CHECK: AIE.flow(%[[T31]], DMA : 1, %[[T30]], DMA : 1) + # CHECK: AIE.flow(%[[T73]], Core : 0, %[[T31]], Core : 0) + # CHECK: AIE.flow(%[[T73]], Core : 1, %[[T31]], Core : 1) + # CHECK: AIE.flow(%[[T73]], DMA : 0, %[[T20]], DMA : 0) + # CHECK: AIE.flow(%[[T73]], DMA : 1, %[[T30]], DMA : 0) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_many_flows2 +@run +def test_many_flows2(): + with open(Path(THIS_FILE).parent.parent / "create-flows" / "many_flows2.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: %[[T02:.*]] = AIE.tile(0, 2) + # CHECK: %[[T03:.*]] = AIE.tile(0, 3) + # CHECK: %[[T11:.*]] = AIE.tile(1, 1) + # CHECK: %[[T13:.*]] = AIE.tile(1, 3) + # CHECK: %[[T20:.*]] = AIE.tile(2, 0) + # CHECK: %[[T22:.*]] = AIE.tile(2, 2) + # CHECK: %[[T30:.*]] = AIE.tile(3, 0) + # CHECK: %[[T31:.*]] = AIE.tile(3, 1) + # CHECK: %[[T60:.*]] = AIE.tile(6, 0) + # CHECK: %[[T70:.*]] = AIE.tile(7, 0) + # CHECK: %[[T73:.*]] = AIE.tile(7, 3) + # + # CHECK: AIE.flow(%[[T02]], DMA : 0, %[[T60]], DMA : 0) + # CHECK: AIE.flow(%[[T03]], Core : 0, %[[T02]], Core : 1) + # CHECK: AIE.flow(%[[T03]], Core : 1, %[[T02]], Core : 0) + # CHECK: AIE.flow(%[[T03]], DMA : 0, %[[T30]], DMA : 0) + # CHECK: AIE.flow(%[[T03]], DMA : 1, %[[T70]], DMA : 1) + # CHECK: AIE.flow(%[[T13]], Core : 1, %[[T31]], Core : 1) + # CHECK: AIE.flow(%[[T22]], Core : 0, %[[T13]], Core : 0) + # CHECK: AIE.flow(%[[T22]], DMA : 0, %[[T20]], DMA : 0) + # CHECK: AIE.flow(%[[T31]], DMA : 0, %[[T20]], DMA : 1) + # CHECK: AIE.flow(%[[T31]], DMA : 1, %[[T30]], DMA : 1) + # CHECK: AIE.flow(%[[T73]], Core : 0, %[[T31]], Core : 0) + # CHECK: AIE.flow(%[[T73]], Core : 1, %[[T22]], Core : 1) + # CHECK: AIE.flow(%[[T73]], DMA : 0, %[[T60]], DMA : 1) + # CHECK: AIE.flow(%[[T73]], DMA : 1, %[[T70]], DMA : 0) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_memtile +@run +def test_memtile(): + with open(Path(THIS_FILE).parent.parent / "create-flows" / "memtile.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: %[[T04:.*]] = AIE.tile(0, 4) + # CHECK: %[[T03:.*]] = AIE.tile(0, 3) + # CHECK: %[[T02:.*]] = AIE.tile(0, 2) + # CHECK: %[[T01:.*]] = AIE.tile(0, 1) + # CHECK: AIE.flow(%[[T04]], DMA : 0, %[[T02]], DMA : 4) + # CHECK: AIE.flow(%[[T04]], DMA : 1, %[[T02]], DMA : 5) + # CHECK: AIE.flow(%[[T03]], DMA : 0, %[[T02]], DMA : 2) + # CHECK: AIE.flow(%[[T03]], DMA : 1, %[[T02]], DMA : 3) + # CHECK: AIE.flow(%[[T02]], DMA : 0, %[[T01]], DMA : 0) + # CHECK: AIE.flow(%[[T02]], DMA : 1, %[[T01]], DMA : 1) + # CHECK: AIE.flow(%[[T02]], DMA : 2, %[[T03]], DMA : 0) + # CHECK: AIE.flow(%[[T02]], DMA : 3, %[[T03]], DMA : 1) + # CHECK: AIE.flow(%[[T02]], DMA : 4, %[[T04]], DMA : 0) + # CHECK: AIE.flow(%[[T02]], DMA : 5, %[[T04]], DMA : 1) + # CHECK: AIE.flow(%[[T01]], DMA : 0, %[[T02]], DMA : 0) + # CHECK: AIE.flow(%[[T01]], DMA : 1, %[[T02]], DMA : 1) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_memtile_routing_constraints +@run +def test_memtile_routing_constraints(): + with open( + Path(THIS_FILE).parent.parent + / "create-flows" + / "memtile_routing_constraints.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) + + # %[[T24:.*]] = AIE.tile(2, 4) + # %[[T23:.*]] = AIE.tile(2, 3) + # %[[T22:.*]] = AIE.tile(2, 2) + # %[[T21:.*]] = AIE.tile(2, 1) + # %[[T20:.*]] = AIE.tile(2, 0) + # AIE.flow(%[[T22]], DMA : 0, %[[T21]], DMA : 0) + # AIE.flow(%[[T23]], DMA : 0, %[[T20]], DMA : 0) + 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(): + with open( + Path(THIS_FILE).parent.parent / "create-flows" / "more_flows_shim.mlir" + ) as f: + for mlir_module in f.read().split("// -----"): + mlir_module = Module.parse(mlir_module) + 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) -def testPythonPassDemo(): - # CHECK-LABEL: testPythonPassDemo - print("\nTEST: testPythonPassDemo") - - def print_ops(op): - print(op.name) - - module = """ - module { - %12 = AIE.tile(1, 2) - %buf = AIE.buffer(%12) : memref<256xi32> - %4 = AIE.core(%12) { - %0 = arith.constant 0 : i32 - %1 = arith.constant 0 : index - memref.store %0, %buf[%1] : memref<256xi32> - AIE.end - } - } + print(mlir_module) + + # CHECK-LABEL: test70 + # CHECK: %[[T70:.*]] = AIE.tile(7, 0) + # CHECK: %[[T71:.*]] = AIE.tile(7, 1) + # CHECK: %[[SB70:.*]] = AIE.switchbox(%[[T70]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %[[SH70:.*]] = AIE.shimmux(%[[T70]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %[[SB71:.*]] = AIE.switchbox(%[[T71]]) { + # CHECK: AIE.connect + # CHECK: } + + # CHECK-LABEL: test60 + # CHECK: %[[T60:.*]] = AIE.tile(6, 0) + # CHECK: %[[T61:.*]] = AIE.tile(6, 1) + # CHECK: %[[SB60:.*]] = AIE.switchbox(%[[T60]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %[[SH60:.*]] = AIE.shimmux(%[[T60]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %[[SB61:.*]] = AIE.switchbox(%[[T61]]) { + # CHECK: AIE.connect + # CHECK: } + + # CHECK-LABEL: test40 + # CHECK: %[[T40:.*]] = AIE.tile(4, 0) + # CHECK: %[[T41:.*]] = AIE.tile(4, 1) + # CHECK: %[[SB40:.*]] = AIE.switchbox(%[[T40]]) { + # CHECK: AIE.connect + # CHECK: AIE.connect + # CHECK: } + # CHECK: %[[SB41:.*]] = AIE.switchbox(%[[T41]]) { + # CHECK: AIE.connect + # CHECK: AIE.connect + # CHECK: } + + # CHECK-LABEL: test100 + # CHECK: %[[T100:.*]] = AIE.tile(10, 0) + # CHECK: %[[T101:.*]] = AIE.tile(10, 1) + # CHECK: %[[SB100:.*]] = AIE.switchbox(%[[T100]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %[[SH100:.*]] = AIE.shimmux(%[[T100]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %[[SB101:.*]] = AIE.switchbox(%[[T101]]) { + # CHECK: AIE.connect + # CHECK: } + + +# CHECK-LABEL: TEST: test_over_flows +@run +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=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(%[[T71]], DMA : 0, %[[T20]], DMA : 0) + # CHECK: AIE.flow(%[[T71]], DMA : 1, %[[T20]], DMA : 1) + # CHECK: AIE.flow(%[[T72]], DMA : 0, %[[T60]], DMA : 0) + # CHECK: AIE.flow(%[[T72]], DMA : 1, %[[T60]], DMA : 1) + # CHECK: AIE.flow(%[[T73]], DMA : 0, %[[T70]], DMA : 0) + # CHECK: AIE.flow(%[[T73]], DMA : 1, %[[T70]], DMA : 1) + # CHECK: AIE.flow(%[[T83]], DMA : 0, %[[T30]], DMA : 0) + # CHECK: AIE.flow(%[[T83]], DMA : 1, %[[T30]], DMA : 1) + 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=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: %[[T01:.*]] = AIE.tile(0, 1) + # CHECK: %[[T12:.*]] = AIE.tile(1, 2) + # CHECK: AIE.flow(%[[T01]], DMA : 0, %[[T12]], Core : 1) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_simple2 +@run +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=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: %[[T23:.*]] = AIE.tile(2, 3) + # CHECK: %[[T32:.*]] = AIE.tile(3, 2) + # CHECK: AIE.flow(%[[T23]], Core : 1, %[[T32]], DMA : 0) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_simple_flows2 +@run +def test_simple_flows2(): + with open( + Path(THIS_FILE).parent.parent / "create-flows" / "simple_flows2.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: %[[T23:.*]] = AIE.tile(2, 3) + # CHECK: %[[T22:.*]] = AIE.tile(2, 2) + # CHECK: %[[T11:.*]] = AIE.tile(1, 1) + # CHECK: AIE.flow(%[[T23]], Core : 0, %[[T22]], Core : 1) + # CHECK: AIE.flow(%[[T22]], Core : 0, %[[T11]], Core : 0) + print(mlir_module) + + +# CHECK-LABEL: TEST: test_simple_flows_shim +@run +def test_simple_flows_shim(): + with open( + Path(THIS_FILE).parent.parent / "create-flows" / "simple_flows_shim.mlir" + ) as f: + for mlir_module in f.read().split("// -----"): + mlir_module = Module.parse(mlir_module) + 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) + + print(mlir_module) + + # CHECK: %[[T21:.*]] = AIE.tile(2, 1) + # CHECK: %[[T20:.*]] = AIE.tile(2, 0) + # CHECK: %{{.*}} = AIE.switchbox(%[[T20]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %{{.*}} = AIE.switchbox(%[[T21]]) { + # CHECK: AIE.connect + # CHECK: } + + # CHECK: %[[T20:.*]] = AIE.tile(2, 0) + # CHECK: %[[T21:.*]] = AIE.tile(2, 1) + # CHECK: %{{.*}} = AIE.switchbox(%[[T20]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %{{.*}} = AIE.shimmux(%[[T20]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %{{.*}} = AIE.switchbox(%[[T21]]) { + # CHECK: AIE.connect + # CHECK: } - # CHECK: AIE.tile - # CHECK: AIE.buffer - # CHECK: arith.constant - # CHECK: arith.constant - # CHECK: memref.store - # CHECK: AIE.end - # CHECK: AIE.core - # CHECK: builtin.module - with Context() as ctx, Location.unknown(): - _aie_python_passes.register_python_pass_demo_pass(print_ops) - mlir_module = Module.parse(module) - PassManager.parse("builtin.module(python-pass-demo)").run(mlir_module.operation) - - -testPythonPassDemo() + # CHECK: %[[T20:.*]] = AIE.tile(2, 0) + # CHECK: %[[T30:.*]] = AIE.tile(3, 0) + # CHECK: %{{.*}} = AIE.switchbox(%[[T20]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %{{.*}} = AIE.shimmux(%[[T20]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %{{.*}} = AIE.switchbox(%[[T30]]) { + # CHECK: AIE.connect + # CHECK: } + # CHECK: %{{.*}} = AIE.shimmux(%[[T30]]) { + # CHECK: AIE.connect + # CHECK: }