Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/aie/Dialect/AIE/Transforms/AIEPasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace xilinx::AIE {
#define GEN_PASS_CLASSES
#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"

std::unique_ptr<mlir::OperationPass<DeviceOp>> createAIEPlaceTilesPass();
std::unique_ptr<mlir::OperationPass<DeviceOp>>
createAIEAssignBufferAddressesPass();
std::unique_ptr<mlir::OperationPass<DeviceOp>> createAIEAssignLockIDsPass();
Expand Down
20 changes: 20 additions & 0 deletions include/aie/Dialect/AIE/Transforms/AIEPasses.td
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@

include "mlir/Pass/PassBase.td"

def AIEPlaceTiles : Pass<"aie-place-tiles", "DeviceOp"> {
let summary = "Place logical tiles onto physical AIE tiles";
let description = [{
Sequential placer algorithm places core tiles in a
column-major order and places fifo-connected Shim/Mem tiles
near its core tiles. One or more aie.logical_tile operation is mapped
to one aie.tile.
}];

let constructor = "xilinx::AIE::createAIEPlaceTilesPass()";

let dependentDialects = ["xilinx::AIE::AIEDialect"];

let options = [
Option<"clPlacerName", "placer", "std::string",
/*default=*/"\"sequential_placer\"",
"Placement algorithm to use (currently only 'sequential_placer')">
];
}

def AIEAssignBufferAddresses : Pass<"aie-assign-buffer-addresses", "DeviceOp"> {
let summary = "Assign memory locations for buffers in each tile";
let description = [{
Expand Down
124 changes: 124 additions & 0 deletions include/aie/Dialect/AIE/Transforms/AIEPlacer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//===- AIEPlacer.h ----------------------------------------------*- C++ -*-===//
//
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// (c) Copyright 2024-2026 Advanced Micro Devices, Inc.
//
//===----------------------------------------------------------------------===//
// This file contains the interface for tile placement algorithms.
// Placers assign physical tile coordinates to logical tiles.
//===----------------------------------------------------------------------===//

#ifndef AIE_PLACER_H
#define AIE_PLACER_H

#include "aie/Dialect/AIE/IR/AIEDialect.h"
#include "aie/Dialect/AIE/IR/AIETargetModel.h"

namespace xilinx::AIE {

// maps logical tile operations to physical coordinates
using PlacementResult = llvm::DenseMap<mlir::Operation *, TileID>;

// Track available tiles and resource usage
struct TileAvailability {
std::vector<TileID> compTiles;
std::vector<TileID> nonCompTiles; // Memory and shim tiles

llvm::DenseMap<TileID, int> inputChannelsUsed;
llvm::DenseMap<TileID, int> outputChannelsUsed;

void removeTile(TileID tile, AIETileType type);
};

// Abstract placer interface
class Placer {
public:
Placer() = default;
virtual ~Placer() = default;

virtual void initialize(DeviceOp device,
const AIETargetModel &targetModel) = 0;

virtual mlir::LogicalResult
place(llvm::ArrayRef<mlir::Operation *> logicalTiles,
llvm::ArrayRef<mlir::Operation *> objectFifos,
llvm::ArrayRef<mlir::Operation *> cores, PlacementResult &result) = 0;

virtual llvm::StringRef getName() const = 0;
};

// Sequential placement algorithm
//
// Places logical tiles to physical tiles using a simple strategy:
// - Compute tiles: Sequential row-major placement
// - Memory/shim tiles: Channel capacity placement near common column
//
// Core-to-core connections are NOT validated because SequentialPlacer
// doesn't account for shared memory optimization.
//
// Shim/Mem tiles with identical placement constraints and sufficient
// DMA capacity are merged to the same physical tile.
class SequentialPlacer : public Placer {
public:
SequentialPlacer(std::optional<int> coresPerCol = std::nullopt)
: coresPerCol(coresPerCol) {}

void initialize(DeviceOp device, const AIETargetModel &targetModel) override;

mlir::LogicalResult place(llvm::ArrayRef<mlir::Operation *> logicalTiles,
llvm::ArrayRef<mlir::Operation *> objectFifos,
llvm::ArrayRef<mlir::Operation *> cores,
PlacementResult &result) override;

llvm::StringRef getName() const override { return "sequential_placer"; }

private:
std::optional<int> coresPerCol;
TileAvailability availability;
const AIETargetModel *targetModel;
DeviceOp device;

int getCommonColumn(const PlacementResult &result);

std::optional<TileID> findTileWithCapacity(int targetCol,
std::vector<TileID> &tiles,
int requiredInputChannels,
int requiredOutputChannels,
AIETileType requestedType);

void updateChannelUsage(TileID tile, bool isOutput, int numChannels);

bool hasAvailableChannels(TileID tile, int inputChannels, int outputChannels);

mlir::LogicalResult validateAndUpdateChannelUsage(
LogicalTileOp logicalTile, TileID tile,
const llvm::DenseMap<mlir::Operation *, std::pair<int, int>>
&channelRequirements,
bool isConstrained);
};

// PlacementAnalysis integrates the Pathfinder class into the MLIR
// environment.
class PlacementAnalysis {
public:
PlacementAnalysis() : placer(std::make_shared<SequentialPlacer>()) {}
explicit PlacementAnalysis(std::shared_ptr<Placer> p)
: placer(std::move(p)) {}

mlir::LogicalResult runAnalysis(DeviceOp &device);

std::optional<TileID> getPlacement(mlir::Operation *logicalTile) const;

Placer &getPlacer() { return *placer; }

private:
std::shared_ptr<Placer> placer;
PlacementResult result;
};

} // namespace xilinx::AIE

#endif // AIE_PLACER_H
99 changes: 99 additions & 0 deletions lib/Dialect/AIE/Transforms/AIEPlaceTiles.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//===- AIEPlaceTiles.cpp ----------------------------------------*- C++ -*-===//
//
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// (c) Copyright 2024-2026 Advanced Micro Devices, Inc.
//
//===----------------------------------------------------------------------===//

#include "aie/Dialect/AIE/IR/AIEDialect.h"
#include "aie/Dialect/AIE/Transforms/AIEPasses.h"
#include "aie/Dialect/AIE/Transforms/AIEPlacer.h"

#include "mlir/IR/PatternMatch.h"
#include "mlir/Transforms/DialectConversion.h"

#define DEBUG_TYPE "aie-place-tiles"

using namespace mlir;
using namespace xilinx;
using namespace xilinx::AIE;

namespace {

struct ConvertLogicalTileToTile : OpConversionPattern<LogicalTileOp> {
ConvertLogicalTileToTile(MLIRContext *context, DeviceOp &d,
PlacementAnalysis &a, PatternBenefit benefit = 1)
: OpConversionPattern(context, benefit), device(d), analyzer(a) {}

LogicalResult
matchAndRewrite(LogicalTileOp logicalTile, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
// Get pre-computed placement from analysis
auto placement = analyzer.getPlacement(logicalTile);
if (!placement) {
return logicalTile.emitError("no placement found for logical tile");
}

// handle merging multiple logical tiles to same physical tile
TileOp tileOp =
TileOp::getOrCreate(rewriter, device, placement->col, placement->row);

// Copy allocation_scheme if present
if (auto scheme = logicalTile.getAllocationScheme())
tileOp.setAllocationScheme(scheme);

// Replace all uses and erase logical tile
rewriter.replaceOp(logicalTile, tileOp.getResult());
return success();
}

private:
DeviceOp device;
PlacementAnalysis &analyzer;
};

} // namespace

struct AIEPlaceTilesPass : AIEPlaceTilesBase<AIEPlaceTilesPass> {
void runOnOperation() override {
DeviceOp device = getOperation();

// Create placer
std::shared_ptr<Placer> placer;
if (clPlacerName == "sequential_placer") {
placer = std::make_shared<SequentialPlacer>();
} else {
device.emitError() << "Unknown placer: " << clPlacerName;
return signalPassFailure();
}

// Run placement analysis
PlacementAnalysis analyzer(placer);
if (failed(analyzer.runAnalysis(device)))
return signalPassFailure();

// Apply placement using conversion pattern
ConversionTarget target(getContext());
target.addLegalOp<TileOp>();
target.addIllegalOp<LogicalTileOp>();

// Mark all other AIE dialect operations as legal
// They will have their operands automatically updated when LogicalTileOp ->
// TileOp
target.addLegalDialect<AIEDialect>();

RewritePatternSet patterns(&getContext());
patterns.add<ConvertLogicalTileToTile>(device.getContext(), device,
analyzer);

if (failed(applyPartialConversion(device, target, std::move(patterns))))
return signalPassFailure();
}
};

std::unique_ptr<OperationPass<DeviceOp>> AIE::createAIEPlaceTilesPass() {
return std::make_unique<AIEPlaceTilesPass>();
}
Loading
Loading