@@ -163,7 +163,8 @@ struct ConvertFlowsToInterconnect : OpConversionPattern<FlowOp> {
163163 }
164164 }
165165
166- LLVM_DEBUG (llvm::dbgs () << tileId << " : " << setting << " | " << " \n " );
166+ LLVM_DEBUG (llvm::dbgs () << tileId << " : " << setting << " | "
167+ << " \n " );
167168 }
168169
169170 LLVM_DEBUG (llvm::dbgs ()
@@ -378,13 +379,20 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder,
378379 // Therefore, the number of "logical" arbiters is 6 x 4 = 24
379380 // A master port can only be associated with one arbiter
380381
382+ // Constants for arbiter configuration
383+ constexpr int INVALID_AMSEL_VALUE = -1 ;
384+ constexpr int INVALID_ARBITER_VALUE = -1 ;
385+
381386 // A map from Tile and master selectValue to the ports targetted by that
382387 // master select.
383388 std::map<std::pair<TileID, int >, SmallVector<Port, 4 >> masterAMSels;
384389
390+ // Track which arbiter each port is assigned to (to prevent conflicts)
391+ std::map<PhysPort, int > portToArbiter;
392+
385393 // Count of currently used logical arbiters for each tile.
386394 DenseMap<Operation *, int > amselValues;
387- int numMsels = 4 ;
395+ int numMselsPerArbiter = 4 ;
388396 int numArbiters = 6 ;
389397
390398 // Get arbiter id from amsel
@@ -398,36 +406,38 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder,
398406 // Get a new unique amsel from masterAMSels on tile op. Prioritize on
399407 // incrementing arbiter id, before incrementing msel
400408 auto getNewUniqueAmsel =
401- [&](std::map<std::pair<TileID, int >, SmallVector<Port, 4 >> masterAMSels,
409+ [&](const std::map<std::pair<TileID, int >, SmallVector<Port, 4 >>
410+ &masterAMSels,
402411 TileOp tileOp, bool isCtrlPkt) {
403412 if (isCtrlPkt) { // Higher AMsel first
404- for (int i = numMsels - 1 ; i >= 0 ; i--)
413+ for (int i = numMselsPerArbiter - 1 ; i >= 0 ; i--)
405414 for (int a = numArbiters - 1 ; a >= 0 ; a--)
406415 if (!masterAMSels.count (
407416 {tileOp.getTileID (), getAmselFromArbiterIDAndMsel (a, i)}))
408417 return getAmselFromArbiterIDAndMsel (a, i);
409418 } else { // Lower AMsel first
410- for (int i = 0 ; i < numMsels ; i++)
419+ for (int i = 0 ; i < numMselsPerArbiter ; i++)
411420 for (int a = 0 ; a < numArbiters; a++)
412421 if (!masterAMSels.count (
413422 {tileOp.getTileID (), getAmselFromArbiterIDAndMsel (a, i)}))
414423 return getAmselFromArbiterIDAndMsel (a, i);
415424 }
416425 tileOp->emitOpError (
417426 " tile op has used up all arbiter-msel combinations" );
418- return - 1 ;
427+ return INVALID_AMSEL_VALUE ;
419428 };
420429 // Get a new unique amsel from masterAMSels on tile op with given arbiter id
421430 auto getNewUniqueAmselPerArbiterID =
422- [&](std::map<std::pair<TileID, int >, SmallVector<Port, 4 >> masterAMSels,
431+ [&](const std::map<std::pair<TileID, int >, SmallVector<Port, 4 >>
432+ &masterAMSels,
423433 TileOp tileOp, int arbiter) {
424- for (int i = 0 ; i < numMsels ; i++)
434+ for (int i = 0 ; i < numMselsPerArbiter ; i++)
425435 if (!masterAMSels.count ({tileOp.getTileID (),
426436 getAmselFromArbiterIDAndMsel (arbiter, i)}))
427437 return getAmselFromArbiterIDAndMsel (arbiter, i);
428438 tileOp->emitOpError (" tile op arbiter " )
429- << std::to_string (arbiter) << " has used up all its msels" ;
430- return - 1 ;
439+ << std::to_string (arbiter) << " has used up all its msels" ;
440+ return INVALID_AMSEL_VALUE ;
431441 };
432442
433443 // Order the packet flows in order to get determinsitic amsel allocation;
@@ -453,6 +463,46 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder,
453463
454464 packetFlows.insert (ctrlPacketFlows.begin (), ctrlPacketFlows.end ());
455465
466+ // Helper function to assign ports to a given amsel value
467+ // Updates masterAMSels and portToArbiter, skipping ports already on different
468+ // arbiters
469+ auto assignPortsToAmsel = [&](TileID tileId, int amselValue,
470+ const SmallVector<PhysPort, 4 > &destinations) {
471+ int targetArbiter = getArbiterIDFromAmsel (amselValue);
472+ for (auto dest : destinations) {
473+ Port port = dest.second ;
474+ PhysPort physPort = {tileId, port};
475+
476+ // Skip this port if it's already assigned to a different arbiter
477+ if (portToArbiter.count (physPort) &&
478+ portToArbiter[physPort] != targetArbiter) {
479+ LLVM_DEBUG (llvm::dbgs ()
480+ << " Skipping port " << stringifyWireBundle (port.bundle )
481+ << " :" << port.channel << " - already on arbiter "
482+ << portToArbiter[physPort] << " , target is " << targetArbiter
483+ << " \n " );
484+ continue ;
485+ }
486+
487+ masterAMSels[{tileId, amselValue}].push_back (port);
488+ portToArbiter[physPort] = targetArbiter;
489+ }
490+ };
491+
492+ // Helper function to find existing arbiter assignment for ports in a flow
493+ // Returns -1 if no existing assignment found
494+ auto findExistingArbiter =
495+ [&](TileID tileId, const SmallVector<PhysPort, 4 > &destinations) -> int {
496+ for (auto dest : destinations) {
497+ Port port = dest.second ;
498+ PhysPort physPort = {tileId, port};
499+ if (portToArbiter.count (physPort)) {
500+ return portToArbiter[physPort];
501+ }
502+ }
503+ return INVALID_ARBITER_VALUE;
504+ };
505+
456506 // Check all multi-cast flows (same source, same ID). They should be
457507 // assigned the same arbiter and msel so that the flow can reach all the
458508 // destination ports at the same time For destination ports that appear in
@@ -475,75 +525,128 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder,
475525 int amselValue = amselValues[tileOp];
476526 assert (amselValue < numArbiters && " Could not allocate new arbiter!" );
477527
478- // Find existing arbiter assignment
479- // If there is an assignment of an arbiter to a master port before, we
480- // assign all the master ports here with the same arbiter but different
481- // msel
482- bool foundMatchedDest =
483- false ; // This switchbox's output channels match completely or
484- // partially with an existing amsel entry.
485- int foundPartialMatchArbiter =
486- -1 ; // This switchbox's output channels match partially with an
487- // existing amsel entry on this arbiter ID (-1 means null).
488- for (const auto &map : masterAMSels) {
489- if (map.first .first != tileId)
528+ // Find existing arbiter and amsel assignments for this flow
529+ // Strategy: Look for existing amsel entries that match the flow's
530+ // destinations
531+ // - Complete match: reuse existing amsel
532+ // - Partial match: create new amsel on same arbiter
533+ // - No match: create new amsel (on existing arbiter if ports already
534+ // assigned)
535+ bool hasMatchingAmselEntry = false ;
536+ int partialMatchArbiterID = INVALID_ARBITER_VALUE;
537+
538+ // Check if any ports in this flow already have arbiter assignments
539+ int existingArbiter = findExistingArbiter (tileId, packetFlow.second );
540+
541+ // Search for matching amsel entries, prioritizing those that match the
542+ // existing arbiter
543+ for (const auto &amselEntry : masterAMSels) {
544+ if (amselEntry.first .first != tileId)
545+ continue ;
546+ amselValue = amselEntry.first .second ;
547+ int thisArbiter = getArbiterIDFromAmsel (amselValue);
548+
549+ // If we have an existing arbiter assignment, only consider amsels from
550+ // that arbiter
551+ if (existingArbiter != INVALID_ARBITER_VALUE &&
552+ existingArbiter != thisArbiter) {
490553 continue ;
491- amselValue = map.first .second ;
492-
493- // check if same destinations
494- SmallVector<Port, 4 > ports (masterAMSels[{tileId, amselValue}]);
495- // check for complete/partial overlapping amsel -> port mapping with any
496- // previous amsel assignments
497- bool matched =
498- false ; // Found at least one port with overlapping amsel assignment
499- bool mismatched = false ; // Found at least one port without any
500- // overlapping amsel assignment
554+ }
555+
556+ // Check if destinations match (completely or partially)
557+ const SmallVector<Port, 4 > &existingPorts =
558+ masterAMSels[{tileId, amselValue}];
559+ bool hasOverlap = false ;
560+ bool hasNonOverlap = false ;
561+
501562 for (auto dest : packetFlow.second ) {
502563 Port port = dest.second ;
503- if (std::find (ports.begin (), ports.end (), port) == ports.end ())
504- mismatched = true ;
564+ if (std::find (existingPorts.begin (), existingPorts.end (), port) ==
565+ existingPorts.end ())
566+ hasNonOverlap = true ;
505567 else
506- matched = true ;
568+ hasOverlap = true ;
507569 }
508570
509- if (matched) {
510- foundMatchedDest = true ;
511- if (mismatched)
512- foundPartialMatchArbiter = getArbiterIDFromAmsel (amselValue);
513- else if (ports.size () != packetFlow.second .size ())
514- foundPartialMatchArbiter = getArbiterIDFromAmsel (amselValue);
571+ if (hasOverlap) {
572+ hasMatchingAmselEntry = true ;
573+ // Partial match if some ports don't overlap or sizes differ
574+ if (hasNonOverlap || existingPorts.size () != packetFlow.second .size ())
575+ partialMatchArbiterID = thisArbiter;
515576 break ;
516577 }
517578 }
518579
519- if (!foundMatchedDest ) {
580+ if (!hasMatchingAmselEntry ) {
520581 // This packet flow switchbox's output ports completely mismatches with
521582 // any existing amsel. Creating a new amsel.
522583
523- // Check if any of the master ports have ever been used for ctrl pkts.
524- // Ctrl pkt (i.e. prioritized packet flow) amsel assignment follows a
525- // different strategy (see method below).
526- bool ctrlPktAMsel =
527- llvm::any_of (packetFlow.second , [&](PhysPort destPhysPort) {
528- Port port = destPhysPort.second ;
529- return ctrlPktFlows[{{tileId, port}, packetFlow.first .second }];
530- });
531-
532- amselValue = getNewUniqueAmsel (masterAMSels, tileOp, ctrlPktAMsel);
533- // Update masterAMSels with new amsel
534- for (auto dest : packetFlow.second ) {
535- Port port = dest.second ;
536- masterAMSels[{tileId, amselValue}].push_back (port);
584+ // Determine target arbiter: use existing if available, otherwise allocate
585+ // based on priority
586+ int targetArbiter = existingArbiter;
587+
588+ if (targetArbiter == INVALID_ARBITER_VALUE) {
589+ // No existing assignment, choose based on control packet priority
590+ bool isCtrlPkt =
591+ llvm::any_of (packetFlow.second , [&](PhysPort destPhysPort) {
592+ Port port = destPhysPort.second ;
593+ return ctrlPktFlows[{{tileId, port}, packetFlow.first .second }];
594+ });
595+
596+ amselValue = getNewUniqueAmsel (masterAMSels, tileOp, isCtrlPkt);
597+ } else {
598+ // Use existing arbiter to maintain consistency
599+ amselValue =
600+ getNewUniqueAmselPerArbiterID (masterAMSels, tileOp, targetArbiter);
601+ if (amselValue == INVALID_AMSEL_VALUE) {
602+ // No more msels available on this arbiter - this is a routing
603+ // conflict
604+ tileOp->emitOpError (" cannot assign flow: arbiter " )
605+ << targetArbiter
606+ << " has no free msels, but flow requires this arbiter due to "
607+ " existing port assignments" ;
608+ return ;
609+ }
537610 }
538- } else if (foundPartialMatchArbiter >= 0 ) {
611+
612+ // Update masterAMSels with new amsel, skipping ports already assigned to
613+ // different arbiters
614+ assignPortsToAmsel (tileId, amselValue, packetFlow.second );
615+ } else if (partialMatchArbiterID != INVALID_ARBITER_VALUE) {
539616 // This packet flow switchbox's output ports partially overlaps with
540- // some existing amsel. Creating a new amsel with the same arbiter.
541- amselValue = getNewUniqueAmselPerArbiterID (masterAMSels, tileOp,
542- foundPartialMatchArbiter);
543- // Update masterAMSels with new amsel
617+ // some existing amsel. Create a NEW amsel with the SAME arbiter for this
618+ // flow. The comment states: "destination ports that appear in different
619+ // (multicast) flows should have a different <arbiterID, msel> value pair
620+ // for each flow" but use the same arbiter to maintain the constraint.
621+
622+ // Use the arbiter we found (which should match existingArbiter if set)
623+ int targetArbiter = partialMatchArbiterID;
624+ if (existingArbiter != INVALID_ARBITER_VALUE &&
625+ existingArbiter != targetArbiter) {
626+ // Conflict detected - should have been caught earlier, but add safety
627+ // check
628+ tileOp->emitOpError (
629+ " internal error: arbiter conflict in partial match" );
630+ return ;
631+ }
632+
633+ amselValue =
634+ getNewUniqueAmselPerArbiterID (masterAMSels, tileOp, targetArbiter);
635+
636+ // Update masterAMSels with new amsel, skipping ports already assigned to
637+ // different arbiters
638+ assignPortsToAmsel (tileId, amselValue, packetFlow.second );
639+ } else {
640+ // Complete match - reuse the existing amsel
641+ // Track arbiter assignments for all ports in this flow
642+ int arbiter = getArbiterIDFromAmsel (amselValue);
544643 for (auto dest : packetFlow.second ) {
545644 Port port = dest.second ;
546- masterAMSels[{tileId, amselValue}].push_back (port);
645+ PhysPort physPort = {tileId, port};
646+ // Update tracking even for reused amsels
647+ if (!portToArbiter.count (physPort)) {
648+ portToArbiter[physPort] = arbiter;
649+ }
547650 }
548651 }
549652
@@ -552,14 +655,38 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder,
552655 }
553656
554657 // Compute the master set IDs
555- // A map from a switchbox output port to the number of that port.
658+ // A map from a switchbox output port to its associated amsel values
556659 std::map<PhysPort, SmallVector<int , 4 >> mastersets;
557660 for (const auto &[physPort, ports] : masterAMSels) {
558661 TileID tileId = physPort.first ;
559662 int amselValue = physPort.second ;
560663 for (auto port : ports) {
561- PhysPort pp = {tileId, port};
562- mastersets[pp].push_back (amselValue);
664+ PhysPort physPort = {tileId, port};
665+ mastersets[physPort].push_back (amselValue);
666+ }
667+ }
668+
669+ // Validate that each port only has amsels from a single arbiter
670+ for (const auto &[physPort, amselList] : mastersets) {
671+ int assignedArbiter = INVALID_ARBITER_VALUE;
672+ for (auto amsel : amselList) {
673+ int thisArbiter = getArbiterIDFromAmsel (amsel);
674+ if (assignedArbiter != INVALID_ARBITER_VALUE &&
675+ assignedArbiter != thisArbiter) {
676+ TileID tileId = physPort.first ;
677+ Port port = physPort.second ;
678+ TileOp tileOp = analyzer.getTile (builder, tileId);
679+ tileOp->emitOpError (" port " )
680+ << stringifyWireBundle (port.bundle ) << " :" << port.channel
681+ << " assigned to multiple arbiters: " << assignedArbiter << " and "
682+ << thisArbiter << " (amsels: " << amselList[0 ];
683+ for (size_t i = 1 ; i < amselList.size (); i++) {
684+ llvm::errs () << " , " << amselList[i];
685+ }
686+ llvm::errs () << " )\n " ;
687+ return signalPassFailure ();
688+ }
689+ assignedArbiter = thisArbiter;
563690 }
564691 }
565692
@@ -661,14 +788,14 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder,
661788 LLVM_DEBUG (llvm::dbgs ()
662789 << " Port " << tile << " " << stringifyWireBundle (bundle) << " "
663790 << channel << ' \n ' );
664- LLVM_DEBUG (llvm::dbgs ()
665- << " Mask " << " 0x" << llvm::Twine::utohexstr (mask) << ' \n ' );
666- LLVM_DEBUG (llvm::dbgs ()
667- << " ID " << " 0x" << llvm::Twine::utohexstr (ID) << ' \n ' );
791+ LLVM_DEBUG (llvm::dbgs () << " Mask "
792+ << " 0x" << llvm::Twine::utohexstr (mask) << ' \n ' );
793+ LLVM_DEBUG (llvm::dbgs () << " ID "
794+ << " 0x" << llvm::Twine::utohexstr (ID) << ' \n ' );
668795 for (int i = 0 ; i < 31 ; i++) {
669796 if ((i & mask) == (ID & mask))
670- LLVM_DEBUG (llvm::dbgs () << " matches flow ID " << " 0x "
671- << llvm::Twine::utohexstr (i) << ' \n ' );
797+ LLVM_DEBUG (llvm::dbgs () << " matches flow ID "
798+ << " 0x " << llvm::Twine::utohexstr (i) << ' \n ' );
672799 }
673800 }
674801#endif
@@ -703,7 +830,7 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder,
703830 Block &b = swbox.getConnections ().front ();
704831 builder.setInsertionPoint (b.getTerminator ());
705832
706- std::vector<bool > amselOpNeededVector (numMsels * numArbiters);
833+ std::vector<bool > amselOpNeededVector (numMselsPerArbiter * numArbiters);
707834 for (const auto &map : mastersets) {
708835 if (tileId != map.first .first )
709836 continue ;
@@ -714,7 +841,7 @@ void AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder,
714841 }
715842 // Create all the amsel Ops
716843 std::map<int , AMSelOp> amselOps;
717- for (int i = 0 ; i < numMsels ; i++) {
844+ for (int i = 0 ; i < numMselsPerArbiter ; i++) {
718845 for (int a = 0 ; a < numArbiters; a++) {
719846 auto amselValue = getAmselFromArbiterIDAndMsel (a, i);
720847 if (amselOpNeededVector[amselValue]) {
0 commit comments