274 ConversionTarget target(getContext());
276 std::map<TileID, mlir::Operation *> tiles;
279 std::map<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>> packetFlows;
280 std::map<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>> ctrlPacketFlows;
281 SmallVector<std::pair<PhysPort, int>, 4> slavePorts;
282 DenseMap<std::pair<PhysPort, int>,
int> slaveAMSels;
284 DenseMap<PhysPort, BoolAttr> keepPktHeaderAttr;
286 DenseMap<std::pair<PhysPort, int>,
bool> ctrlPktFlows;
288 for (
auto tileOp : device.getOps<TileOp>()) {
289 int col = tileOp.colIndex();
290 int row = tileOp.rowIndex();
291 tiles[{
col,
row}] = tileOp;
295 std::map<TileID, SmallVector<std::pair<Connect, int>, 8>> switchboxes;
296 for (PacketFlowOp pktFlowOp : device.getOps<PacketFlowOp>()) {
297 Region &r = pktFlowOp.getPorts();
298 Block &b = r.front();
299 int flowID = pktFlowOp.IDInt();
300 Port srcPort, destPort;
301 TileOp srcTile, destTile;
305 for (Operation &Op : b.getOperations()) {
306 if (
auto pktSource = dyn_cast<PacketSourceOp>(Op)) {
307 srcTile = dyn_cast<TileOp>(pktSource.getTile().getDefiningOp());
308 srcPort = pktSource.port();
309 srcCoords = {srcTile.colIndex(), srcTile.rowIndex()};
313 return pktFlowOp.emitOpError(
"packet_flow has no packet_source");
315 for (Operation &Op : b.getOperations()) {
316 if (
auto pktDest = dyn_cast<PacketDestOp>(Op)) {
317 destTile = dyn_cast<TileOp>(pktDest.getTile().getDefiningOp());
318 destPort = pktDest.port();
319 destCoords = {destTile.colIndex(), destTile.rowIndex()};
321 auto keep = pktFlowOp.getKeepPktHeader();
322 keepPktHeaderAttr[{destTile.getTileID(), destPort}] =
323 keep ? BoolAttr::get(Op.getContext(), *keep) :
nullptr;
330 for (
const auto &[curr, setting] : settings) {
331 assert(setting.srcs.size() == setting.dsts.size());
332 TileID currTile = {curr.col, curr.row};
333 for (
size_t i = 0; i < setting.srcs.size(); i++) {
335 Port dest = setting.dsts[i];
337 if (!
findPathToDest(settings, currTile, dest.bundle, dest.channel,
338 destCoords, destPort.bundle,
342 {dest.bundle, dest.channel}};
343 if (std::find(switchboxes[currTile].begin(),
344 switchboxes[currTile].end(),
345 std::pair{connect, flowID}) ==
346 switchboxes[currTile].end())
347 switchboxes[currTile].push_back({connect, flowID});
350 auto ctrlPkt = pktFlowOp.getPriorityRoute();
351 ctrlPktFlows[{{currTile, dest}, flowID}] =
352 ctrlPkt ? *ctrlPkt :
false;
360 LLVM_DEBUG(llvm::dbgs() <<
"Check switchboxes\n");
362 for (
const auto &[tileId, connects] : switchboxes) {
363 LLVM_DEBUG(llvm::dbgs() <<
"***switchbox*** " << tileId.col <<
" "
364 << tileId.row <<
'\n');
365 for (
const auto &[conn, flowID] : connects) {
366 Port sourcePort = conn.src;
367 Port destPort = conn.dst;
369 std::make_pair(std::make_pair(tileId, sourcePort), flowID);
370 if (ctrlPktFlows[{{tileId, destPort}, flowID}])
371 ctrlPacketFlows[sourceFlow].push_back({tileId, destPort});
373 packetFlows[sourceFlow].push_back({tileId, destPort});
374 slavePorts.push_back(sourceFlow);
375 LLVM_DEBUG(llvm::dbgs() <<
"flowID " << flowID <<
':'
376 << stringifyWireBundle(sourcePort.bundle) <<
" "
377 << sourcePort.channel <<
" -> "
378 << stringifyWireBundle(destPort.bundle) <<
" "
379 << destPort.channel <<
"\n");
393 constexpr int INVALID_AMSEL_VALUE = -1;
394 constexpr int INVALID_ARBITER_VALUE = -1;
398 std::map<std::pair<TileID, int>, SmallVector<Port, 4>> masterAMSels;
401 std::map<PhysPort, int> portToArbiter;
404 DenseMap<Operation *, int> amselValues;
405 int numMselsPerArbiter = 4;
409 auto getArbiterIDFromAmsel = [numArbiters](
int amsel) {
410 return amsel % numArbiters;
413 auto getAmselFromArbiterIDAndMsel = [numArbiters](
int arbiter,
int msel) {
414 return arbiter + msel * numArbiters;
418 auto getNewUniqueAmsel =
419 [&](
const std::map<std::pair<TileID, int>, SmallVector<Port, 4>>
421 TileOp tileOp,
bool isCtrlPkt) {
423 for (
int i = numMselsPerArbiter - 1; i >= 0; i--)
424 for (
int a = numArbiters - 1; a >= 0; a--)
425 if (!masterAMSels.count(
426 {tileOp.getTileID(), getAmselFromArbiterIDAndMsel(a, i)}))
427 return getAmselFromArbiterIDAndMsel(a, i);
429 for (
int i = 0; i < numMselsPerArbiter; i++)
430 for (
int a = 0; a < numArbiters; a++)
431 if (!masterAMSels.count(
432 {tileOp.getTileID(), getAmselFromArbiterIDAndMsel(a, i)}))
433 return getAmselFromArbiterIDAndMsel(a, i);
436 "tile op has used up all arbiter-msel combinations");
437 return INVALID_AMSEL_VALUE;
440 auto getNewUniqueAmselPerArbiterID =
441 [&](
const std::map<std::pair<TileID, int>, SmallVector<Port, 4>>
443 TileOp tileOp,
int arbiter) {
444 for (
int i = 0; i < numMselsPerArbiter; i++)
445 if (!masterAMSels.count({tileOp.getTileID(),
446 getAmselFromArbiterIDAndMsel(arbiter, i)}))
447 return getAmselFromArbiterIDAndMsel(arbiter, i);
448 tileOp->emitOpError(
"tile op arbiter ")
449 << std::to_string(arbiter) <<
" has used up all its msels";
450 return INVALID_AMSEL_VALUE;
456 auto getSortedPacketFlows =
457 [&](std::map<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>> pktFlows,
458 std::map<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>
461 std::pair<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>>
462 sortedpktFlows(pktFlows.begin(), pktFlows.end());
464 std::pair<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>>
465 sortedctrlpktFlows(ctrlPktFlows.begin(), ctrlPktFlows.end());
466 sortedctrlpktFlows.insert(sortedctrlpktFlows.end(),
467 sortedpktFlows.begin(), sortedpktFlows.end());
468 return sortedctrlpktFlows;
471 std::vector<std::pair<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>>
472 sortedPacketFlows = getSortedPacketFlows(packetFlows, ctrlPacketFlows);
474 packetFlows.insert(ctrlPacketFlows.begin(), ctrlPacketFlows.end());
479 auto assignPortsToAmsel = [&](
TileID tileId,
int amselValue,
480 const SmallVector<PhysPort, 4> &destinations) {
481 int targetArbiter = getArbiterIDFromAmsel(amselValue);
482 for (
auto dest : destinations) {
484 PhysPort physPort = {tileId,
port};
487 if (portToArbiter.count(physPort) &&
488 portToArbiter[physPort] != targetArbiter) {
489 LLVM_DEBUG(llvm::dbgs()
490 <<
"Skipping port " << stringifyWireBundle(
port.bundle)
491 <<
":" <<
port.channel <<
" - already on arbiter "
492 << portToArbiter[physPort] <<
", target is " << targetArbiter
497 masterAMSels[{tileId, amselValue}].push_back(
port);
498 portToArbiter[physPort] = targetArbiter;
504 auto findExistingArbiter =
505 [&](
TileID tileId,
const SmallVector<PhysPort, 4> &destinations) ->
int {
506 for (
auto dest : destinations) {
508 PhysPort physPort = {tileId,
port};
509 if (portToArbiter.count(physPort)) {
510 return portToArbiter[physPort];
513 return INVALID_ARBITER_VALUE;
521 for (
const auto &packetFlow : sortedPacketFlows) {
523 TileID tileId = packetFlow.first.first.first;
524 TileOp tileOp = analyzer.getTile(builder, tileId);
525 if (amselValues.count(tileOp) == 0)
526 amselValues[tileOp] = 0;
535 int amselValue = amselValues[tileOp];
536 assert(amselValue < numArbiters &&
"Could not allocate new arbiter!");
545 bool hasMatchingAmselEntry =
false;
546 int partialMatchArbiterID = INVALID_ARBITER_VALUE;
549 int existingArbiter = findExistingArbiter(tileId, packetFlow.second);
553 for (
const auto &amselEntry : masterAMSels) {
554 if (amselEntry.first.first != tileId)
556 amselValue = amselEntry.first.second;
557 int thisArbiter = getArbiterIDFromAmsel(amselValue);
561 if (existingArbiter != INVALID_ARBITER_VALUE &&
562 existingArbiter != thisArbiter) {
567 const SmallVector<Port, 4> &existingPorts =
568 masterAMSels[{tileId, amselValue}];
569 bool hasOverlap =
false;
570 bool hasNonOverlap =
false;
572 for (
auto dest : packetFlow.second) {
574 if (std::find(existingPorts.begin(), existingPorts.end(),
port) ==
576 hasNonOverlap =
true;
582 hasMatchingAmselEntry =
true;
584 if (hasNonOverlap || existingPorts.size() != packetFlow.second.size())
585 partialMatchArbiterID = thisArbiter;
590 if (!hasMatchingAmselEntry) {
596 int targetArbiter = existingArbiter;
598 if (targetArbiter == INVALID_ARBITER_VALUE) {
601 llvm::any_of(packetFlow.second, [&](PhysPort destPhysPort) {
602 Port port = destPhysPort.second;
603 return ctrlPktFlows[{{tileId, port}, packetFlow.first.second}];
606 amselValue = getNewUniqueAmsel(masterAMSels, tileOp, isCtrlPkt);
607 if (amselValue == INVALID_AMSEL_VALUE)
612 getNewUniqueAmselPerArbiterID(masterAMSels, tileOp, targetArbiter);
613 if (amselValue == INVALID_AMSEL_VALUE) {
616 tileOp->emitOpError(
"cannot assign flow: arbiter ")
618 <<
" has no free msels, but flow requires this arbiter due to "
619 "existing port assignments";
626 assignPortsToAmsel(tileId, amselValue, packetFlow.second);
627 }
else if (partialMatchArbiterID != INVALID_ARBITER_VALUE) {
635 int targetArbiter = partialMatchArbiterID;
636 if (existingArbiter != INVALID_ARBITER_VALUE &&
637 existingArbiter != targetArbiter) {
641 "internal error: arbiter conflict in partial match");
646 getNewUniqueAmselPerArbiterID(masterAMSels, tileOp, targetArbiter);
650 assignPortsToAmsel(tileId, amselValue, packetFlow.second);
654 int arbiter = getArbiterIDFromAmsel(amselValue);
655 for (
auto dest : packetFlow.second) {
657 PhysPort physPort = {tileId,
port};
659 if (!portToArbiter.count(physPort)) {
660 portToArbiter[physPort] = arbiter;
665 slaveAMSels[packetFlow.first] = amselValue;
666 amselValues[tileOp] = getArbiterIDFromAmsel(amselValue);
671 std::map<PhysPort, SmallVector<int, 4>> mastersets;
672 for (
const auto &[physPort, ports] : masterAMSels) {
673 TileID tileId = physPort.first;
674 int amselValue = physPort.second;
675 for (
auto port : ports) {
676 PhysPort physPort = {tileId,
port};
677 mastersets[physPort].push_back(amselValue);
682 for (
const auto &[physPort, amselList] : mastersets) {
683 int assignedArbiter = INVALID_ARBITER_VALUE;
684 for (
auto amsel : amselList) {
685 int thisArbiter = getArbiterIDFromAmsel(amsel);
686 if (assignedArbiter != INVALID_ARBITER_VALUE &&
687 assignedArbiter != thisArbiter) {
688 TileID tileId = physPort.first;
690 TileOp tileOp = analyzer.getTile(builder, tileId);
691 tileOp->emitOpError(
"port ")
692 << stringifyWireBundle(
port.bundle) <<
":" <<
port.channel
693 <<
" assigned to multiple arbiters: " << assignedArbiter <<
" and "
694 << thisArbiter <<
" (amsels: " << amselList[0];
695 for (
size_t i = 1; i < amselList.size(); i++) {
696 llvm::errs() <<
", " << amselList[i];
698 llvm::errs() <<
")\n";
701 assignedArbiter = thisArbiter;
705 LLVM_DEBUG(llvm::dbgs() <<
"CHECK mastersets\n");
707 for (
const auto &[physPort, values] : mastersets) {
708 TileID tileId = physPort.first;
709 WireBundle bundle = physPort.second.bundle;
710 int channel = physPort.second.channel;
711 LLVM_DEBUG(llvm::dbgs()
712 <<
"master " << tileId <<
" " << stringifyWireBundle(bundle)
714 for (
auto value : values)
715 LLVM_DEBUG(
llvm::dbgs() <<
"amsel: " <<
value <<
'\n');
723 SmallVector<SmallVector<std::pair<PhysPort, int>, 4>, 4> slaveGroups;
724 SmallVector<std::pair<PhysPort, int>, 4> workList(slavePorts);
725 while (!workList.empty()) {
726 auto slave1 = workList.pop_back_val();
727 Port slavePort1 = slave1.first.second;
729 bool foundgroup =
false;
730 for (
auto &group : slaveGroups) {
731 auto slave2 = group.front();
732 if (
Port slavePort2 = slave2.first.second; slavePort1 != slavePort2)
736 auto dests1 = packetFlows[slave1];
737 auto dests2 = packetFlows[slave2];
738 if (dests1.size() != dests2.size())
741 for (
auto dest1 : dests1) {
742 if (std::find(dests2.begin(), dests2.end(), dest1) == dests2.end()) {
749 group.push_back(slave1);
756 SmallVector<std::pair<PhysPort, int>, 4> group({slave1});
757 slaveGroups.push_back(group);
761 std::map<std::pair<PhysPort, int>,
int> slaveMasks;
762 for (
const auto &group : slaveGroups) {
767 int mask[5] = {-1, -1, -1, -1, -1};
768 for (
auto port : group) {
769 int ID =
port.second;
770 for (
int i = 0; i < 5; i++) {
772 mask[i] = ID >> i & 0x1;
773 else if (mask[i] != (ID >> i & 0x1))
779 for (
int i = 4; i >= 0; i--) {
784 maskValue = (maskValue << 1) + mask[i];
786 for (
auto port : group)
787 slaveMasks[
port] = maskValue;
791 LLVM_DEBUG(llvm::dbgs() <<
"CHECK Slave Masks\n");
792 for (
auto map : slaveMasks) {
793 PhysPort
port = map.first.first;
794 TileOp tile = analyzer.getTile(builder,
port.first);
795 WireBundle bundle =
port.second.bundle;
797 int ID = map.first.second;
798 int mask = map.second;
800 LLVM_DEBUG(llvm::dbgs()
801 <<
"Port " << tile <<
" " << stringifyWireBundle(bundle) <<
" "
803 LLVM_DEBUG(llvm::dbgs() <<
"Mask "
804 <<
"0x" << llvm::Twine::utohexstr(mask) <<
'\n');
805 LLVM_DEBUG(llvm::dbgs() <<
"ID "
806 <<
"0x" << llvm::Twine::utohexstr(ID) <<
'\n');
807 for (
int i = 0; i < 31; i++) {
808 if ((i & mask) == (ID & mask))
809 LLVM_DEBUG(llvm::dbgs() <<
"matches flow ID "
810 <<
"0x" << llvm::Twine::utohexstr(i) <<
'\n');
819 for (
const auto &swMap : mastersets) {
820 TileID tileId = swMap.first.first;
821 TileOp tileOp = analyzer.getTile(builder, tileId);
822 if (std::none_of(tiles.begin(), tiles.end(),
824 Operation *> &tileMapEntry) {
825 return tileMapEntry.second == tileOp.getOperation();
827 tiles[{tileOp.colIndex(), tileOp.rowIndex()}] = tileOp;
831 for (
auto map : tiles) {
832 Operation *tileOp = map.second;
833 TileOp tile = cast<TileOp>(map.second);
834 TileID tileId = tile.getTileID();
837 builder.setInsertionPointAfter(tileOp);
839 analyzer.getSwitchbox(builder, tile.colIndex(), tile.rowIndex());
840 SwitchboxOp::ensureTerminator(swbox.getConnections(), builder,
841 builder.getUnknownLoc());
842 Block &b = swbox.getConnections().front();
843 builder.setInsertionPoint(b.getTerminator());
845 std::vector<bool> amselOpNeededVector(numMselsPerArbiter * numArbiters);
846 for (
const auto &map : mastersets) {
847 if (tileId != map.first.first)
850 for (
auto value : map.second) {
851 amselOpNeededVector[
value] =
true;
855 std::map<int, AMSelOp> amselOps;
856 for (
int i = 0; i < numMselsPerArbiter; i++) {
857 for (
int a = 0; a < numArbiters; a++) {
858 auto amselValue = getAmselFromArbiterIDAndMsel(a, i);
859 if (amselOpNeededVector[amselValue]) {
862 auto amsel = AMSelOp::create(builder, builder.getUnknownLoc(),
864 amselOps[amselValue] = amsel;
870 SmallVector<Port, 4> tileMasters;
871 for (
const auto &map : mastersets) {
872 if (tileId != map.first.first)
874 tileMasters.push_back(map.first.second);
877 std::sort(tileMasters.begin(), tileMasters.end());
878 for (
auto tileMaster : tileMasters) {
879 WireBundle bundle = tileMaster.bundle;
880 int channel = tileMaster.channel;
881 SmallVector<int, 4> msels = mastersets[{tileId, tileMaster}];
882 SmallVector<Value, 4> amsels;
883 for (
auto msel : msels) {
884 assert(amselOps.count(msel) == 1);
885 amsels.push_back(amselOps[msel]);
888 MasterSetOp::create(builder, builder.getUnknownLoc(),
889 builder.getIndexType(), bundle,
channel, amsels,
890 keepPktHeaderAttr[{tileId, tileMaster}]);
894 DenseMap<Port, PacketRulesOp> slaveRules;
895 for (
auto group : slaveGroups) {
896 builder.setInsertionPoint(b.getTerminator());
898 auto port = group.front().first;
899 if (tileId !=
port.first)
902 WireBundle bundle =
port.second.bundle;
904 auto slave =
port.second;
906 int mask = slaveMasks[group.front()];
907 int ID = group.front().second & mask;
911 for (
auto slave : group)
912 assert((slave.second & mask) == ID);
914 Value amsel = amselOps[slaveAMSels[group.front()]];
916 PacketRulesOp packetrules;
917 if (slaveRules.count(slave) == 0) {
918 packetrules = PacketRulesOp::create(builder, builder.getUnknownLoc(),
920 PacketRulesOp::ensureTerminator(packetrules.getRules(), builder,
921 builder.getUnknownLoc());
922 slaveRules[slave] = packetrules;
924 packetrules = slaveRules[slave];
926 Block &rules = packetrules.getRules().front();
929 for (
auto rule : rules.getOps<PacketRuleOp>()) {
930 auto verifyMask = rule.maskInt();
931 auto verifyValue = rule.valueInt();
932 if ((group.front().second & verifyMask) == verifyValue) {
933 rule->emitOpError(
"can lead to false packet id match for id ")
934 << ID <<
", which is not supposed to pass through this port.";
935 rule->emitRemark(
"Please consider changing all uses of packet id ")
936 << ID <<
" to avoid deadlock.";
941 builder.setInsertionPoint(rules.getTerminator());
942 PacketRuleOp::create(builder, builder.getUnknownLoc(), mask, ID, amsel);
952 for (
auto switchbox : make_early_inc_range(device.getOps<SwitchboxOp>())) {
953 auto retVal = switchbox->getOperand(0);
954 auto tileOp = retVal.getDefiningOp<TileOp>();
957 if (!tileOp.isShimNOCTile())
961 if (&switchbox.getBody()->front() == switchbox.getBody()->getTerminator())
964 Region &r = switchbox.getConnections();
965 Block &b = r.front();
970 for (
auto shimmux : device.getOps<ShimMuxOp>()) {
971 if (shimmux.getTile() == tileOp) {
978 for (Operation &Op : b.getOperations()) {
979 if (
auto pktrules = dyn_cast<PacketRulesOp>(Op)) {
982 if (pktrules.getSourceBundle() == WireBundle::DMA) {
987 builder.setInsertionPointAfter(tileOp);
988 shimOp = analyzer.getShimMux(builder, tileOp.colIndex());
992 Region &r0 = shimOp.getConnections();
993 Block &b0 = r0.front();
994 builder.setInsertionPointToStart(&b0);
996 pktrules.setSourceBundle(WireBundle::South);
997 if (pktrules.getSourceChannel() == 0) {
998 pktrules.setSourceChannel(3);
999 ConnectOp::create(builder, builder.getUnknownLoc(), WireBundle::DMA,
1000 0, WireBundle::North, 3);
1002 if (pktrules.getSourceChannel() == 1) {
1003 pktrules.setSourceChannel(7);
1004 ConnectOp::create(builder, builder.getUnknownLoc(), WireBundle::DMA,
1005 1, WireBundle::North, 7);
1010 if (
auto mtset = dyn_cast<MasterSetOp>(Op)) {
1013 if (mtset.getDestBundle() == WireBundle::DMA) {
1018 builder.setInsertionPointAfter(tileOp);
1019 shimOp = analyzer.getShimMux(builder, tileOp.colIndex());
1023 Region &r0 = shimOp.getConnections();
1024 Block &b0 = r0.front();
1025 builder.setInsertionPointToStart(&b0);
1027 mtset.setDestBundle(WireBundle::South);
1028 if (mtset.getDestChannel() == 0) {
1029 mtset.setDestChannel(2);
1030 ConnectOp::create(builder, builder.getUnknownLoc(),
1031 WireBundle::North, 2, WireBundle::DMA, 0);
1033 if (mtset.getDestChannel() == 1) {
1034 mtset.setDestChannel(3);
1035 ConnectOp::create(builder, builder.getUnknownLoc(),
1036 WireBundle::North, 3, WireBundle::DMA, 1);
1043 RewritePatternSet patterns(&getContext());
1045 if (failed(applyPartialConversion(device, target, std::move(patterns))))