-
Notifications
You must be signed in to change notification settings - Fork 182
Runtime TXN generation #3002
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Runtime TXN generation #3002
Changes from 18 commits
4e1d883
58e3d15
ec123b2
a48a3ed
1bb1992
73d54a9
f017bcf
88de190
3f8bcd3
6c28cc9
0155ebd
544aec0
39fed19
0066a60
93acc43
c97ce07
5286ec4
934d90b
84a7704
1fa8774
93c6450
ba6319d
0e9e2fe
90ab0cd
2b63765
fe0ba91
2943c94
8da783e
240b7b3
88c66ee
bdf4d07
1c58970
f6e8a06
b8cafe9
606ca9f
e508bb5
1531551
e0d55eb
119053c
5b59027
758ab3d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| //===- AIEXToEmitC.h - AIEX to EmitC conversion -----------------*- 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 2025 Advanced Micro Devices, Inc. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef AIE_CONVERSION_AIEXTOEMITC_AIEXTOEMITC_H | ||
| #define AIE_CONVERSION_AIEXTOEMITC_AIEXTOEMITC_H | ||
|
|
||
| #include "mlir/IR/BuiltinOps.h" | ||
| #include "mlir/Pass/Pass.h" | ||
| #include <memory> | ||
|
|
||
| namespace xilinx { | ||
|
|
||
| std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>> | ||
| createConvertAIEXToEmitCPass(); | ||
|
|
||
| } // namespace xilinx | ||
|
|
||
| #endif // AIE_CONVERSION_AIEXTOEMITC_AIEXTOEMITC_H | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -748,13 +748,19 @@ def AIE_NpuWriteRTPOp: AIEX_Op<"npu.rtp_write", []> { | |
| let arguments = ( | ||
| ins FlatSymbolRefAttr:$buffer, | ||
| UI32Attr:$index, | ||
| I32Attr:$value | ||
| OptionalAttr<I32Attr>:$value, | ||
| Optional<I32>:$dyn_value | ||
| ); | ||
| let results = (outs ); | ||
| let assemblyFormat = [{ `(` $buffer `,` $index `,` $value `)` attr-dict | ||
| }]; | ||
| let hasCustomAssemblyFormat = 1; | ||
| let hasVerifier = 1; | ||
| let description = [{ | ||
| rtp write operator | ||
| rtp write operator. | ||
| When `dyn_value` is provided, it supplies the RTP value at runtime | ||
| instead of the static `value` attribute. | ||
| }]; | ||
| let extraClassDeclaration = [{ | ||
| bool hasDynamicValue() { return getDynValue() != nullptr; } | ||
| }]; | ||
|
Comment on lines
+776
to
789
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a little worried about code bloat with having every parameter for these ops duplicated, once as an attribute and once as an SSA value, along with the added custom verifier and assembly format for each op. Could we consider removing the attributes altogether and instead use SSA values, with |
||
| } | ||
|
|
||
|
|
@@ -781,19 +787,20 @@ def AIE_NpuPushQueueOp: AIEX_Op<"npu.push_queue", []> { | |
| } | ||
|
|
||
| // WRITE32 | ||
| def AIE_NpuWrite32Op: AIEX_Op<"npu.write32", []> { | ||
| def AIE_NpuWrite32Op: AIEX_Op<"npu.write32", [AttrSizedOperandSegments]> { | ||
| let summary = "write32 operator"; | ||
| let arguments = ( | ||
| ins UI32Attr:$address, | ||
| UI32Attr:$value, | ||
| OptionalAttr<FlatSymbolRefAttr>:$buffer, | ||
| OptionalAttr<I32Attr>:$column, | ||
| OptionalAttr<I32Attr>:$row | ||
| OptionalAttr<I32Attr>:$row, | ||
| Optional<AnySignlessInteger>:$dyn_address, | ||
| Optional<AnySignlessInteger>:$dyn_value | ||
| ); | ||
| let results = (outs ); | ||
| let assemblyFormat = [{ | ||
| attr-dict | ||
| }]; | ||
| let hasCustomAssemblyFormat = 1; | ||
| let hasVerifier = 1; | ||
| let description = [{ | ||
| NPU write32 operator writes a 32bit value to the AIE array. | ||
| If 'buffer' is present then 'address' is interpreted as an offset into the | ||
|
|
@@ -802,27 +809,57 @@ def AIE_NpuWrite32Op: AIEX_Op<"npu.write32", []> { | |
| into the memory space of aie.tile(column, row). | ||
| If 'buffer' is not present and 'column' and 'row' are not present then | ||
| 'address' is interpreted as a full 32-bit address in the AIE array. | ||
|
|
||
| Optionally, SSA values can be provided for 'dyn_address' and 'dyn_value' | ||
| to enable runtime-parameterized sequences. When dynamic operands are present, | ||
| the static attributes serve as placeholders (typically 0) and the SSA values | ||
| are used instead. | ||
|
|
||
| Static syntax (unchanged): `aiex.npu.write32 {address = 123 : ui32, value = 456 : ui32}` | ||
| Dynamic syntax: `aiex.npu.write32(%addr, %val) {address = 0 : ui32, value = 0 : ui32} : i32, i32` | ||
| }]; | ||
| let extraClassDeclaration = [{ | ||
| std::optional<uint32_t> getAbsoluteAddress(); | ||
| bool hasDynamicOperands() { return getDynAddress() != nullptr; } | ||
| }]; | ||
| let builders = [ | ||
| OpBuilder<(ins "uint32_t":$address, "uint32_t":$value, | ||
| "mlir::FlatSymbolRefAttr":$buffer, | ||
| "mlir::IntegerAttr":$column, "mlir::IntegerAttr":$row), [{ | ||
| build($_builder, $_state, | ||
| $_builder.getUI32IntegerAttr(address), | ||
| $_builder.getUI32IntegerAttr(value), | ||
| buffer, column, row, | ||
| /*dyn_address=*/Value(), /*dyn_value=*/Value()); | ||
| }]>, | ||
| OpBuilder<(ins "mlir::IntegerAttr":$address, "mlir::IntegerAttr":$value, | ||
| "mlir::FlatSymbolRefAttr":$buffer, | ||
| "mlir::IntegerAttr":$column, "mlir::IntegerAttr":$row), [{ | ||
| build($_builder, $_state, | ||
| address, value, | ||
| buffer, column, row, | ||
| /*dyn_address=*/Value(), /*dyn_value=*/Value()); | ||
| }]> | ||
| ]; | ||
| } | ||
|
|
||
| // MASKWRITE | ||
| def AIE_NpuMaskWrite32Op: AIEX_Op<"npu.maskwrite32", []> { | ||
| def AIE_NpuMaskWrite32Op: AIEX_Op<"npu.maskwrite32", [AttrSizedOperandSegments]> { | ||
| let summary = "Write a masked 32-bit value to the AIE array"; | ||
| let arguments = ( | ||
| ins UI32Attr:$address, | ||
| UI32Attr:$value, | ||
| UI32Attr:$mask, | ||
| OptionalAttr<FlatSymbolRefAttr>:$buffer, | ||
| OptionalAttr<I32Attr>:$column, | ||
| OptionalAttr<I32Attr>:$row | ||
| OptionalAttr<I32Attr>:$row, | ||
| Optional<AnySignlessInteger>:$dyn_address, | ||
| Optional<AnySignlessInteger>:$dyn_value, | ||
| Optional<AnySignlessInteger>:$dyn_mask | ||
| ); | ||
| let results = (outs ); | ||
| let assemblyFormat = [{ | ||
| attr-dict | ||
| }]; | ||
| let hasCustomAssemblyFormat = 1; | ||
| let hasVerifier = 1; | ||
| let description = [{ | ||
| NPU mask write32 operator writes a masked 32bit value to the AIE array. | ||
| If 'buffer' is present then 'address' is interpreted as an offset into the | ||
|
|
@@ -831,10 +868,38 @@ def AIE_NpuMaskWrite32Op: AIEX_Op<"npu.maskwrite32", []> { | |
| into the memory space of aie.tile(column, row). | ||
| If 'buffer' is not present and 'column' and 'row' are not present then | ||
| 'address' is interpreted as a full 32-bit address in the AIE array. | ||
|
|
||
| Optionally, SSA values can be provided for 'dyn_address', 'dyn_value', and | ||
| 'dyn_mask' to enable runtime-parameterized sequences. | ||
|
|
||
| Static syntax (unchanged): `aiex.npu.maskwrite32 {address = 123 : ui32, ...}` | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest removing "(unchanged)" from these comments |
||
| Dynamic syntax: `aiex.npu.maskwrite32(%addr, %val, %mask) {address = 0 : ui32, ...} : i32, i32, i32` | ||
| }]; | ||
| let extraClassDeclaration = [{ | ||
| std::optional<uint32_t> getAbsoluteAddress(); | ||
| bool hasDynamicOperands() { return getDynAddress() != nullptr; } | ||
| }]; | ||
| let builders = [ | ||
| OpBuilder<(ins "uint32_t":$address, "uint32_t":$value, "uint32_t":$mask, | ||
| "mlir::FlatSymbolRefAttr":$buffer, | ||
| "mlir::IntegerAttr":$column, "mlir::IntegerAttr":$row), [{ | ||
| build($_builder, $_state, | ||
| $_builder.getUI32IntegerAttr(address), | ||
| $_builder.getUI32IntegerAttr(value), | ||
| $_builder.getUI32IntegerAttr(mask), | ||
| buffer, column, row, | ||
| /*dyn_address=*/Value(), /*dyn_value=*/Value(), /*dyn_mask=*/Value()); | ||
| }]>, | ||
| OpBuilder<(ins "mlir::IntegerAttr":$address, "mlir::IntegerAttr":$value, | ||
| "mlir::IntegerAttr":$mask, | ||
| "mlir::FlatSymbolRefAttr":$buffer, | ||
| "mlir::IntegerAttr":$column, "mlir::IntegerAttr":$row), [{ | ||
| build($_builder, $_state, | ||
| address, value, mask, | ||
| buffer, column, row, | ||
| /*dyn_address=*/Value(), /*dyn_value=*/Value(), /*dyn_mask=*/Value()); | ||
| }]> | ||
| ]; | ||
| } | ||
|
|
||
| // BLOCKWRITE | ||
|
|
@@ -867,28 +932,67 @@ def AIE_NpuBlockWriteOp: AIEX_Op<"npu.blockwrite", []> { | |
| } | ||
|
|
||
| // OP_SYNC | ||
| def AIE_NpuSyncOp: AIEX_Op<"npu.sync", []> { | ||
| def AIE_NpuSyncOp: AIEX_Op<"npu.sync", [AttrSizedOperandSegments]> { | ||
| let summary = "sync operator"; | ||
| let arguments = ( | ||
| ins I32Attr:$column, | ||
| I32Attr:$row, | ||
| I32Attr:$direction, | ||
| I32Attr:$channel, | ||
| I32Attr:$column_num, | ||
| I32Attr:$row_num | ||
| I32Attr:$row_num, | ||
| Optional<AnySignlessInteger>:$dyn_column, | ||
| Optional<AnySignlessInteger>:$dyn_row, | ||
| Optional<AnySignlessInteger>:$dyn_direction, | ||
| Optional<AnySignlessInteger>:$dyn_channel, | ||
| Optional<AnySignlessInteger>:$dyn_column_num, | ||
| Optional<AnySignlessInteger>:$dyn_row_num | ||
| ); | ||
| let results = (outs ); | ||
| let assemblyFormat = [{ | ||
| attr-dict | ||
| }]; | ||
| let hasCustomAssemblyFormat = 1; | ||
| let hasVerifier = 1; | ||
| let description = [{ | ||
| The sync operation blocks execution of the instruction stream until a task-complete token (TCT) is received on `column`, `row`, channel `channel`, direction `direction` (where `0` is `S2MM` and `1` is `MM2S`). | ||
|
|
||
| #### Troubleshooting | ||
|
|
||
| If this operation appears to deadlock, ensure that at least one buffer descriptor is configured to issue a TCT on the channel you expect. | ||
| By default, `dma_memcpy_nd` operations only issue tokens for `S2MM` channels, and `issue_token` must be set to `true` to issue tokens for `MM2S` channels. | ||
|
|
||
| Optionally, SSA values can be provided for all parameters to enable | ||
| runtime-parameterized sequences. | ||
|
|
||
| Static syntax (unchanged): `aiex.npu.sync {column = 0 : i32, ...}` | ||
| Dynamic syntax: `aiex.npu.sync(%col, %row, %dir, %chan, %ncol, %nrow) {column = 0 : i32, ...} : i32, i32, i32, i32, i32, i32` | ||
| }]; | ||
| let extraClassDeclaration = [{ | ||
| bool hasDynamicOperands() { return getDynColumn() != nullptr; } | ||
| }]; | ||
| let builders = [ | ||
| OpBuilder<(ins "int32_t":$column, "int32_t":$row, | ||
| "int32_t":$direction, "int32_t":$channel, | ||
| "int32_t":$column_num, "int32_t":$row_num), [{ | ||
| build($_builder, $_state, | ||
| $_builder.getI32IntegerAttr(column), | ||
| $_builder.getI32IntegerAttr(row), | ||
| $_builder.getI32IntegerAttr(direction), | ||
| $_builder.getI32IntegerAttr(channel), | ||
| $_builder.getI32IntegerAttr(column_num), | ||
| $_builder.getI32IntegerAttr(row_num), | ||
| /*dyn_column=*/Value(), /*dyn_row=*/Value(), | ||
| /*dyn_direction=*/Value(), /*dyn_channel=*/Value(), | ||
| /*dyn_column_num=*/Value(), /*dyn_row_num=*/Value()); | ||
| }]>, | ||
| OpBuilder<(ins "mlir::IntegerAttr":$column, "mlir::IntegerAttr":$row, | ||
| "mlir::IntegerAttr":$direction, "mlir::IntegerAttr":$channel, | ||
| "mlir::IntegerAttr":$column_num, "mlir::IntegerAttr":$row_num), [{ | ||
| build($_builder, $_state, | ||
| column, row, direction, channel, column_num, row_num, | ||
| /*dyn_column=*/Value(), /*dyn_row=*/Value(), | ||
| /*dyn_direction=*/Value(), /*dyn_channel=*/Value(), | ||
| /*dyn_column_num=*/Value(), /*dyn_row_num=*/Value()); | ||
| }]> | ||
| ]; | ||
| } | ||
|
|
||
| // XAIE_IO_CUSTOM_OP_BEGIN + 1 (address patch) | ||
|
|
@@ -897,14 +1001,17 @@ def AIE_NpuAddressPatchOp: AIEX_Op<"npu.address_patch", []> { | |
| let arguments = ( | ||
| ins UI32Attr:$addr, | ||
| I32Attr:$arg_idx, | ||
| I32Attr:$arg_plus | ||
| I32Attr:$arg_plus, | ||
| Optional<I32>:$dyn_arg_plus | ||
| ); | ||
| let results = (outs ); | ||
| let assemblyFormat = [{ | ||
| attr-dict | ||
| (`(` $dyn_arg_plus^ `:` type($dyn_arg_plus) `)`)? attr-dict | ||
| }]; | ||
| let description = [{ | ||
| address patch operator | ||
| address patch operator. | ||
| When `dyn_arg_plus` is provided, it is used instead of the static | ||
| `arg_plus` attribute. This enables runtime-parameterized buffer offsets. | ||
| }]; | ||
| } | ||
|
|
||
|
|
@@ -1015,6 +1122,7 @@ def AIE_NpuLoadPdiOp: AIEX_Op<"npu.load_pdi", []> { | |
| let hasCanonicalizeMethod = 1; | ||
| } | ||
|
|
||
|
|
||
| def AIE_DMAConfigureTaskOp : AIEX_Op<"dma_configure_task", [HasParent<"AIE::RuntimeSequenceOp">, TileElement]>, Results<(outs Index:$result)> { | ||
| let summary = "Concrete Instantiation of a Buffer Descriptor Chain as a Task on a Channel and Direction on a Tile"; | ||
| let description = [{ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2026