/** * **************************************************************************** * Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com). * <p> * This file is part of the Archimulator multicore architectural simulator. * <p> * Archimulator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * <p> * Archimulator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * <p> * You should have received a copy of the GNU General Public License * along with Archimulator. If not, see <http://www.gnu.org/licenses/>. * **************************************************************************** */ package archimulator.uncore.coherence.msi.fsm; import archimulator.uncore.MemoryHierarchyAccess; import archimulator.uncore.cache.CacheAccess; import archimulator.uncore.cache.CacheLine; import archimulator.uncore.coherence.event.*; import archimulator.uncore.coherence.msi.controller.CacheController; import archimulator.uncore.coherence.msi.controller.DirectoryController; import archimulator.uncore.coherence.msi.controller.DirectoryEntry; import archimulator.uncore.coherence.msi.event.directory.*; import archimulator.uncore.coherence.msi.flow.CacheCoherenceFlow; import archimulator.uncore.coherence.msi.message.*; import archimulator.uncore.coherence.msi.state.DirectoryControllerState; import archimulator.util.ValueProvider; import archimulator.util.fsm.BasicFiniteStateMachine; import archimulator.util.fsm.event.ExitStateEvent; import java.util.ArrayList; import java.util.List; /** * Directory controller finite state machine. * * @author Min Cai */ public class DirectoryControllerFiniteStateMachine extends BasicFiniteStateMachine<DirectoryControllerState, DirectoryControllerEventType> implements ValueProvider<DirectoryControllerState> { private DirectoryController directoryController; private DirectoryEntry directoryEntry; private DirectoryControllerState previousState; private int set; private int way; private int numRecallAcks; private List<Runnable> stalledEvents; private Runnable onCompletedCallback; private int evicterTag; private int victimTag; /** * Create a directory controller finite state machine. * * @param name the name * @param set the set index * @param way the way * @param directoryController the directory controller */ public DirectoryControllerFiniteStateMachine(String name, int set, int way, final DirectoryController directoryController) { super(name, DirectoryControllerState.I); this.set = set; this.way = way; this.directoryController = directoryController; this.directoryEntry = new DirectoryEntry(); this.stalledEvents = new ArrayList<>(); this.evicterTag = CacheLine.INVALID_TAG; this.victimTag = CacheLine.INVALID_TAG; this.addListener(ExitStateEvent.class, exitStateEvent -> previousState = getState()); } /** * Act on a "GetS" event. * * @param producerFlow the producer cache coherence flow * @param requester the requester L1 cache controller * @param tag the tag * @param onStalledCallback the callback action performed when the event is stalled */ public void onEventGetS(CacheCoherenceFlow producerFlow, CacheController requester, int tag, Runnable onStalledCallback) { GetSEvent getSEvent = new GetSEvent(this.directoryController, producerFlow, requester, tag, set, way, onStalledCallback, producerFlow.getAccess()); this.fireTransition(requester + "." + String.format("0x%08x", tag), getSEvent); } /** * Act on a "GetM" event. * * @param producerFlow the producer cache coherence flow * @param requester the requester L1 cache controller * @param tag the tag * @param onStalledCallback the callback action performed when the event is stalled */ public void onEventGetM(CacheCoherenceFlow producerFlow, CacheController requester, int tag, Runnable onStalledCallback) { GetMEvent getMEvent = new GetMEvent(this.directoryController, producerFlow, requester, tag, set, way, onStalledCallback, producerFlow.getAccess()); this.fireTransition(requester + "." + String.format("0x%08x", tag), getMEvent); } /** * Act on a "replacement" event. * * @param producerFlow the producer cache coherence flow * @param requester the requester L1 cache controller * @param tag the tag * @param cacheAccess the cache access * @param onCompletedCallback the callback action performed when the replacement is completed * @param onStalledCallback the callback action performed when the replacement is stalled */ public void onEventReplacement(CacheCoherenceFlow producerFlow, CacheController requester, int tag, CacheAccess<DirectoryControllerState> cacheAccess, Runnable onCompletedCallback, Runnable onStalledCallback) { ReplacementEvent replacementEvent = new ReplacementEvent(this.directoryController, producerFlow, tag, cacheAccess, set, way, onCompletedCallback, onStalledCallback, producerFlow.getAccess()); this.fireTransition(requester + "." + String.format("0x%08x", tag), replacementEvent); } /** * Act on a "recall acknowledgement" event. * * @param producerFlow the producer cache coherence flow * @param sender the sender L1 cache controller * @param tag the tag */ public void onEventRecallAck(CacheCoherenceFlow producerFlow, CacheController sender, int tag) { RecallAckEvent recallAckEvent = new RecallAckEvent(this.directoryController, producerFlow, sender, tag, producerFlow.getAccess()); this.fireTransition(sender + "." + String.format("0x%08x", tag), recallAckEvent); if (this.numRecallAcks == 0) { LastRecallAckEvent lastRecallAckEvent = new LastRecallAckEvent(this.directoryController, producerFlow, tag, producerFlow.getAccess()); this.fireTransition(sender + "." + String.format("0x%08x", tag), lastRecallAckEvent); } } /** * Act on a "PutS" event. * * @param producerFlow the producer cache coherence flow * @param requester the requester L1 cache controller * @param tag the tag */ public void onEventPutS(CacheCoherenceFlow producerFlow, CacheController requester, int tag) { if (this.getDirectoryEntry().getSharers().size() > 1) { PutSNotLastEvent putSNotLastEvent = new PutSNotLastEvent(this.directoryController, producerFlow, requester, tag, producerFlow.getAccess()); this.fireTransition(requester + "." + String.format("0x%08x", tag), putSNotLastEvent); } else { PutSLastEvent putSLastEvent = new PutSLastEvent(this.directoryController, producerFlow, requester, tag, producerFlow.getAccess()); this.fireTransition(requester + "." + String.format("0x%08x", tag), putSLastEvent); } } /** * Act on a "PutM and data" event. * * @param producerFlow the producer cache coherence flow * @param requester the requester L1 cache controller * @param tag the tag */ public void onEventPutMAndData(CacheCoherenceFlow producerFlow, CacheController requester, int tag) { if (requester == this.getDirectoryEntry().getOwner()) { PutMAndDataFromOwnerEvent putMAndDataFromOwnerEvent = new PutMAndDataFromOwnerEvent(this.directoryController, producerFlow, requester, tag, producerFlow.getAccess()); this.fireTransition(requester + "." + String.format("0x%08x", tag), putMAndDataFromOwnerEvent); } else { PutMAndDataFromNonOwnerEvent putMAndDataFromNonOwnerEvent = new PutMAndDataFromNonOwnerEvent(this.directoryController, producerFlow, requester, tag, producerFlow.getAccess()); this.fireTransition(requester + "." + String.format("0x%08x", tag), putMAndDataFromNonOwnerEvent); } } /** * Act on a "data" event. * * @param producerFlow the producer cache coherence flow * @param sender the sender L1 cache controller * @param tag the tag */ public void onEventData(CacheCoherenceFlow producerFlow, CacheController sender, int tag) { DataEvent dataEvent = new DataEvent(this.directoryController, producerFlow, sender, tag, producerFlow.getAccess()); this.fireTransition(sender + "." + String.format("0x%08x", tag), dataEvent); } /** * Fire the predefined action based on the specified directory controller event. * * @param sender the sender object * @param event the directory controller event */ public void fireTransition(Object sender, DirectoryControllerEvent event) { event.onCompleted(); this.directoryController.getFsmFactory().fireTransition(this, sender, event.getType(), event); } /** * Act on a cache hit. * * @param access the memory hierarchy access * @param tag the tag * @param set the set * @param way the way */ public void hit(MemoryHierarchyAccess access, int tag, int set, int way) { this.fireServiceNonblockingRequestEvent(access, tag, true); this.directoryController.getCache().getReplacementPolicy().handlePromotionOnHit(access, set, way); this.getLine().setAccess(access); } /** * Act on a stall. * * @param sender the sender object * @param event the directory controller event */ public void stall(final Object sender, final DirectoryControllerEvent event) { stall(() -> fireTransition(sender, event)); } /** * Act on a stall. * * @param action the callback action performed when the stall is awaken */ public void stall(Runnable action) { stalledEvents.add(action); } /** * Fire a "service non-blocking request" event. * * @param access the memory hierarchy access * @param tag the tag * @param hitInCache a value indicating whether the access hits in the cache or not */ public void fireServiceNonblockingRequestEvent(MemoryHierarchyAccess access, int tag, boolean hitInCache) { this.getDirectoryController().getBlockingEventDispatcher().dispatch(new GeneralCacheControllerServiceNonblockingRequestEvent(this.getDirectoryController(), access, tag, getSet(), getWay(), hitInCache)); this.getDirectoryController().updateStats(access.getType().isRead(), hitInCache); } /** * Fire a "cache line inserted" event. * * @param access the memory hierarchy access * @param tag the tag * @param victimTag the victim tag */ public void fireCacheLineInsertEvent(MemoryHierarchyAccess access, int tag, int victimTag) { this.getDirectoryController().getBlockingEventDispatcher().dispatch(new LastLevelCacheControllerLineInsertEvent(this.getDirectoryController(), access, tag, getSet(), getWay(), victimTag)); } /** * Fire a "replacement" event. * * @param access the memory hierarchy access * @param tag the tag */ public void fireReplacementEvent(MemoryHierarchyAccess access, int tag) { this.getDirectoryController().getBlockingEventDispatcher().dispatch(new GeneralCacheControllerLineReplacementEvent(this.getDirectoryController(), access, tag, getSet(), getWay())); } /** * Fire a "PutS or PutM and data from the owner" event. * * @param access the memory hierarchy access * @param tag the tag */ public void firePutSOrPutMAndDataFromOwnerEvent(MemoryHierarchyAccess access, int tag) { this.getDirectoryController().getBlockingEventDispatcher().dispatch(new GeneralCacheControllerLastPutSOrPutMAndDataFromOwnerEvent(this.getDirectoryController(), access, tag, getSet(), getWay())); } /** * Fire a "non-blocking request hit to transient tag" event. * * @param access the memory hierarchy access * @param tag the tag */ public void fireNonblockingRequestHitToTransientTagEvent(MemoryHierarchyAccess access, int tag) { this.getDirectoryController().getBlockingEventDispatcher().dispatch(new GeneralCacheControllerNonblockingRequestHitToTransientTagEvent(this.getDirectoryController(), access, tag, getSet(), getWay())); } /** * Send a "data" message to the requester L1 cache controller. * * @param producerFlow the producer cache coherence flow * @param requester the requester L1 cache controller * @param tag the tag * @param numInvalidationAcknowledgements * the number of pending invalidation acknowledgements expected */ public void sendDataToRequester(CacheCoherenceFlow producerFlow, CacheController requester, int tag, int numInvalidationAcknowledgements) { this.directoryController.transfer(requester, this.directoryController.getCache().getLineSize() + 8, new DataMessage(this.directoryController, producerFlow, this.directoryController, tag, numInvalidationAcknowledgements, producerFlow.getAccess())); } /** * Send a "Put acknowledgement" message to the requester L1 cache controller. * * @param producerFlow the producer cache coherence flow * @param requester the requester L1 cache controller * @param tag the tag */ public void sendPutAckToReq(CacheCoherenceFlow producerFlow, CacheController requester, int tag) { sendPutAckToReq(producerFlow, this.directoryController, requester, tag); } /** * Send a "Put acknowledgement" message to the requester L1 cache controller. * * @param producerFlow the producer cache coherence flow * @param directoryController the directory controller * @param requester the requester L1 cache controller * @param tag the tag */ public static void sendPutAckToReq(CacheCoherenceFlow producerFlow, DirectoryController directoryController, CacheController requester, int tag) { directoryController.transfer(requester, 8, new PutAckMessage(directoryController, producerFlow, tag, producerFlow.getAccess())); } /** * Send the data to the memory controller. * * @param tag the tag */ public void copyDataToMem(int tag) { this.directoryController.getNext().memWriteRequestReceive(this.directoryController, tag, () -> { }); } /** * Send a "forwarded GetS" message to the owner L1 cache controller. * * @param producerFlow the producer cache coherence flow * @param requester the requester L1 cache controller * @param tag the tag */ public void sendFwdGetSToOwner(CacheCoherenceFlow producerFlow, CacheController requester, int tag) { this.directoryController.transfer(getDirectoryEntry().getOwner(), 8, new FwdGetSMessage(this.directoryController, producerFlow, requester, tag, producerFlow.getAccess())); } /** * Send a "forwarded GetM" message to the owner L1 cache controller. * * @param producerFlow the producer cache coherence flow * @param requester the requester L1 cache controller * @param tag the tag */ public void sendFwdGetMToOwner(CacheCoherenceFlow producerFlow, CacheController requester, int tag) { this.directoryController.transfer(getDirectoryEntry().getOwner(), 8, new FwdGetMMessage(this.directoryController, producerFlow, requester, tag, producerFlow.getAccess())); } /** * Send "invalidation" messages to the L1 sharers. * * @param producerFlow the producer cache coherence flow * @param requester the requester L1 cache controller * @param tag the tag */ public void sendInvToSharers(CacheCoherenceFlow producerFlow, CacheController requester, int tag) { this.getDirectoryEntry().getSharers().stream().filter(sharer -> requester != sharer).forEach(sharer -> this.directoryController.transfer(sharer, 8, new InvMessage(this.directoryController, producerFlow, requester, tag, producerFlow.getAccess()))); } /** * Send a "recall" message to the owner L1 cache controller. * * @param producerFlow the producer cache coherence flow * @param tag the tag */ public void sendRecallToOwner(CacheCoherenceFlow producerFlow, int tag) { CacheController owner = getDirectoryEntry().getOwner(); if (owner.getCache().findWay(tag) == -1) { throw new IllegalArgumentException(); } this.directoryController.transfer(owner, 8, new RecallMessage(this.directoryController, producerFlow, tag, producerFlow.getAccess())); } /** * Send "recall" messages to the L1 sharers. * * @param producerFlow the producer cache coherence flow * @param tag the tag */ public void sendRecallToSharers(CacheCoherenceFlow producerFlow, int tag) { for (CacheController sharer : this.getDirectoryEntry().getSharers()) { if (sharer.getCache().findWay(tag) == -1) { throw new IllegalArgumentException(); } this.directoryController.transfer(sharer, 8, new RecallMessage(this.directoryController, producerFlow, tag, producerFlow.getAccess())); } } /** * Set the number of recall acknowledgements. * * @param numRecallAcks the number of recall acknowledgements */ public void setNumRecallAcks(int numRecallAcks) { this.numRecallAcks = numRecallAcks; } /** * Decrement the number of recall acknowledgements. */ public void decrementRecallAcks() { this.numRecallAcks--; } /** * Add the requester L1 cache controller and the owner L1 cache controller to the list of sharers. * * @param requester the requester L1 cache controller */ public void addRequesterAndOwnerToSharers(CacheController requester) { if (this.getDirectoryEntry().getSharers().contains(requester) || this.getDirectoryEntry().getSharers().contains(this.getDirectoryEntry().getOwner())) { throw new IllegalArgumentException(); } this.getDirectoryEntry().getSharers().add(requester); this.getDirectoryEntry().getSharers().add(this.getDirectoryEntry().getOwner()); } /** * Add the requester L1 cache controller to the list of sharers. * * @param requester the requester L1 cache controller */ public void addRequesterToSharers(CacheController requester) { if (this.getDirectoryEntry().getSharers().contains(requester)) { throw new IllegalArgumentException(); } this.getDirectoryEntry().getSharers().add(requester); } /** * Remove the requester L1 cache controller from the list of sharers. * * @param requester the requester L1 cache controller */ public void removeRequesterFromSharers(CacheController requester) { if (!this.getDirectoryEntry().getSharers().contains(requester)) { throw new IllegalArgumentException(); } this.getDirectoryEntry().getSharers().remove(requester); } /** * Set the owner to the requester L1 cache controller. * * @param requester the requester L1 cache controller */ public void setOwnerToRequester(CacheController requester) { this.getDirectoryEntry().setOwner(requester); } /** * Clear the list of sharers. */ public void clearSharers() { this.getDirectoryEntry().getSharers().clear(); } /** * Clear the owner L1 cache controller. */ public void clearOwner() { this.getDirectoryEntry().setOwner(null); } /** * Get the state of the owning directory controller. * * @return the state of the owning directory controller */ @Override public DirectoryControllerState get() { return this.getState(); } /** * Get the initial state of the owning directory controller. * * @return the initial state of the owning directory controller */ @Override public DirectoryControllerState getInitialValue() { return DirectoryControllerState.I; } /** * Get the directory entry. * * @return the directory entry */ public DirectoryEntry getDirectoryEntry() { return directoryEntry; } /** * Get the line in the owning directory controller. * * @return the line in the owning directory controller */ public CacheLine<DirectoryControllerState> getLine() { return this.directoryController.getCache().getLine(this.getSet(), this.getWay()); } /** * Get the set index. * * @return the set index */ public int getSet() { return set; } /** * Get the way. * * @return the way */ public int getWay() { return way; } /** * Get the owning directory controller. * * @return the owning directory controller */ public DirectoryController getDirectoryController() { return directoryController; } /** * Get the evicter tag. * * @return the evicter tag */ public int getEvicterTag() { return evicterTag; } /** * Set the evicter tag. * * @param evicterTag the evicter tag */ public void setEvicterTag(int evicterTag) { this.evicterTag = evicterTag; } /** * Set the victim tag. * * @return the victim tag */ public int getVictimTag() { return victimTag; } /** * Set the victim tag. * * @param victimTag the victim tag */ public void setVictimTag(int victimTag) { this.victimTag = victimTag; } /** * Get the previous state of the line in the owning directory controller. * * @return the previous state of the line in the owning directory controller */ public DirectoryControllerState getPreviousState() { return previousState; } /** * Get the list of stalled events. * * @return the list of stalled events */ public List<Runnable> getStalledEvents() { return stalledEvents; } /** * Get the callback action performed when the pending event is completed. * * @return the callback action performed when the pending event is completed */ public Runnable getOnCompletedCallback() { return onCompletedCallback; } /** * Set the callback action performed when the pending event is completed. * * @param onCompletedCallback the callback action performed when the pending event is completed */ public void setOnCompletedCallback(Runnable onCompletedCallback) { if (this.onCompletedCallback != null && onCompletedCallback != null) { throw new IllegalArgumentException(); } this.onCompletedCallback = onCompletedCallback; } }