Skip to content

Commit e62aad0

Browse files
authored
Allow multiple devices in one design and add device configuration op (aiex.configure) (#2532)
1 parent 25d82ea commit e62aad0

File tree

118 files changed

+1899
-832
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+1899
-832
lines changed

docs/buildHostLin.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ For your design of interest, for instance from [programming_examples](../program
403403
1. Run (program arguments are just an example for add_one design)
404404
```bash
405405
cd Release
406-
.\<testName>.exe -x ..\..\build\final.xclbin -k MLIR_AIE -i ..\..\build\insts.txt -v 1
406+
.\<testName>.exe -x ..\..\build\main.xclbin -k MLIR_AIE -i ..\..\build\main_sequence.bin -v 1
407407
```
408408
409409
# Troubleshooting

include/aie-c/Translation.h

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,23 @@ extern "C" {
2121
MLIR_CAPI_EXPORTED MlirStringRef aieTranslateAIEVecToCpp(MlirOperation op,
2222
bool aie2);
2323
MLIR_CAPI_EXPORTED MlirStringRef aieTranslateModuleToLLVMIR(MlirOperation op);
24-
MLIR_CAPI_EXPORTED MlirStringRef aieTranslateNpuToBinary(MlirOperation op,
25-
MlirStringRef name);
24+
MLIR_CAPI_EXPORTED MlirStringRef aieTranslateNpuToBinary(
25+
MlirOperation op, MlirStringRef deviceName, MlirStringRef sequenceName);
2626
MLIR_CAPI_EXPORTED MlirStringRef
27-
aieTranslateControlPacketsToUI32Vec(MlirOperation op);
28-
MLIR_CAPI_EXPORTED MlirStringRef aieTranslateToXAIEV2(MlirOperation op);
29-
MLIR_CAPI_EXPORTED MlirStringRef aieTranslateToHSA(MlirOperation op);
27+
aieTranslateControlPacketsToUI32Vec(MlirOperation op, MlirStringRef deviceName);
28+
MLIR_CAPI_EXPORTED MlirStringRef aieTranslateToXAIEV2(MlirOperation op,
29+
MlirStringRef deviceName);
30+
MLIR_CAPI_EXPORTED MlirStringRef aieTranslateToHSA(MlirOperation op,
31+
MlirStringRef deviceName);
3032
MLIR_CAPI_EXPORTED MlirStringRef aieTranslateToBCF(MlirOperation op, int col,
31-
int row);
33+
int row,
34+
MlirStringRef deviceName);
3235
MLIR_CAPI_EXPORTED MlirStringRef aieLLVMLink(MlirStringRef *modules,
3336
int nModules);
34-
MLIR_CAPI_EXPORTED MlirLogicalResult
35-
aieTranslateToCDODirect(MlirOperation moduleOp, MlirStringRef workDirPath,
36-
bool bigEndian, bool emitUnified, bool cdoDebug,
37-
bool aieSim, bool xaieDebug, bool enableCores);
37+
MLIR_CAPI_EXPORTED MlirLogicalResult aieTranslateToCDODirect(
38+
MlirOperation moduleOp, MlirStringRef workDirPath, MlirStringRef deviceName,
39+
bool bigEndian, bool emitUnified, bool cdoDebug, bool aieSim,
40+
bool xaieDebug, bool enableCores);
3841
MLIR_CAPI_EXPORTED MlirOperation aieTranslateBinaryToTxn(MlirContext ctx,
3942
MlirStringRef binary);
4043

include/aie/Conversion/Passes.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ def ConvertAIEToTransaction : Pass<"convert-aie-to-transaction",
7272
let options = [
7373
Option<"clElfDir", "elf-dir", "std::string", /*default=*/"",
7474
"Where to find ELF files">,
75+
Option<"clDeviceName", "device-name", "std::string", /*default=*/"",
76+
"Which device to generate code for; if none specified, process all devices">,
7577
];
7678
}
7779

@@ -94,6 +96,8 @@ def ConvertAIEToControlPackets : Pass<"convert-aie-to-control-packets",
9496
let options = [
9597
Option<"clElfDir", "elf-dir", "std::string", /*default=*/"",
9698
"Where to find ELF files">,
99+
Option<"clDeviceName", "device-name", "std::string", /*default=*/"",
100+
"Which device to generate code for; if none specified, process all devices">,
97101
];
98102
}
99103

