Skip to content

Commit

Permalink
Multi-Dimensional Buffer Descriptors in AIE2 (#547)
Browse files Browse the repository at this point in the history
* add error handling to libXAIE calls

* address PR comments on error handling in libXAIE calls
do not expose AieRC type in aie_inc.cpp functions
do not print redundant error messages
use C++ raw string for code block
rename __mlir_aie_print to __mlir_aie_verbose

* replace some duplicated definitions with auto-generated TableGen ones

* initial draft of N-dimensional buffer descriptors for AIE-ML

* add simulator tests for multi-dim buffer descriptors in AIE2
small fixes

* clang format

* remove unused tileDMATensorRefStr function

* fix merge conflict

* re-add objectFifoTypes; fix optional ndim generation; format

* add dependency on AIE lib to AIEX CMakeLists

* reformat and add N-dimensional BD unit tests

* n-dimensonal DMA: add tests; add documentation; add checks on buffer size, rank, and contiguity; reorder arguments to be highest dimension first
  • Loading branch information
andrej authored Jul 26, 2023
1 parent 0480134 commit 2ded45c
Show file tree
Hide file tree
Showing 48 changed files with 1,342 additions and 412 deletions.
63 changes: 52 additions & 11 deletions include/aie/Dialect/AIE/IR/AIE.td
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ switch is referred to as `switchbox` to avoid confusion with the

}];
let useDefaultTypePrinterParser = 1;
let useDefaultAttributePrinterParser = 1;
let useFoldAPI = kEmitFoldAdaptorFolder;
}

Expand Down Expand Up @@ -797,6 +798,20 @@ def AIE_DMABDPACKETOp: AIE_Op<"dmaBdPacket", []> {

}

def AIE_DimTupleAttr : AttrDef<AIE_Dialect, "DimTuple", []> {
let mnemonic = "DimTuple";
let summary =
"Tuple encoding the stride and wrap of one dimension in an "
"AIE2 n-dimensional buffer descriptor";
let parameters = (ins
"uint32_t" : $stepsize,
"uint16_t" : $wrap
);
let assemblyFormat = "`<` $stepsize `,` $wrap `>`";
}

def AIE_DimTupleArrayAttr : ArrayOfAttr<AIE_Dialect, "DimTupleArray", "DimTupleArray", "::xilinx::AIE::DimTupleAttr">;

