Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Yu-Zhewen committed Jan 9, 2025
1 parent f519ca2 commit 5b4313b
Show file tree
Hide file tree
Showing 8 changed files with 395 additions and 313 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ class AIETargetBackend final : public IREE::HAL::TargetBackend {

void buildTranslationPassPipeline(IREE::HAL::ExecutableTargetAttr,
OpPassManager &passManager) override {

buildAMDAIETransformPassPipeline(
passManager, options.AMDAIETargetDevice, options.AMDAIENumRows,
options.AMDAIENumCols, options.useTilePipeline,
Expand Down Expand Up @@ -513,7 +512,8 @@ LogicalResult AIETargetBackend::serializeExecutable(
/*amdAIEInstallDir=*/options.amdAieInstallDir,
/*InputXCLBin=*/std::nullopt,
/*ukernel=*/options.enableAMDAIEUkernels,
/*additionalPeanoOptFlags=*/options.additionalPeanoOptFlags))) {
/*additionalPeanoOptFlags=*/options.additionalPeanoOptFlags,
/*controlPacket=*/options.controlPacket))) {
return failure();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ struct AMDAIEOptions {
enum class DeviceHAL { XRT, XRT_LITE };
DeviceHAL deviceHal{DeviceHAL::XRT_LITE};

bool controlPacket{false};

void bindOptions(OptionsBinder &binder) {
static llvm::cl::OptionCategory category("AMD AIE Options");

Expand Down Expand Up @@ -268,6 +270,10 @@ struct AMDAIEOptions {
llvm::cl::values(clEnumValN(DeviceHAL::XRT, "xrt", "xrt device HAL"),
clEnumValN(DeviceHAL::XRT_LITE, "xrt-lite",
"xrt-lite device HAL")));

binder.opt<bool>("iree-amdaie-convert-to-control-packet", controlPacket,
llvm::cl::cat(category),
llvm::cl::desc("Convert to control packet."));
}
};

Expand Down
315 changes: 315 additions & 0 deletions compiler/plugins/target/AMD-AIE/iree-amd-aie/Target/AMDAIERT.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
// Copyright 2025 The IREE Authors
//
// 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

#include "AMDAIERT.h"

#include "iree-amd-aie/aie_runtime/iree_aie_configure.h"

using namespace mlir;

using xilinx::AIE::AMSelOp;
using xilinx::AIE::BufferOp;
using xilinx::AIE::ConnectOp;
using xilinx::AIE::CoreOp;
using xilinx::AIE::DeviceOp;
using xilinx::AIE::DMABDOp;
using xilinx::AIE::DMABDPACKETOp;
using xilinx::AIE::DMAChannelDir;
using xilinx::AIE::DMAStartOp;
using xilinx::AIE::LockAction;
using xilinx::AIE::LockOp;
using xilinx::AIE::MasterSetOp;
using xilinx::AIE::MemOp;
using xilinx::AIE::MemTileDMAOp;
using xilinx::AIE::PacketRuleOp;
using xilinx::AIE::PacketRulesOp;
using xilinx::AIE::ShimMuxOp;
using xilinx::AIE::SwitchboxOp;
using xilinx::AIE::TileOp;
using xilinx::AIE::UseLockOp;

using Path = std::filesystem::path;

namespace {

mlir::iree_compiler::AMDAIE::Lock::Action toLock(xilinx::AIE::LockAction l) {
switch (l) {
case xilinx::AIE::LockAction::Acquire:
return mlir::iree_compiler::AMDAIE::Lock::Action::Acquire;
case xilinx::AIE::LockAction::AcquireGreaterEqual:
return mlir::iree_compiler::AMDAIE::Lock::Action::AcquireGreaterEqual;
case xilinx::AIE::LockAction::Release:
return mlir::iree_compiler::AMDAIE::Lock::Action::Release;
}
llvm::report_fatal_error("unhandled lock action");
}

} // namespace

namespace mlir::iree_compiler::AMDAIE {

LogicalResult addAllAieElfs(const AMDAIEDeviceModel &deviceModel,
DeviceOp &device, const Path &workDirPath,
bool aieSim) {
for (auto tileOp : device.getOps<TileOp>()) {
TileLoc tileLoc{tileOp.getCol(), tileOp.getRow()};
if (deviceModel.isShimNOCorPLTile(tileLoc.col, tileLoc.row)) continue;
if (auto coreOp = getCoreOp(tileOp)) {
std::string fileName;
if (auto elfFile = coreOp.getElfFile())
fileName = *elfFile;
else
fileName = "core_" + std::to_string(tileLoc.col) + "_" +
std::to_string(tileLoc.row) + ".elf";
if (failed(addElfToTile(deviceModel, tileLoc, workDirPath / fileName,
aieSim))) {
return failure();
}
}
}
return success();
}

LogicalResult addAllCoreEnable(const AMDAIEDeviceModel &deviceModel,
DeviceOp &device) {
// Start execution of all the cores.
for (auto tileOp : device.getOps<TileOp>()) {
TileLoc tileLoc = {tileOp.getCol(), tileOp.getRow()};
if (auto coreOp = getCoreOp(tileOp);
coreOp && failed(coreEnable(deviceModel, tileLoc)))
return failure();
}
return success();
}

LogicalResult configureLocksAndBd(Block &block, const TileLoc &tileLoc,
const AMDAIEDeviceModel &deviceModel) {
FailureOr<XAie_DmaDesc> dmaTileBd = initDMADesc(deviceModel, tileLoc);
if (failed(dmaTileBd)) return failure();
std::optional<int> acqValue, relValue, acqLockId, relLockId;
bool acqEn;
for (auto op : block.getOps<UseLockOp>()) {
// Only dyn_cast if you are going to check if it was of the type
// expected; if you aren't checking use cast instead as it will at
// least assert in debug mode with an easier to understand error than
// dereferencing.
LockOp lock = cast<LockOp>(op.getLock().getDefiningOp());
switch (toLock(op.getAction())) {
case Lock::Action::Acquire:
case Lock::Action::AcquireGreaterEqual:
acqEn = op.getAcqEn();
acqLockId = lock.getLockID();
acqValue = op.getValue().value_or(1);
if (op.getAction() == LockAction::AcquireGreaterEqual)
acqValue.value() = -acqValue.value();
break;
case Lock::Action::Release:
relLockId = lock.getLockID();
relValue = op.getValue().value_or(1);
break;
}
}
// Disable acquire and release locks if not set.
if (!acqLockId) {
acqLockId = 0;
acqValue = 0;
acqEn = false;
}
if (!relLockId) {
relLockId = 0;
relValue = 0;
}
assert(acqValue && relValue && acqLockId && relLockId &&
"expected both use_lock(acquire) and use_lock(release) with bd");
if (failed(configureDMALocks(deviceModel, dmaTileBd.value(), tileLoc,
*acqValue, *relValue, *acqLockId, *relLockId,
acqEn))) {
return failure();
}

// pull metadata related to packet routing, bdid, buffer length, size, stride
// to pass to aie-rt
DMABDOp bdOp = *block.getOps<DMABDOp>().begin();
assert(bdOp.getBdId().has_value() &&
"DMABDOp must have assigned bd_id; did you forget to run "
"aie-assign-bd-ids?");
bool validBd = true;
std::optional<uint8_t> packetType;
std::optional<uint8_t> packetID;
bool enablePacket = false;
auto maybePacketOps = block.getOps<DMABDPACKETOp>();
if (!maybePacketOps.empty()) {
assert(llvm::range_size(maybePacketOps) == 1 &&
"expected only one dma_bd_packet");
auto packetOp = *maybePacketOps.begin();
packetType = packetOp.getPacketType();
packetID = packetOp.getPacketId();
enablePacket = true;
}

BufferOp bufferOp = cast<BufferOp>(bdOp.getBuffer().getDefiningOp());
if (!bufferOp.getAddress())
return bufferOp.emitError("buffer must have address assigned");
std::optional<std::vector<BDDimLayout>> maybeDims;
if (auto dims = bdOp.getDimensions()) {
maybeDims = std::vector<BDDimLayout>{};
for (const auto &dim : (*dims)) {
maybeDims->emplace_back(BDDimLayout{dim.getSize(), dim.getStride()});
}
}

std::optional<std::vector<BDPadLayout>> maybePadDims;
if (auto dims = bdOp.getPadDimensions()) {
maybePadDims = std::vector<BDPadLayout>{};
for (const auto &dim : (*dims)) {
maybePadDims->emplace_back(
BDPadLayout{dim.getConstPadBefore(), dim.getConstPadAfter()});
}
}

bool enableNextBd = bdOp.getNextBdId().has_value();
std::optional<uint8_t> nextBdId =
enableNextBd
? std::optional<uint8_t>{static_cast<uint8_t>(*bdOp.getNextBdId())}
: std::nullopt;
std::optional<BDIterLayout> maybeIter = std::nullopt;
if (failed(configureDMABD(deviceModel, dmaTileBd.value(), tileLoc, validBd,
static_cast<uint8_t>(*bdOp.getBdId()), enableNextBd,
nextBdId, enablePacket, packetType, packetID,
*bufferOp.getAddress(), getLenInBytes(bdOp),
getOffsetInBytes(bdOp),
getBufferElementTypeWidthInBytes(bdOp), maybeDims,
maybePadDims, maybeIter))) {
return failure();
}
return success();
}

LogicalResult addInitConfig(const AMDAIEDeviceModel &deviceModel,
DeviceOp &device) {
for (auto tileOp : device.getOps<TileOp>()) {
TileLoc tileLoc = {tileOp.getCol(), tileOp.getRow()};
if (deviceModel.isShimTile(tileOp.getCol(), tileOp.getRow())) {
continue;
}
if (auto coreOp = getCoreOp(tileOp);
coreOp && failed(resetUnResetCore(deviceModel, tileLoc))) {
return failure();
}
}

// Set locks with explicit initializers
WalkResult r;
r = device.walk<WalkOrder::PreOrder>([&](LockOp lockOp) {
if (lockOp.getLockID() && lockOp.getInit()) {
TileOp t = xilinx::AIE::getTileOp(*lockOp.getOperation());
TileLoc tileLoc = {t.getCol(), t.getRow()};
Lock lock{tileLoc, static_cast<uint8_t>(*lockOp.getLockID()),
static_cast<int8_t>(*lockOp.getInit())};
if (failed(initializeLock(deviceModel, lock)))
return WalkResult::interrupt();
}
return WalkResult::advance();
});
if (r.wasInterrupted()) return failure();

auto memOps = llvm::to_vector_of<Operation *>(device.getOps<MemOp>());
llvm::append_range(memOps, device.getOps<MemTileDMAOp>());
for (Operation *memOp : memOps) {
TileOp t = xilinx::AIE::getTileOp(*memOp);
TileLoc tileLoc = {t.getCol(), t.getRow()};
if (deviceModel.isShimNOCorPLTile(tileLoc.col, tileLoc.row)) {
continue;
}

// handle DMA ops separately
for (Block &block : memOp->getRegion(0)) {
if (block.getOps<DMABDOp>().empty()) continue;
if (failed(configureLocksAndBd(block, tileLoc, deviceModel)))
return failure();
}

for (Block &block : memOp->getRegion(0)) {
for (auto op : block.getOps<DMAStartOp>()) {
DMABDOp bd = *op.getDest()->getOps<DMABDOp>().begin();
int chNum = op.getChannelIndex();
auto channelDir = static_cast<DMAChannelDir>(op.getChannelDir());
bool issueToken = tileLoc.row == 0 && channelDir == DMAChannelDir::MM2S;
bool setChannelEnable = true;
if (failed(configurePushToBdQueue(
deviceModel, tileLoc, chNum, channelDir, bd.getBdId().value(),
op.getRepeatCount(), issueToken, setChannelEnable)))
return failure();
}
}
}

// StreamSwitch (switchbox) configuration
for (auto switchboxOp : device.getOps<SwitchboxOp>()) {
TileOp t = xilinx::AIE::getTileOp(*switchboxOp.getOperation());
TileLoc tileLoc = {t.getCol(), t.getRow()};
std::vector<Connect> connects;
for (auto connectOp : switchboxOp.getOps<ConnectOp>()) {
connects.emplace_back(
Port{connectOp.getSourceBundle(), connectOp.getSourceChannel()},
Port{connectOp.getDestBundle(), connectOp.getDestChannel()},
Connect::Interconnect::SWB, tileLoc.col, tileLoc.row);
}
if (failed(configureStreamSwitch(deviceModel, tileLoc, connects))) {
return failure();
}

Block &b = switchboxOp.getConnections().front();
for (auto masterSetOp : b.getOps<MasterSetOp>()) {
std::vector<AMSel> amSels;
for (auto val : masterSetOp.getAmsels()) {
AMSelOp amsel = cast<AMSelOp>(val.getDefiningOp());
amSels.push_back({static_cast<uint8_t>(amsel.getArbiterID()),
static_cast<uint8_t>(amsel.getMsel())});
}
if (failed(configureSwitchPacketMasters(
deviceModel, tileLoc, masterSetOp.getDestBundle(),
masterSetOp.getDestChannel(), amSels,
masterSetOp->hasAttr("keep_pkt_header"))))
return failure();
}

for (auto packetRulesOp : b.getOps<PacketRulesOp>()) {
int slot = 0;
Block &block = packetRulesOp.getRules().front();
for (auto packetRuleOp : block.getOps<PacketRuleOp>()) {
AMSelOp amselOp =
cast<AMSelOp>(packetRuleOp.getAmsel().getDefiningOp());
if (failed(configureSwitchPacketSlaves(
deviceModel, tileLoc, packetRulesOp.getSourceBundle(),
packetRulesOp.getSourceChannel(),
AMSel{amselOp.getArbiterID(), amselOp.getMsel()},
packetRuleOp.getValue(), packetRuleOp.getMask(), slot)))
return failure();
slot++;
}
}
}

for (auto muxOp : device.getOps<ShimMuxOp>()) {
TileOp t = xilinx::AIE::getTileOp(*muxOp.getOperation());
TileLoc tileLoc = {t.getCol(), t.getRow()};
std::vector<Connect> connects;
for (auto connectOp : muxOp.getOps<ConnectOp>()) {
connects.emplace_back(
Port{connectOp.getSourceBundle(), connectOp.getSourceChannel()},
Port{connectOp.getDestBundle(), connectOp.getDestChannel()},
Connect::Interconnect::SHIMMUX, tileLoc.col, tileLoc.row);
}
if (failed(configureStreamSwitch(deviceModel, tileLoc, connects))) {
return failure();
}
}

return success();
}

} // namespace mlir::iree_compiler::AMDAIE
30 changes: 30 additions & 0 deletions compiler/plugins/target/AMD-AIE/iree-amd-aie/Target/AMDAIERT.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2025 The IREE Authors
//
// 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
#pragma once

#include <filesystem>

#include "aie/AIEDialect.h"

namespace mlir::iree_compiler::AMDAIE {

/// Load ELF files for all cores within the device operation.
LogicalResult addAllAieElfs(const AMDAIEDeviceModel &deviceModel,
xilinx::AIE::DeviceOp &device,
const std::filesystem::path &workDirPath,
bool aieSim);

/// Update core control registers to enable all cores within the device
/// operation.
LogicalResult addAllCoreEnable(const AMDAIEDeviceModel &deviceModel,
xilinx::AIE::DeviceOp &device);

/// Utility function to reset all cores, initialize hardware locks,
/// and configure all switchboxes.
LogicalResult addInitConfig(const AMDAIEDeviceModel &deviceModel,
xilinx::AIE::DeviceOp &device);

} // namespace mlir::iree_compiler::AMDAIE
Loading

0 comments on commit 5b4313b

Please sign in to comment.