From aec688df0ecea1a12ba723f69157e795ba698f93 Mon Sep 17 00:00:00 2001 From: Robin Melhuish Date: Sat, 14 Mar 2026 11:03:14 -0700 Subject: [PATCH 1/2] Expose set_aie_stream() on IRON ObjectFifo class Add set_aie_stream(stream_end, stream_port) method to the IRON-level ObjectFifo class, plumbing through to the existing low-level object_fifo.set_aie_stream() during resolve(). This allows IRON designs to use Core stream ports instead of DMA channels for ObjectFifo connections, enabling tiles with more than 2 input data flows (2 DMA + N stream ports). The low-level dialect already supports aie_stream (PR #2693) but it was not exposed at the IRON Python API level. --- python/iron/dataflow/objectfifo.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/python/iron/dataflow/objectfifo.py b/python/iron/dataflow/objectfifo.py index ef6bc9de614..860887aaf68 100644 --- a/python/iron/dataflow/objectfifo.py +++ b/python/iron/dataflow/objectfifo.py @@ -79,6 +79,8 @@ def __init__( self._cons: list[ObjectFifoHandle] = [] self._resolving = False self._iter_count: int | None = None + self._aie_stream: int | None = None + self._aie_stream_port: int | None = None @classmethod def __get_index(cls) -> int: @@ -137,6 +139,19 @@ def set_iter_count(self, iter_count: int): self._iter_count = iter_count + def set_aie_stream(self, stream_end: int, stream_port: int = 0): + """Configure one end of this ObjectFifo to use a Core stream port + instead of DMA, bypassing L1 buffering. + + Args: + stream_end: 0 (producer stream), 1 (consumer stream), 2 (both) + stream_port: Core stream port index (default 0) + """ + if stream_end not in (0, 1, 2): + raise ValueError("stream_end must be 0 (producer), 1 (consumer), or 2 (both)") + self._aie_stream = stream_end + self._aie_stream_port = stream_port + def __str__(self) -> str: prod_endpoint = None if self._prod: @@ -300,6 +315,9 @@ def resolve( iter_count=self._iter_count, ) + if self._aie_stream is not None: + self._op.set_aie_stream(self._aie_stream, self._aie_stream_port) + if isinstance(self._prod.endpoint, ObjectFifoLink): self._prod.endpoint.resolve() for con in self._cons: From 6310f3639b76de3568649346b13f57e6887a7173 Mon Sep 17 00:00:00 2001 From: Robin Melhuish Date: Sat, 14 Mar 2026 11:28:48 -0700 Subject: [PATCH 2/2] Skip DMA channel allocation for stream-port ObjectFIFOs When aie_stream is set on an ObjectFIFO endpoint, that endpoint uses a Core stream port instead of a DMA channel. The DMA channel allocation in assignDMAChannelIndices() was not checking for this attribute, causing stream-port FIFOs to consume DMA channel slots. This led to false number of input DMA channel exceeded errors when mixing stream and DMA ObjectFIFOs on tiles that would otherwise have sufficient DMA channels. Skip DMA channel allocation for: - Producers with aie_stream = 0 or 2 (producer uses Core stream port) - Consumers with aie_stream = 1 or 2 (consumer uses Core stream port) The downstream flow creation code (lines 2096+) already correctly handles stream vs DMA routing independently, so skipping the allocation is safe. --- .../AIEObjectFifoStatefulTransform.cpp | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/Dialect/AIE/Transforms/AIEObjectFifoStatefulTransform.cpp b/lib/Dialect/AIE/Transforms/AIEObjectFifoStatefulTransform.cpp index 121c1a73be7..71c3167484f 100644 --- a/lib/Dialect/AIE/Transforms/AIEObjectFifoStatefulTransform.cpp +++ b/lib/Dialect/AIE/Transforms/AIEObjectFifoStatefulTransform.cpp @@ -1812,11 +1812,20 @@ struct AIEObjectFifoStatefulTransformPass : !crossTileInfos.at(producer); if (shouldProcessProducer) { - bool requiresAdjacentTileAccessChannels = crossTileInfos.at(producer); - int channelIndex = dmaAnalysis.getDMAChannelIndex( - producer.getProducerTileOp(), DMAChannelDir::MM2S, - requiresAdjacentTileAccessChannels); - fifo_dma_channel_index[producer] = channelIndex; + // Skip stream-port producers — they use Core ports, not DMA channels + bool prodIsStream = false; + if (producer.getAieStream()) { + int streamEnd = producer.getAieStream().value(); + prodIsStream = (streamEnd == 0 || streamEnd == 2); + } + if (!prodIsStream) { + bool requiresAdjacentTileAccessChannels = + crossTileInfos.at(producer); + int channelIndex = dmaAnalysis.getDMAChannelIndex( + producer.getProducerTileOp(), DMAChannelDir::MM2S, + requiresAdjacentTileAccessChannels); + fifo_dma_channel_index[producer] = channelIndex; + } } for (auto consumer : consumers) { @@ -1827,6 +1836,12 @@ struct AIEObjectFifoStatefulTransformPass : !crossTileInfos.at(consumer); if (shouldProcessConsumer) { + // Skip stream-port consumers — they use Core ports, not DMA channels + if (consumer.getAieStream()) { + int streamEnd = consumer.getAieStream().value(); + if (streamEnd == 1 || streamEnd == 2) + continue; + } bool requiresAdjacentTileAccessChannels = crossTileInfos.at(consumer); int channelIndex = dmaAnalysis.getDMAChannelIndex( consumer.getProducerTileOp(), DMAChannelDir::S2MM,