/**
* ****************************************************************************
* 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.GeneralCacheControllerLineReplacementEvent;
import archimulator.uncore.coherence.event.GeneralCacheControllerNonblockingRequestHitToTransientTagEvent;
import archimulator.uncore.coherence.event.GeneralCacheControllerServiceNonblockingRequestEvent;
import archimulator.uncore.coherence.msi.controller.CacheController;
import archimulator.uncore.coherence.msi.controller.Controller;
import archimulator.uncore.coherence.msi.controller.DirectoryController;
import archimulator.uncore.coherence.msi.event.cache.*;
import archimulator.uncore.coherence.msi.flow.CacheCoherenceFlow;
import archimulator.uncore.coherence.msi.flow.LoadFlow;
import archimulator.uncore.coherence.msi.flow.StoreFlow;
import archimulator.uncore.coherence.msi.message.*;
import archimulator.uncore.coherence.msi.state.CacheControllerState;
import archimulator.util.ValueProvider;
import archimulator.util.fsm.BasicFiniteStateMachine;
import archimulator.util.fsm.event.ExitStateEvent;
import java.util.ArrayList;
import java.util.List;
/**
* L1 cache controller finite state machine.
*
* @author Min Cai
*/
public class CacheControllerFiniteStateMachine extends BasicFiniteStateMachine<CacheControllerState, CacheControllerEventType> implements ValueProvider<CacheControllerState> {
private CacheController cacheController;
private CacheControllerState previousState;
private int set;
private int way;
private int numInvAcks;
private List<Runnable> stalledEvents = new ArrayList<>();
private Runnable onCompletedCallback;
/**
* Create an L1 cache controller finite state machine.
*
* @param name the name
* @param set the set index
* @param way the way
* @param cacheController the L1 cache controller
*/
public CacheControllerFiniteStateMachine(String name, int set, int way, final CacheController cacheController) {
super(name, CacheControllerState.I);
this.set = set;
this.way = way;
this.cacheController = cacheController;
this.addListener(ExitStateEvent.class, exitStateEvent -> previousState = getState());
}
/**
* Act on a "load" event.
*
* @param producerFlow the producer cache coherence flow
* @param tag the tag
* @param onCompletedCallback the callback action performed when the event is completed
* @param onStalledCallback the callback action performed when the event is stalled
*/
public void onEventLoad(LoadFlow producerFlow, int tag, Runnable onCompletedCallback, Runnable onStalledCallback) {
LoadEvent loadEvent = new LoadEvent(cacheController, producerFlow, tag, set, way, onCompletedCallback, onStalledCallback, producerFlow.getAccess());
this.fireTransition(producerFlow.getAccess().getThread().getName() + "." + String.format("0x%08x", tag), loadEvent);
}
/**
* Act on a "store" event.
*
* @param producerFlow the producer cache coherence flow
* @param tag the tag
* @param onCompletedCallback the callback action performed when the event is completed
* @param onStalledCallback the callback action performed when the event is stalled
*/
public void onEventStore(StoreFlow producerFlow, int tag, Runnable onCompletedCallback, Runnable onStalledCallback) {
StoreEvent storeEvent = new StoreEvent(cacheController, producerFlow, tag, set, way, onCompletedCallback, onStalledCallback, producerFlow.getAccess());
this.fireTransition(producerFlow.getAccess().getThread().getName() + "." + String.format("0x%08x", tag), storeEvent);
}
/**
* Act on a "replacement" event.
*
* @param producerFlow the producer cache coherence flow
* @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, int tag, CacheAccess<CacheControllerState> cacheAccess, Runnable onCompletedCallback, Runnable onStalledCallback) {
ReplacementEvent replacementEvent = new ReplacementEvent(cacheController, producerFlow, tag, cacheAccess, set, way, onCompletedCallback, onStalledCallback, producerFlow.getAccess());
this.fireTransition(producerFlow.getAccess().getThread().getName() + "." + String.format("0x%08x", tag), replacementEvent);
}
/**
* Act on a "forwarded GetS" event.
*
* @param producerFlow the producer cache coherence flow
* @param requester the requester L1 cache controller
* @param tag the tag
*/
public void onEventFwdGetS(CacheCoherenceFlow producerFlow, CacheController requester, int tag) {
FwdGetSEvent fwdGetSEvent = new FwdGetSEvent(cacheController, producerFlow, requester, tag, producerFlow.getAccess());
this.fireTransition(requester + "." + String.format("0x%08x", tag), fwdGetSEvent);
}
/**
* Act on a "forwarded GetM" event.
*
* @param producerFlow the producer cache coherence flow
* @param requester the requester L1 cache controller
* @param tag the tag
*/
public void onEventFwdGetM(CacheCoherenceFlow producerFlow, CacheController requester, int tag) {
FwdGetMEvent fwdGetMEvent = new FwdGetMEvent(cacheController, producerFlow, requester, tag, producerFlow.getAccess());
this.fireTransition(requester + "." + String.format("0x%08x", tag), fwdGetMEvent);
}
/**
* Act on an "invalidation" event.
*
* @param producerFlow the producer cache coherence flow
* @param requester the requester L1 cache controller
* @param tag the tag
*/
public void onEventInv(CacheCoherenceFlow producerFlow, CacheController requester, int tag) {
InvEvent invEvent = new InvEvent(cacheController, producerFlow, requester, tag, producerFlow.getAccess());
this.fireTransition(requester + "." + String.format("0x%08x", tag), invEvent);
}
/**
* Act on a "recall" event.
*
* @param producerFlow the producer cache coherence flow
* @param tag the tag
*/
public void onEventRecall(CacheCoherenceFlow producerFlow, int tag) {
RecallEvent recallEvent = new RecallEvent(cacheController, producerFlow, tag, producerFlow.getAccess());
this.fireTransition("<dir>" + "." + String.format("0x%08x", tag), recallEvent);
}
/**
* Act on a "put acknowledgement" event.
*
* @param producerFlow the producer cache coherence flow
* @param tag the tag
*/
public void onEventPutAck(CacheCoherenceFlow producerFlow, int tag) {
PutAckEvent putAckEvent = new PutAckEvent(cacheController, producerFlow, tag, producerFlow.getAccess());
this.fireTransition(cacheController.getDirectoryController() + "." + String.format("0x%08x", tag), putAckEvent);
}
/**
* Act on a "data" event.
*
* @param producerFlow the producer cache coherence flow
* @param sender the sender controller
* @param tag the tag
* @param numInvalidationAcknowledgements
* the number of pending invalidation acknowledgements expected
*/
public void onEventData(CacheCoherenceFlow producerFlow, Controller sender, int tag, int numInvalidationAcknowledgements) {
this.numInvAcks += numInvalidationAcknowledgements;
if (sender instanceof DirectoryController) {
if (numInvalidationAcknowledgements == 0) {
DataFromDirAcksEq0Event dataFromDirAcksEq0Event = new DataFromDirAcksEq0Event(cacheController, producerFlow, sender, tag, producerFlow.getAccess());
this.fireTransition(sender + "." + String.format("0x%08x", tag), dataFromDirAcksEq0Event);
} else {
DataFromDirAcksGt0Event dataFromDirAcksGt0Event = new DataFromDirAcksGt0Event(cacheController, producerFlow, sender, tag, producerFlow.getAccess());
this.fireTransition(sender + "." + String.format("0x%08x", tag), dataFromDirAcksGt0Event);
if (this.numInvAcks == 0) {
onEventLastInvAck(producerFlow, tag);
}
}
} else {
DataFromOwnerEvent dataFromOwnerEvent = new DataFromOwnerEvent(cacheController, producerFlow, sender, tag, producerFlow.getAccess());
this.fireTransition(sender + "." + String.format("0x%08x", tag), dataFromOwnerEvent);
}
}
/**
* Act on an "invalidation acknowledgement" event.
*
* @param producerFlow the producer cache coherence flow
* @param sender the sender L1 cache controller
* @param tag the tag
*/
public void onEventInvAck(CacheCoherenceFlow producerFlow, CacheController sender, int tag) {
InvAckEvent invAckEvent = new InvAckEvent(cacheController, producerFlow, sender, tag, producerFlow.getAccess());
this.fireTransition(sender + "." + String.format("0x%08x", tag), invAckEvent);
if (this.numInvAcks == 0) {
onEventLastInvAck(producerFlow, tag);
}
}
/**
* Act on a "last invalidation acknowledgement" event.
*
* @param producerFlow the producer cache coherence flow
* @param tag the tag
*/
private void onEventLastInvAck(CacheCoherenceFlow producerFlow, int tag) {
LastInvAckEvent lastInvAckEvent = new LastInvAckEvent(cacheController, producerFlow, tag, producerFlow.getAccess());
this.fireTransition("<N/A>" + "." + String.format("0x%08x", tag), lastInvAckEvent);
this.numInvAcks = 0;
}
/**
* Fire the predefined transition based on the specified sender and event.
*
* @param sender the sender
* @param event the event
*/
public void fireTransition(Object sender, CacheControllerEvent event) {
event.onCompleted();
cacheController.getFsmFactory().fireTransition(this, sender, event.getType(), event);
}
/**
* Send a "GetS" message to the directory controller.
*
* @param producerFlow the producer cache coherence flow
* @param tag the tag
*/
public void sendGetSToDir(CacheCoherenceFlow producerFlow, int tag) {
cacheController.transfer(cacheController.getDirectoryController(), 8, new GetSMessage(cacheController, producerFlow, cacheController, tag, producerFlow.getAccess()));
}
/**
* Send a "GetM" message to the directory controller.
*
* @param producerFlow the producer cache coherence flow
* @param tag the tag
*/
public void sendGetMToDir(CacheCoherenceFlow producerFlow, int tag) {
cacheController.transfer(cacheController.getDirectoryController(), 8, new GetMMessage(cacheController, producerFlow, cacheController, tag, producerFlow.getAccess()));
}
/**
* Send a "PutS" message to the directory controller.
*
* @param producerFlow the producer cache coherence flow
* @param tag the tag
*/
public void sendPutSToDir(CacheCoherenceFlow producerFlow, int tag) {
cacheController.transfer(cacheController.getDirectoryController(), 8, new PutSMessage(cacheController, producerFlow, cacheController, tag, producerFlow.getAccess()));
}
/**
* Send a "PutM and data" message to the directory controller.
*
* @param producerFlow the producer cache coherence flow
* @param tag the tag
*/
public void sendPutMAndDataToDir(CacheCoherenceFlow producerFlow, int tag) {
cacheController.transfer(cacheController.getDirectoryController(), cacheController.getCache().getLineSize() + 8, new PutMAndDataMessage(cacheController, producerFlow, cacheController, tag, producerFlow.getAccess()));
}
/**
* Send a "data" message to the requester L1 cache controller and the directory controller.
*
* @param producerFlow the producer cache coherence flow
* @param requester the requester L1 cache controller
* @param tag the tag
*/
public void sendDataToRequesterAndDir(CacheCoherenceFlow producerFlow, CacheController requester, int tag) {
cacheController.transfer(requester, 10, new DataMessage(cacheController, producerFlow, cacheController, tag, 0, producerFlow.getAccess()));
cacheController.transfer(cacheController.getDirectoryController(), cacheController.getCache().getLineSize() + 8, new DataMessage(cacheController, producerFlow, cacheController, tag, 0, producerFlow.getAccess()));
}
/**
* 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
*/
public void sendDataToRequester(CacheCoherenceFlow producerFlow, CacheController requester, int tag) {
cacheController.transfer(requester, cacheController.getCache().getLineSize() + 8, new DataMessage(cacheController, producerFlow, cacheController, tag, 0, producerFlow.getAccess()));
}
/**
* Send an "invalidation 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 sendInvAckToRequester(CacheCoherenceFlow producerFlow, CacheController requester, int tag) {
cacheController.transfer(requester, 8, new InvAckMessage(cacheController, producerFlow, cacheController, tag, producerFlow.getAccess()));
}
/**
* Send a "recall acknowledgement" message to the directory controller.
*
* @param producerFlow the producer cache coherence flow
* @param tag the tag
* @param size the size of the message
*/
public void sendRecallAckToDir(CacheCoherenceFlow producerFlow, int tag, int size) {
cacheController.transfer(cacheController.getDirectoryController(), size, new RecallAckMessage(cacheController, producerFlow, cacheController, tag, producerFlow.getAccess()));
}
/**
* Decrement the number of invalidation acknowledgements.
*/
public void decrementInvAcks() {
this.numInvAcks--;
}
/**
* Act on a cache hit.
*
* @param access the memory hierarchy access
* @param tag the tag
* @param set the set index
* @param way the way
*/
public void hit(MemoryHierarchyAccess access, int tag, int set, int way) {
this.fireServiceNonblockingRequestEvent(access, tag, true);
this.cacheController.getCache().getReplacementPolicy().handlePromotionOnHit(access, set, way);
this.getLine().setAccess(access);
}
/**
* Act on a stall.
*
* @param sender the sender object
* @param event the L1 cache controller event
*/
public void stall(final Object sender, final CacheControllerEvent 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.getCacheController().getBlockingEventDispatcher().dispatch(new GeneralCacheControllerServiceNonblockingRequestEvent(this.getCacheController(), access, tag, getSet(), getWay(), hitInCache));
this.getCacheController().updateStats(access.getType().isRead(), hitInCache);
}
/**
* Fire a "replacement" event.
*
* @param access the memory hierarchy access
* @param tag the tag
*/
public void fireReplacementEvent(MemoryHierarchyAccess access, int tag) {
this.getCacheController().getBlockingEventDispatcher().dispatch(new GeneralCacheControllerLineReplacementEvent(this.getCacheController(), access, tag, getSet(), getWay()));
}
/**
* Fire a "nonblocking request hit to transient tag" event.
*
* @param access the memory hierarchy access
* @param tag the tag
*/
public void fireNonblockingRequestHitToTransientTagEvent(MemoryHierarchyAccess access, int tag) {
this.getCacheController().getBlockingEventDispatcher().dispatch(new GeneralCacheControllerNonblockingRequestHitToTransientTagEvent(this.getCacheController(), access, tag, getSet(), getWay()));
}
/**
* Get the state of the owning L1 cache controller.
*
* @return the state of the owning L1 cache controller
*/
@Override
public CacheControllerState get() {
return this.getState();
}
/**
* Get the initial state of the owning L1 cache controller.
*
* @return the initial state of the owning L1 cache controller
*/
@Override
public CacheControllerState getInitialValue() {
return CacheControllerState.I;
}
/**
* Get the line in the owning L1 cache controller.
*
* @return the line in the owning L1 cache controller
*/
public CacheLine<CacheControllerState> getLine() {
return this.cacheController.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 L1 cache controller.
*
* @return the owning L1 cache controller
*/
public CacheController getCacheController() {
return cacheController;
}
/**
* Get the list of stalled events.
*
* @return the list of stalled events
*/
public List<Runnable> getStalledEvents() {
return stalledEvents;
}
/**
* Get the previous state of the line in the owning L1 cache controller.
*
* @return the previous state of the line in the owning L1 cache controller
*/
public CacheControllerState getPreviousState() {
return previousState;
}
/**
* 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;
}
}