include/aie/Dialect/AIE/IR/AIEDialect.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "mlir/IR/OpImplementation.h"
2323
#include "mlir/Interfaces/DataLayoutInterfaces.h"
2424

25+
#include "llvm/ADT/StringRef.h"
26+
2527
namespace xilinx::AIE {
2628

2729
// Check that the given DMA-like op (e.g. MemOp, ShimDMAOp)

include/aie/Dialect/AIE/IR/AIEOps.td

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@ class AIE_Op<string mnemonic, list<Trait> traits = []> :
2727

2828

2929
def AIE_DeviceOp: AIE_Op<"device", [
30-
AIETarget, HasParent<"mlir::ModuleOp">,
31-
SymbolTable, SingleBlockImplicitTerminator<"EndOp">, IsolatedFromAbove
30+
Symbol,
31+
AIETarget,
32+
HasParent<"mlir::ModuleOp">,
33+
SymbolTable,
34+
SingleBlockImplicitTerminator<"EndOp">,
35+
IsolatedFromAbove
3236
]> {
3337
let summary = "Define an AIE design targetting a complete device";
3438
let description = [{
@@ -55,13 +59,19 @@ def AIE_DeviceOp: AIE_Op<"device", [
5559
```
5660
}];
5761

58-
let arguments = (ins AIEDevice:$device);
62+
let arguments = (
63+
ins AIEDevice:$device,
64+
DefaultValuedAttr<SymbolNameAttr, "getDefaultDeviceName()">:$sym_name
65+
);
5966
let regions = (region AnyRegion:$body_region);
6067
let assemblyFormat = [{
61-
`(` $device `)` regions attr-dict
68+
`(` $device `)` ($sym_name^)? regions attr-dict
6269
}];
6370
let extraClassDeclaration = [{
6471
const xilinx::AIE::AIETargetModel &getTargetModel();
72+
static xilinx::AIE::DeviceOp getForSymbolInModule(mlir::ModuleOp module, llvm::StringRef symbol);
73+
static xilinx::AIE::DeviceOp getForSymbolInModuleOrError(mlir::ModuleOp module, llvm::StringRef symbol);
74+
static llvm::StringRef getDefaultDeviceName() { return "main"; }
6575
}];
6676
}
6777

@@ -325,6 +335,11 @@ def AIE_CoreOp: AIE_Op<"core", [
325335
for each core. The name of this file can be be specified using the `elf_file`
326336
attribute.
327337

338+
If the `elf_file` attribute is present, no MLIR besides a terminator may be
339+
present in the core; in that case, the binary file linked dictates what
340+
will run in the core. The path specified should is relative to the MLIR
341+
file.
342+
328343
This op has an optional `stackSize` attribute, to control the amount of memory (in bytes)
329344
reserved for the stack. The default value is 1024. The stack (and other data allocations)
330345
are always stored in the local core memory, to avoid conflicts with static data allocations
@@ -359,6 +374,7 @@ def AIE_CoreOp: AIE_Op<"core", [
359374
int colIndex();
360375
int rowIndex();
361376
bool isMemWest() { return ((rowIndex() % 2) == 0); };
377+
bool isEmpty();
362378
TileOp getTileOp();
363379
using ::xilinx::AIE::TileElement::Trait<CoreOp>::getAsmResultNames;
364380
}];

include/aie/Dialect/AIE/Transforms/AIEPasses.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,12 @@ std::unique_ptr<mlir::OperationPass<DeviceOp>> createAIEAssignTileCtrlIDsPass();
6363
/// 5. rewrite stream-switches (within a bounding box) back to flows
6464
struct AIEPathfinderPass : AIERoutePathfinderFlowsBase<AIEPathfinderPass> {
6565

66-
DynamicTileAnalysis analyzer;
67-
mlir::DenseMap<TileID, mlir::Operation *> tiles;
68-
6966
AIEPathfinderPass() = default;
70-
AIEPathfinderPass(DynamicTileAnalysis analyzer)
71-
: analyzer(std::move(analyzer)) {}
7267

7368
void runOnOperation() override;
74-
void runOnFlow(DeviceOp d);
75-
void runOnPacketFlow(DeviceOp d, mlir::OpBuilder &builder);
69+
void runOnFlow(DeviceOp d, DynamicTileAnalysis &analyzer);
70+
void runOnPacketFlow(DeviceOp d, mlir::OpBuilder &builder,
71+
DynamicTileAnalysis &analyzer);
7672

7773
typedef std::pair<mlir::Operation *, Port> PhysPort;
7874

include/aie/Dialect/AIE/Transforms/AIEPasses.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def AIECoreToStandard : Pass<"aie-standard-lowering", "mlir::ModuleOp"> {
6666

6767
}];
6868
let options = [
69+
Option<"deviceName", "device", "std::string", /*default=*/"\"\"", "Device to generate code for">,
6970
Option<"tileCol", "tilecol", "unsigned",
7071
/*default=*/"-1", "X coordinate of tile to generate code for">,
7172
Option<"tileRow", "tilerow", "unsigned",

include/aie/Dialect/AIE/Transforms/AIEPathFinder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ class DynamicTileAnalysis {
245245

246246
DynamicTileAnalysis() : pathfinder(std::make_shared<Pathfinder>()) {}
247247
DynamicTileAnalysis(std::shared_ptr<Router> p) : pathfinder(std::move(p)) {}
248+
DynamicTileAnalysis(mlir::Operation *op)
249+
: pathfinder(std::make_shared<Pathfinder>()) {}
248250

249251
mlir::LogicalResult runAnalysis(DeviceOp &device);
250252

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//===- AIEUtils.h -----------------------------------------------*- C++ -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// (c) Copyright 2025 Advanced Micro Devices, Inc.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
#include "aie/Dialect/AIE/IR/AIEDialect.h"
12+
#include "mlir/Dialect/MemRef/IR/MemRef.h"
13+
14+
using namespace mlir;
15+
16+
namespace xilinx {
17+
namespace AIEX {
18+
19+
memref::GlobalOp getOrCreateDataMemref(OpBuilder &builder, AIE::DeviceOp dev,
20+
mlir::Location loc,
21+
ArrayRef<uint32_t> words);
22+
23+
}
24+
} // namespace xilinx

include/aie/Dialect/AIEX/IR/AIEX.td

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ include "aie/Dialect/AIE/IR/AIEInterfaces.td"
1717
include "mlir/IR/OpBase.td"
1818
include "mlir/IR/AttrTypeBase.td"
1919
include "mlir/IR/EnumAttr.td"
20+
include "mlir/IR/BuiltinAttributes.td"
2021
include "mlir/IR/SymbolInterfaces.td"
2122
include "mlir/Interfaces/SideEffectInterfaces.td"
2223
include "mlir/Interfaces/DataLayoutInterfaces.td"
@@ -527,7 +528,11 @@ def AIE_SelectOp: AIEX_Op<"select", []>, Results<(outs Index)> {
527528
];
528529
}
529530

530-
def AIE_RuntimeSequenceOp : AIEX_Op<"runtime_sequence", [NoTerminator, HasParent<"AIE::DeviceOp">]> {
531+
def AIE_RuntimeSequenceOp : AIEX_Op<"runtime_sequence", [
532+
Symbol,
533+
NoTerminator,
534+
HasParent<"AIE::DeviceOp">,
535+
]> {
531536
let summary = "Program the configuration co-processor of the AI Engine array";
532537
let description = [{
533538
Instructions in this operation allow for runtime (re-)configuration of the AI Engine array, such as configuring data movement buffer descriptors.
@@ -537,13 +542,69 @@ def AIE_RuntimeSequenceOp : AIEX_Op<"runtime_sequence", [NoTerminator, HasParent
537542
The input arguments are arguments passed in from the host at kernel invocation time. This may include buffers on the host.
538543
}];
539544
let arguments = (
540-
ins OptionalAttr<SymbolNameAttr>:$sym_name
545+
ins DefaultValuedAttr<SymbolNameAttr, "getDefaultRuntimeSequenceName()">:$sym_name
541546
);
542547
let regions = (region
543548
AnyRegion:$body
544549
);
545550
let hasCustomAssemblyFormat = 1;
546551
let hasVerifier = 1;
552+
let extraClassDeclaration = [{
553+
static llvm::StringRef getDefaultRuntimeSequenceName() { return "sequence"; }
554+
static RuntimeSequenceOp getForSymbolInDevice(AIE::DeviceOp module, llvm::StringRef symbol);
555+
static RuntimeSequenceOp getForSymbolInDeviceOrError(AIE::DeviceOp module, llvm::StringRef symbol);
556+
}];
557+
let extraClassDefinition = [{
558+
}];
559+
}
560+
561+
def AIE_ConfigureOp: AIEX_Op<"configure", [
562+
HasParent<"RuntimeSequenceOp">,
563+
NoTerminator
564+
]>,
565+
Results<(outs Index:$result)>
566+
{
567+
let summary = "Set up a configuration (program memories, stream switches, etc.) on the NPU device.";
568+
let arguments = (
569+
ins FlatSymbolRefAttr:$symbol
570+
);
571+
572+
let assemblyFormat = [{
573+
$symbol regions attr-dict
574+
}];
575+
576+
let extraClassDeclaration = [{
577+
AIE::DeviceOp getReferencedDeviceOp();
578+
}];
579+
580+
let regions = (region
581+
AnyRegion:$body
582+
);
583+
584+
let hasVerifier = 1;
585+
}
586+
587+
def AIE_RunOp: AIEX_Op<"run", [HasParent<"ConfigureOp">]> {
588+
let arguments = (
589+
ins FlatSymbolRefAttr:$runtime_sequence_symbol,
590+
Variadic<AnyType>:$args
591+
);
592+
593+
let assemblyFormat = [{
594+
$runtime_sequence_symbol `(` $args `)` `:` `(` type($args) `)` attr-dict
595+
}];
596+
597+
let extraClassDeclaration = [{
598+
AIE::DeviceOp getCalleeDeviceOp();
599+
RuntimeSequenceOp getCalleeRuntimeSequenceOp();
600+
}];
601+
602+
let hasVerifier = 1;
603+
604+
let summary = "Execute a runtime sequence";
605+
let description = [{
606+
Executes an `aiex.runtime_sequence` with the given name and arguments by inlining its instructions at the call site.
607+
}];
547608
}
548609

549610
def AIE_NpuDmaMemcpyNdOp: AIEX_Op<"npu.dma_memcpy_nd", [
@@ -772,6 +833,9 @@ def AIE_NpuWrite32Op: AIEX_Op<"npu.write32", []> {
772833
If 'buffer' is not present and 'column' and 'row' are not present then
773834
'address' is interpreted as a full 32-bit address in the AIE array.
774835
}];
836+
let extraClassDeclaration = [{
837+
std::optional<uint32_t> getAbsoluteAddress();
838+
}];
775839
}
776840

777841
// MASKWRITE
@@ -798,6 +862,9 @@ def AIE_NpuMaskWrite32Op: AIEX_Op<"npu.maskwrite32", []> {
798862
If 'buffer' is not present and 'column' and 'row' are not present then
799863
'address' is interpreted as a full 32-bit address in the AIE array.
800864
}];
865+
let extraClassDeclaration = [{
866+
std::optional<uint32_t> getAbsoluteAddress();
867+
}];
801868
}
802869

803870
// BLOCKWRITE
@@ -823,6 +890,10 @@ def AIE_NpuBlockWriteOp: AIEX_Op<"npu.blockwrite", []> {
823890
If 'buffer' is not present and 'column' and 'row' are not present then
824891
'address' is interpreted as a full 32-bit address in the AIE array.
825892
}];
893+
let extraClassDeclaration = [{
894+
std::optional<uint32_t> getAbsoluteAddress();
895+
mlir::DenseIntElementsAttr getDataWords();
896+
}];
826897
}
827898

828899
// OP_SYNC

0 commit comments

Comments
 (0)