Skip to content

Commit

Permalink
ObjFifo unroll dependency fixes (#621)
Browse files Browse the repository at this point in the history
* Fixes for objFifo unrolling algorithm.

* EOF

* clang-format (#625)

* Use target model functions to get number of DMA channels.

* Clang format

* Fix function call

* Add shim tiles that are not in noc columns in the getNumDestShimMuxConnections() functions.

* Add isShimNOCorPLTile () to the target model.

* Add missing target model.

* Add improvements to the doc

* Add isShimNOCorPLTile() virtual function.

* Clang format

---------

Co-authored-by: abisca <[email protected]>
Co-authored-by: Joseph Melber <[email protected]>
  • Loading branch information
3 people authored Sep 7, 2023
1 parent dae0939 commit 8fd0b2b
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 132 deletions.
83 changes: 42 additions & 41 deletions include/aie/Dialect/AIE/IR/AIE.td
Original file line number Diff line number Diff line change
Expand Up @@ -1370,42 +1370,43 @@ def AIE_ShimDMAAllocationOp : AIE_Op<"shimDMAAllocation", [HasParent<"DeviceOp">
def AIE_ObjectFifoCreateOp: AIE_Op<"objectFifo", [HasParent<"DeviceOp">, Symbol]> {
let summary = "Create a circular buffer or channel between two tiles";
let description = [{
The `aie.createObjectFifo` operation creates a circular buffer established between a producer and one or
more consumers, which are `aie.tile` operations. The aie.createObjectFifo instantiates the given number of
buffers (of given output type) and their locks in the Memory Module of the appropriate tile(s) after lowering,
based on tile-adjacency. These elements represent the conceptual depth of the objectFifo or, more specifically,
of its object pool.

For the producer and for each consumer, a different size (i.e., element number) can be specified. This will
take effect in the case of consumers placed on tiles non-adjacent to the producer. Otherwise, the producer
size will be applied. If a single size is specified, it will be applied to both producer and consumers.

This operation is then converted by the AIEObjectFifoStatefulTransformPass into buffers and their associated
locks. The pass also establishes Flow and DMA operations between the producer and consumer tiles if they are
The `aie.objectFifo` operation creates a circular buffer established between a producer and one or
more consumers, which are `aie.tile` operations. The`aie.objectFifo` instantiates the given number of
buffers (of given output type) and their locks in the Memory Module of the appropriate tile(s) after
lowering, based on tile-adjacency. These elements represent the conceptual depth of the `objectFifo` or,
more specifically, of its object pool.

For the producer and for each consumer, a different size (i.e., element number) can be specified as an
array of integer values. This will take effect in the case of consumers placed on tiles non-adjacent to
the producer. Otherwise, the producer size will be applied. If a single size is specified, it will be
applied to both producer and consumers.

This operation is then converted by the `AIEObjectFifoStatefulTransformPass` into `aie.buffers` and their associated
`aie.locks`. The pass also establishes Flow and DMA operations between the producer and consumer tiles if they are
not adjacent.

1-to-1 tile example:
```
AIE.objectFifo @of1 (%tile12, { %tile13 }, 4 : i32) : !AIE.objectFifo<memref<16xi32>>
AIE.objectFifo @of1 (%tile12, { %tile23 }, 4 : i32) : !AIE.objectFifo<memref<16xi32>>
```
This operation creates an objectFifo between %tile12 and %tile13 of 4 elements, each a buffer of 16 32-bit integers.
Note: If there are no ObjectFifoAcquireOps corresponding to this objectFifo on the cores of %tile12 and %tile13,
This operation creates an `objectFifo` between `%tile12` and `%tile23` of 4 elements, each a buffer of 16 32-bit integers.
Note: If there are no `ObjectFifoAcquireOps` corresponding to this `objectFifo` on the cores of `%tile12` and `%tile23`,
then the depths of the object pools on each tile will be 4, as specified. Otherwise, the cores are scanned and the
highest number of acquired elements (+1 for prefetching) will be used instead, to ensure minimal resource usage.

1-to-2 tiles broadcast example:
```
AIE.objectFifo @of2 (%tile12, { %tile13, %tile23 }, 4 : i32) : !AIE.objectFifo<memref<16xi32>>
```
This operation creates an objectFifo between %tile12 and tiles %tile13, %tile23 of 4 elements, each a buffer of x16
This operation creates an `objectFifo` between `%tile12` and tiles `%tile13`, `%tile23` of 4 elements, each a buffer of x16
32-bit integers.

1-to-2 tiles broadcast with explicit sizes example:
```
AIE.objectFifo @of3 (%tile12, { %tile13, %tile23 }, [2, 3, 4]) : !AIE.objectFifo<memref<16xi32>>
```
This operation creates an objectFifo between %tile12, %tile13 and %tile23. The depths of the objectFifo object pool
at each tile are respectively 2, 3 and 4 for tiles %tile12, %tile13 and %tile23. This overrides the depth analysis
This operation creates an `objectFifo` between `%tile12`, `%tile13` and `%tile23`. The depths of the `objectFifo` object pool
at each tile are respectively 2, 3 and 4 for tiles `%tile12`, `%tile13` and `%tile23`. This overrides the depth analysis
specified in the first example.
}];

Expand Down Expand Up @@ -1441,24 +1442,24 @@ def AIE_ObjectFifoCreateOp: AIE_Op<"objectFifo", [HasParent<"DeviceOp">, Symbol]
def AIE_ObjectFifoLinkOp: AIE_Op<"objectFifo.link", [HasParent<"DeviceOp">]> {
let summary = "Links two objectFifos through an intermediary tile's DMA";
let description = [{
The "aie.objectFifo.link" operation allows to mark two objectFifos as linked. This implies that the two objectFifos form
one dataflow movement which is split accross multiple objectFifos. Specifically, during the objectFifo lowering there will
be less memory elements generated at the link point as the two objectFifos can share.
The `aie.objectFifo.link` operation allows to mark two `objectFifos` as linked. This implies that the two `objectFifos` form
one dataflow movement which is split accross multiple `objectFifos`. Specifically, during the `objectFifo` lowering there will
be less memory elements generated at the link point as the two `objectFifos` can share.

The two objectFifos which are linked must have a link point (i.e., a shared AIE tile).
In L1, only objectFifos of same size may be linked. In L2, different sized objectFifos can be linked.
The two `objectFifos` which are linked must have a link point (i.e., a shared AIE tile).
In L1, only `objectFifos` of same size may be linked. In L2, different sized objectFifos can be linked.

Example:
```
AIE.objectFifo @of1 (%t70, { %t72 }, 2) : !AIE.objectFifo<memref<64xi16>>
AIE.objectFifo @of2 (%t72, { %t74 }, 2) : !AIE.objectFifo<memref<64xi16>>
AIE.objectFifo.link [@of1] -> [@of2] ()
```
This operation links two objectFifos which have tile %t72 as a link point.
This operation links two `objectFifos` which have tile `%t72` as a link point.

To achieve a broadcast pattern through the link tile, the output objectFifo should have a list of all the consumers tiles.
To achieve a distribute pattern from the link tile, there should be multiple output objectFifos in the LinkOp. In this case,
parts will be taken out of the input objectFifo's buffers based on the sizes of the output objectFifos, in the order they
To achieve a broadcast pattern through the link tile, the output `objectFifo` should have a list of all the consumers tiles.
To achieve a distribute pattern from the link tile, there should be multiple output `objectFifos` in the LinkOp. In this case,
parts will be taken out of the input `objectFifo`'s buffers based on the sizes of the output `objectFifos`, in the order they
were given in the LinkOp.
The join pattern is the exact inverse of the distribute one.
}];
Expand Down Expand Up @@ -1492,10 +1493,10 @@ def AIE_ObjectFifoRegisterExternalBuffersOp: AIE_Op<"objectFifo.registerExternal
let summary = "Registers external buffers to given object fifo shim tile(s) to use in the associated shim DMA(s)";
let description = [{
The `aie.objectFifo.registerExternalBuffers` operation is used to register one or multiple external buffers
to the shim tile(s) used in an objectFifo creation. During the objectFifo lowering pass, shim DMAs that are
to the shim tile(s) used in an `objectFifo` creation. During the `objectFifo` lowering pass, shim DMAs that are
generated for those shim tiles will use the registered external buffers. This is currently done because
external buffers typically have a different size than the AIE buffers which are used in the AIE tiles of the
same objectFifos.
same `objectFifos`.

Example:
```
Expand Down Expand Up @@ -1529,22 +1530,22 @@ def AIE_ObjectFifoAcquireOp: AIE_Op<"objectFifo.acquire", []> {
let summary = "Acquire operation to lock and return objects of an ObjectFifo";
let description = [{
The `aie.objectFifo.acquire` operation first acquires the locks of the next given number
of objects in the objectFifo. The mode it acquires the locks in is chosen based on the port
of objects in the `objectFifo`. The mode it acquires the locks in is chosen based on the port
(producer: acquire for write, consumer: acquire for read). Then, it returns a subview of
the acquired objects which can be used to access them.

This operation is then converted by the AIEObjectFifoStatefulTransformPass into useLock operations on
the locks of the objectFifo objects that will be acquired. Under the hood, the operation only performs
This operation is then converted by the `AIEObjectFifoStatefulTransformPass` into `aie.useLock` operations on
the locks of the `objectFifo` objects that will be acquired. Under the hood, the operation only performs
new acquires if necessary. For example, if two objects have been acquired in the past and none have yet
to be released by the same process, then performing another acquire operation on the same objectFifo
to be released by the same process, then performing another acquire operation on the same `objectFifo`
within the same process of size two or less will not result in any new useLock operations (and for size
greater than two, only (size - 2) useLock operations will be performed).

Example:
```
%subview = AIE.objectFifo.acquire @of1 (Consume, 2) : !AIE.objectFifoSubview<memref<16xi32>>
```
This operation acquires the locks of the next two objects in the objectFifo named @of1 from its consumer
This operation acquires the locks of the next two objects in the `objectFifo` named `@of1` from its consumer
port and returns a subview of the acquired objects.
}];

Expand Down Expand Up @@ -1572,16 +1573,16 @@ def AIE_ObjectFifoReleaseOp: AIE_Op<"objectFifo.release", []> {
let summary = "Release operation for object locks in an ObjectFifo";
let description = [{
The `aie.objectFifo.release` operation releases the locks of the given number of objects
in the objectFifo. The mode it releases the locks in is chosen based on the `port`
in the `objectFifo`. The mode it releases the locks in is chosen based on the `port`
(producer: release for read, consumer: release for write).

This operation is then converted by the AIEObjectFifoStatefulTransformPass into useLock operations.
This operation is then converted by the `AIEObjectFifoStatefulTransformPass` into `aie.useLock` operations.

Example:
```
AIE.objectFifo.release @of1 (Produce, 1)
```
This operation releases the lock of the next object in the objectFifo named @of1 from producer port.
This operation releases the lock of the next object in the `objectFifo` named `@of1` from producer port.
}];

let arguments = (
Expand All @@ -1605,15 +1606,15 @@ def AIE_ObjectFifoReleaseOp: AIE_Op<"objectFifo.release", []> {
def AIE_ObjectFifoSubviewAccessOp : AIE_Op<"objectFifo.subview.access", []> {
let summary = "ObjectFifoSubview type accessor method";
let description = [{
Access the Nth element of a value of ObjectFifoSubview type.
Access the Nth element of a value of `ObjectFifoSubview` type.

Example:
```
%subview = AIE.objectFifo.acquire @of1 (Produce, 3) : !AIE.objectFifoSubview<memref<16xi32>>
%elem = AIE.objectFifo.subview.access %subview[0] : !AIE.objectFifoSubview<memref<16xi32>> -> memref<16xi32>
```
In this example, %elem is the first object of the subview. Note that this may not correspond to the first element of
the objectFifo if other acquire operations took place beforehand.
the `objectFifo` if other acquire operations took place beforehand.

}];

Expand All @@ -1639,9 +1640,9 @@ def AIE_ObjectFifoSubviewAccessOp : AIE_Op<"objectFifo.subview.access", []> {
def AIE_ObjectFifoRegisterProcessOp: AIE_Op<"objectFifo.registerProcess", []> {
let summary = "Operation that produces the acquire/release patterns for a process registered to an objectFifo";
let description = [{
The `aie.registerProcess` operation allows the user to register a function to an objectFifo along with its
The `aie.registerProcess` operation allows the user to register a function to an `objectFifo` along with its
acquire and release patterns. These patterns will be used to generate a sequence of acquires and releases
on the objectFifo elements. This generated sequence is often in the form of a for loop, however, in the case
on the `objectFifo` elements. This generated sequence is often in the form of a for loop, however, in the case
of cyclo-static patterns only the repetition of same number accesses and releases will generate a for loop.
This may result in multiple for loops of different sizes being generated. If there is no repetition, then no
loops will be generated.
Expand Down
13 changes: 13 additions & 0 deletions include/aie/Dialect/AIE/IR/AIETargetModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ class AIETargetModel {
/// not contain any memory.
virtual bool isShimPLTile(int col, int row) const = 0;

/// Return true if the given tile is either a Shim NOC or a Shim PL interface
/// tile.
virtual bool isShimNOCorPLTile(int col, int row) const = 0;

/// Return true if the given tile ID is valid.
virtual bool isValidTile(TileID src) const {
return (src.first >= 0) && (src.first < columns()) && (src.second >= 0) &&
Expand Down Expand Up @@ -269,6 +273,9 @@ class VC1902TargetModel : public AIE1TargetModel {
bool isShimPLTile(int col, int row) const override {
return row == 0 && !noc_columns.contains(col);
}
bool isShimNOCorPLTile(int col, int row) const override {
return isShimNOCTile(col, row) || isShimPLTile(col, row);
}
};

class VE2302TargetModel : public AIE2TargetModel {
Expand All @@ -290,6 +297,9 @@ class VE2302TargetModel : public AIE2TargetModel {
bool isShimPLTile(int col, int row) const override {
return row == 0 && !noc_columns.contains(col);
}
bool isShimNOCorPLTile(int col, int row) const override {
return isShimNOCTile(col, row) || isShimPLTile(col, row);
}
uint32_t getNumMemTileRows() const override { return 1; }
};

Expand All @@ -315,6 +325,9 @@ class VE2802TargetModel : public AIE2TargetModel {
bool isShimPLTile(int col, int row) const override {
return row == 0 && !noc_columns.contains(col);
}
bool isShimNOCorPLTile(int col, int row) const override {
return isShimNOCTile(col, row) || isShimPLTile(col, row);
}
uint32_t getNumMemTileRows() const override { return 2; }
};

Expand Down
41 changes: 4 additions & 37 deletions lib/Dialect/AIE/IR/AIEDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,18 +564,6 @@ xilinx::AIE::ObjectFifoRegisterExternalBuffersOp::getObjectFifo() {
}
return ObjectFifoCreateOp();
}
// xilinx::AIE::ObjectFifoCreateOp
// xilinx::AIE::ObjectFifoRegisterExternalBuffersOp::getObjectFifo() {
// Operation *parent = getOperation();
// while ((parent = parent->getParentOp())) {
// if (auto device = dyn_cast<DeviceOp>(parent)) {
// for (auto objFifo : device.getOps<ObjectFifoCreateOp>())
// if (objFifo.name() == getObjFifoName())
// return objFifo;
// }
// }
// return ObjectFifoCreateOp();
// }

// ObjectFifoAcquireOp
LogicalResult xilinx::AIE::ObjectFifoAcquireOp::verify() {
Expand Down Expand Up @@ -666,18 +654,6 @@ xilinx::AIE::ObjectFifoReleaseOp::getObjectFifo() {
}
return ObjectFifoCreateOp();
}
// xilinx::AIE::ObjectFifoCreateOp
// xilinx::AIE::ObjectFifoReleaseOp::getObjectFifo() {
// Operation *parent = getOperation();
// while ((parent = parent->getParentOp())) {
// if (auto device = dyn_cast<DeviceOp>(parent)) {
// for (auto objFifo : device.getOps<ObjectFifoCreateOp>())
// if (objFifo.name() == getObjFifoName())
// return objFifo;
// }
// }
// return ObjectFifoCreateOp();
// }

// ObjectFifoSubviewAccessOp
LogicalResult xilinx::AIE::ObjectFifoSubviewAccessOp::verify() {
Expand Down Expand Up @@ -723,18 +699,6 @@ xilinx::AIE::ObjectFifoRegisterProcessOp::getObjectFifo() {
}
return ObjectFifoCreateOp();
}
// xilinx::AIE::ObjectFifoCreateOp
// xilinx::AIE::ObjectFifoRegisterProcessOp::getObjectFifo() {
// Operation *parent = getOperation();
// while ((parent = parent->getParentOp())) {
// if (auto device = dyn_cast<DeviceOp>(parent)) {
// for (auto objFifo : device.getOps<ObjectFifoCreateOp>())
// if (objFifo.name() == getObjFifoName())
// return objFifo;
// }
// }
// return ObjectFifoCreateOp();
// }

const xilinx::AIE::AIETargetModel &xilinx::AIE::DeviceOp::getTargetModel() {
switch (getDevice()) {
Expand Down Expand Up @@ -1466,7 +1430,10 @@ bool TileOp::isShimPLTile() {
const auto &target_model = getTargetModel(*this);
return target_model.isShimPLTile(getCol(), getRow());
}
bool TileOp::isShimNOCorPLTile() { return isShimNOCTile() || isShimPLTile(); }
bool TileOp::isShimNOCorPLTile() {
const auto &target_model = getTargetModel(*this);
return target_model.isShimNOCorPLTile(getCol(), getRow());
}
} // namespace AIE
} // namespace xilinx

Expand Down
4 changes: 2 additions & 2 deletions lib/Dialect/AIE/IR/AIETargetModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ AIE1TargetModel::getNumSourceSwitchboxConnections(int col, int row,
uint32_t
AIE1TargetModel::getNumDestShimMuxConnections(int col, int row,
WireBundle bundle) const {
if (isShimNOCTile(col, row))
if (isShimNOCorPLTile(col, row))
switch (bundle) {
case WireBundle::DMA:
return 2;
Expand Down Expand Up @@ -462,7 +462,7 @@ AIE2TargetModel::getNumSourceSwitchboxConnections(int col, int row,
uint32_t
AIE2TargetModel::getNumDestShimMuxConnections(int col, int row,
WireBundle bundle) const {
if (isShimNOCTile(col, row))
if (isShimNOCorPLTile(col, row))
switch (bundle) {
case WireBundle::DMA:
return 2;
Expand Down
Loading

0 comments on commit 8fd0b2b

Please sign in to comment.