def AIE_DMABDOp: AIE_Op<"dmaBd", []> {
let summary = "Declare a dma block descriptor op";
let description = [{
Expand All @@ -823,17 +838,44 @@ def AIE_DMABDOp: AIE_Op<"dmaBd", []> {
```
A DMA channel in a Memory Module can process one block descriptor after another by chaining them.
There are 16 block descriptors per Memory Module. They are shared by four DMA channels.

On AIE-ML devices, an optional argument can be used to specify an array of
step sizes and wraps to move data in more advanced patterns. Strides and
wraps are specified as tuples `<stride, wrap>`, and up to three dimensions
can be specified (or up to four dimensions on memtiles).

The first element of the array gives the _highest-dimension_ stride and
wrap, the last element of the array gives the lowest-dimension.

Example:

```
AIE.dmaBd(<%buf : memref<128xi32>, 0, 128>, 0, [<16, 8>, <1, 2>, <2, 8>])
```

This corresponds to alternating between even and odd elements of the
buffer/stream every 8 elements, like so, equivalent to nested loops like so:

```
for(int i = 0; i < 8 /* wrap[0] */; i += 16 /* stepsize[0] */)
for(int j = 0; j < 2 /* wrap[1] */; j += 1 /* stepsize[1] */)
for(int k = 0; k < 8 /* wrap[2] */; k += 2 /* stepsize[2] */)
// access/store element at/to index (i + j + k)
```
}];

let arguments = (
ins AnyMemRef:$buffer,
I32Attr:$offset,
I32Attr:$len,
ConfinedAttr<I32Attr, [IntMinValue<0>, IntMaxValue<1>]>:$AB // 0: A, 1: B
ConfinedAttr<I32Attr, [IntMinValue<0>, IntMaxValue<1>]>:$AB, // 0: A, 1: B
OptionalAttr<AIE_DimTupleArrayAttr>:$dimensions
);

let hasVerifier = 1;

let assemblyFormat = [{
`(` `<` $buffer `:` type($buffer) `,` $offset `,` $len `>` `,` $AB `)` attr-dict
`(` `<` $buffer `:` type($buffer) `,` $offset `,` $len `>` `,` $AB (`,` $dimensions^ )? `)` attr-dict
}];

let extraClassDeclaration = [{
Expand All @@ -843,15 +885,14 @@ def AIE_DMABDOp: AIE_Op<"dmaBd", []> {
bool isB() { return (getAB() == 1); }
}];

// let builders = [
// OpBuilder<(ins "Value":$buffer, "int":$offset, "int":$len, "int":$AB), [{
// build($_builder, $_state,
// buffer,
// $_builder.getI32IntegerAttr(offset),
// $_builder.getI32IntegerAttr(len),
// $_builder.getI32IntegerAttr(AB));
// }]>
// ];
let builders = [
OpBuilder<(ins "Value":$buffer, "int":$offset, "int":$len, "int":$AB), [{
odsState.addOperands(buffer);
odsState.addAttribute(getOffsetAttrName(odsState.name), $_builder.getI32IntegerAttr(offset));
odsState.addAttribute(getLenAttrName(odsState.name), $_builder.getI32IntegerAttr(len));
odsState.addAttribute(getABAttrName(odsState.name), $_builder.getI32IntegerAttr(AB));
}]>
];
}

def AIE_DMAStartOp: AIE_Op<"dmaStart", [ParentOneOf<["MemOp", "MemTileDMAOp", "func::FuncOp", "ShimDMAOp"]>, Terminator]>,
Expand Down
75 changes: 24 additions & 51 deletions include/aie/Dialect/AIE/IR/AIEDialect.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,67 +65,27 @@ class TileOp;
/// Include the generated interface declarations.
#include "aie/Dialect/AIE/IR/AIEInterfaces.h.inc"

// Include dialect declarations such as parseAttributes, parseType
#include "aie/Dialect/AIE/IR/AIEDialect.h.inc"

namespace xilinx {
namespace AIE {

void registerAIETranslations();

// FIXME: use this
//#include "AIEDialect.h.inc"

// The Dialect
class AIEDialect : public mlir::Dialect {
public:
explicit AIEDialect(mlir::MLIRContext *ctx);
static StringRef getDialectNamespace() { return "AIE"; }

/// Parse a type registered to this dialect. Overridding this method is
/// required for dialects that have custom types.
/// Technically this is only needed to be able to round-trip to textual IR.
mlir::Type parseType(DialectAsmParser &parser) const override;

/// Print a type registered to this dialect. Overridding this method is
/// only required for dialects that have custom types.
/// Technically this is only needed to be able to round-trip to textual IR.
void printType(mlir::Type type, DialectAsmPrinter &os) const override;
};
} // namespace AIE
} // namespace xilinx

////////////////////////////////////////////////////////////////////////////////
/////////////////////// Custom Types for the Dialect ///////////////////////////
////////////////////////////////////////////////////////////////////////////////

// namespace detail {
// struct AIEListTypeStorage;
// }

// /// LLVM-style RTTI: one entry per subclass to allow dyn_cast/isa.
// enum AIETypeKind {
// // The enum starts at the range reserved for this dialect.
// AIE_TYPE = mlir::Type::FIRST_PRIVATE_EXPERIMENTAL_0_TYPE,
// AIE_LIST,
// };

// /// Type for Toy arrays.
// /// In MLIR Types are reference to immutable and uniqued objects owned by the
// /// MLIRContext. As such `AIEListType` only wraps a pointer to an uniqued
// /// instance of `AIEListTypeStorage` (defined in our implementation file) and
// /// provides the public facade API to interact with the type.
// class AIEListType : public mlir::Type::TypeBase<AIEListType, mlir::Type,
// detail::AIEListTypeStorage>
// {
// public:
// using Base::Base;

// /// Return the type of individual elements in the array.
// mlir::Type getElementType();

// /// Get the unique instance of this Type from the context.
// static AIEListType get(mlir::Type elementType);

// /// Support method to enable LLVM-style RTTI type casting.
// static bool kindof(unsigned kind) { return kind == AIETypeKind::AIE_LIST; }
// };
// Include generated TableGen-generated type definitions
#define GET_TYPEDEF_CLASSES 1
#include "aie/Dialect/AIE/IR/AIETypes.h.inc"

namespace xilinx {
namespace AIE {
namespace detail {
struct AIEObjectFifoTypeStorage;
}
Expand Down Expand Up @@ -172,11 +132,24 @@ class AIEObjectFifoSubviewType
mlir::Type getElementType();
};

} // namespace AIE
} // namespace xilinx

////////////////////////////////////////////////////////////////////////////////
// Custom Attributes ///////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

#define GET_ATTRDEF_CLASSES
#include "aie/Dialect/AIE/IR/AIEAttrDefs.h.inc"

////////////////////////////////////////////////////////////////////////////////
//////////////////// Custom Operations for the Dialect /////////////////////////
////////////////////////////////////////////////////////////////////////////////

//#include "AIEOpInterfaces.h.inc"
namespace xilinx {
namespace AIE {

// #include "AIEOpInterfaces.h.inc"

typedef std::pair<WireBundle, int> Port;
typedef std::pair<Port, Port> Connect;
Expand Down
11 changes: 5 additions & 6 deletions include/aie/Dialect/AIE/IR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
add_mlir_dialect(AIE AIE)
add_mlir_doc(AIE AIEDialect ./ -gen-dialect-doc)

set(LLVM_TARGET_DEFINITIONS AIE.td)
mlir_tablegen(AIEAttrDefs.h.inc -gen-attrdef-decls -attrdefs-dialect=AIE)
mlir_tablegen(AIEAttrDefs.cpp.inc -gen-attrdef-defs -attrdefs-dialect=AIE)
add_public_tablegen_target(MLIRAIEAttrDefsIncGen)

set(LLVM_TARGET_DEFINITIONS AIE.td)
mlir_tablegen(AIEEnums.h.inc -gen-enum-decls)
mlir_tablegen(AIEEnums.cpp.inc -gen-enum-defs)
Expand All @@ -17,9 +22,3 @@ set(LLVM_TARGET_DEFINITIONS AIEInterfaces.td)
mlir_tablegen(AIEInterfaces.h.inc -gen-op-interface-decls)
mlir_tablegen(AIEInterfaces.cpp.inc -gen-op-interface-defs)
add_public_tablegen_target(MLIRAIEInterfacesIncGen)

# set(LLVM_TARGET_DEFINITIONS AIE.td)
# mlir_tablegen(LLVMConversions.inc -gen-llvmir-conversions)
# mlir_tablegen(LLVMConversionEnumsToLLVM.inc -gen-enum-to-llvmir-conversions)
# mlir_tablegen(LLVMConversionEnumsFromLLVM.inc -gen-enum-from-llvmir-conversions)
# add_public_tablegen_target(MLIRLLVMConversionsIncGen)
84 changes: 77 additions & 7 deletions lib/Dialect/AIE/IR/AIEDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@
#include "mlir/Transforms/InliningUtils.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/TypeSwitch.h"

using namespace mlir;

// Add TableGen'erated dialect definitions (including constructor)
// We implement the initialize() function further below
#include "aie/Dialect/AIE/IR/AIEDialect.cpp.inc"

namespace {

struct AIEInlinerInterface : public DialectInlinerInterface {
Expand Down Expand Up @@ -330,11 +335,16 @@ void AIEDialect::printType(mlir::Type type,
}
}

// FIXME: use Tablegen'd dialect class
AIEDialect::AIEDialect(mlir::MLIRContext *ctx)
: mlir::Dialect("AIE", ctx, ::mlir::TypeID::get<AIEDialect>()) {
// addTypes<AIEListType>();
void AIEDialect::initialize() {
addTypes<
#define GET_TYPE_LIST
#include "aie/Dialect/AIE/IR/AIETypes.cpp.inc"
>();
addTypes<AIEObjectFifoType, AIEObjectFifoSubviewType>();
addAttributes<
#define GET_ATTRDEF_LIST
#include "aie/Dialect/AIE/IR/AIEAttrDefs.cpp.inc"
>();
addOperations<
#define GET_OP_LIST
#include "aie/Dialect/AIE/IR/AIE.cpp.inc"
Expand Down Expand Up @@ -624,9 +634,7 @@ const xilinx::AIE::AIETargetModel &xilinx::AIE::DeviceOp::getTargetModel() {
return VC1902model;
}

LogicalResult xilinx::AIE::DeviceOp::verify() {
return success();
}
LogicalResult xilinx::AIE::DeviceOp::verify() { return success(); }

LogicalResult xilinx::AIE::TileOp::verify() {
const auto &target_model = getTargetModel(*this);
Expand Down Expand Up @@ -987,6 +995,64 @@ LogicalResult xilinx::AIE::MemTileDMAOp::verify() {
return success();
}

// DMABDOp
LogicalResult xilinx::AIE::DMABDOp::verify() {
// The following checks only apply if non-default strides/wraps are defined.
if (getDimensions()) {
::mlir::MemRefType buffer = getBuffer().getType();
// We are restrictive about the type of the memref used as the input address
// to the DMABD when used with multi-dimensional strides/wraps. Since the
// BD will use the memref as a base address and copy from it in 32 bit
// chunks, while assuming the layout of the memref is contiguous, we
// disallow anything whose elemental size is not 32 bits, or where we
// cannot verify that the layout is contiguous.
if (!buffer.getElementType().isInteger(32) || buffer.getRank() > 1 ||
!buffer.getLayout().isIdentity()) {
return emitOpError() << "Specifying transfer step sizes and wraps is only"
" supported for one-dimensional memrefs of 32 bit"
" integer elements.";
}
uint64_t memref_size = 1; // in bytes
uint64_t max_idx = 0;
for (int64_t memref_dim : buffer.getShape()) {
memref_size *= 4 * memref_dim;
}
llvm::ArrayRef<xilinx::AIE::DimTupleAttr> dims = *getDimensions();
size_t max_n_dims = 3;
if (isa_and_nonnull<xilinx::AIE::MemTileDMAOp>((*this)->getParentOp())) {
max_n_dims = 4;
}
if (dims.size() > max_n_dims) {
return emitOpError() << "Cannot give more than "
<< std::to_string(max_n_dims)
<< " dimensions for step sizes and wraps in this "
" tile (got "
<< std::to_string(dims.size()) << " dimensions).";
}
for (xilinx::AIE::DimTupleAttr dim : dims) {
max_idx += dim.getStepsize() * (dim.getWrap() - 1);
if (0 == dim.getStepsize()) {
return emitOpError()
<< "Invalid step size; must be a positive integer.";
}
if (dim.getStepsize() > memref_size) {
return emitOpError()
<< "Step size " << std::to_string(dim.getStepsize() * 4) << " "
<< "bytes exceeds memref size " << std::to_string(memref_size);
}
}
if (memref_size <= 4 * max_idx) {
return emitOpError() << "Specified stepsize(s) and wrap(s) result in out "
"of bounds access in buffer, for index "
<< std::to_string(max_idx) << ", accessing at "
<< std::to_string(4 * max_idx)
<< " byte offset in memref of length "
<< std::to_string(memref_size) << ".";
}
}
return success();
}

xilinx::AIE::TileOp xilinx::AIE::MemTileDMAOp::getTileOp() {
return cast<xilinx::AIE::TileOp>(getTile().getDefiningOp());
}
Expand Down Expand Up @@ -1206,3 +1272,7 @@ bool TileOp::isShimPLTile() {
bool TileOp::isShimNOCorPLTile() { return isShimNOCTile() || isShimPLTile(); }
} // namespace AIE
} // namespace xilinx

// Include implementations for custom attributes
#define GET_ATTRDEF_CLASSES
#include "aie/Dialect/AIE/IR/AIEAttrDefs.cpp.inc"
1 change: 1 addition & 0 deletions lib/Dialect/AIE/IR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_mlir_dialect_library(AIE

DEPENDS
MLIRAIEIncGen
MLIRAIEAttrDefsIncGen
MLIRAIEEnumsIncGen
MLIRAIEInterfacesIncGen

Expand Down
1 change: 1 addition & 0 deletions lib/Dialect/AIEX/IR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_mlir_dialect_library(AIEX
MLIRAIEXEnumsIncGen

LINK_LIBS PUBLIC
AIE
MLIRIR
MLIRSupport
)
Loading

0 comments on commit 2ded45c

Please sign in to comment.