/**
* ****************************************************************************
* 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.controller;
import archimulator.core.DynamicInstruction;
import archimulator.core.MemoryHierarchyCore;
import archimulator.core.Thread;
import archimulator.uncore.MemoryDevice;
import archimulator.uncore.MemoryHierarchy;
import archimulator.uncore.MemoryHierarchyAccess;
import archimulator.uncore.MemoryHierarchyAccessType;
import archimulator.uncore.cache.*;
import archimulator.uncore.cache.replacement.CacheReplacementPolicyType;
import archimulator.uncore.coherence.msi.event.cache.CacheControllerEventType;
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.fsm.CacheControllerFiniteStateMachine;
import archimulator.uncore.coherence.msi.fsm.CacheControllerFiniteStateMachineFactory;
import archimulator.uncore.coherence.msi.message.*;
import archimulator.uncore.coherence.msi.state.CacheControllerState;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
/**
* Cache controller.
*
* @author Min Cai
*/
public abstract class CacheController extends GeneralCacheController<CacheControllerState, CacheControllerEventType> {
private MemoryHierarchyCore core;
private EvictableCache<CacheControllerState> cache;
private Map<Integer, MemoryHierarchyAccess> pendingAccesses;
private EnumMap<MemoryHierarchyAccessType, Integer> pendingAccessesPerType;
private CacheControllerFiniteStateMachineFactory fsmFactory;
/**
* Create a cache controller.
*
* @param memoryHierarchy the memory hierarchy
* @param name the name
* @param type the type
*/
public CacheController(MemoryHierarchy memoryHierarchy, final String name, MemoryDeviceType type) {
super(memoryHierarchy, name, type);
this.cache = new BasicEvictableCache<>(memoryHierarchy, name, getGeometry(), getReplacementPolicyType(), args -> {
int set = (Integer) args[0];
int way = (Integer) args[1];
return new CacheControllerFiniteStateMachine(name, set, way, this);
});
this.pendingAccesses = new HashMap<>();
this.pendingAccessesPerType = new EnumMap<>(MemoryHierarchyAccessType.class);
this.pendingAccessesPerType.put(MemoryHierarchyAccessType.IFETCH, 0);
this.pendingAccessesPerType.put(MemoryHierarchyAccessType.LOAD, 0);
this.pendingAccessesPerType.put(MemoryHierarchyAccessType.STORE, 0);
this.fsmFactory = CacheControllerFiniteStateMachineFactory.getSingleton();
}
/**
* Get a value indicating whether the specified memory hierarchy access can be performed now.
*
* @param type the type of the memory hierarchy access
* @param physicalTag the physical tag
* @return a value indicating whether the specified memory hierarchy access can be performed now
*/
public boolean canAccess(MemoryHierarchyAccessType type, int physicalTag) {
MemoryHierarchyAccess access = this.findAccess(physicalTag);
return access == null ?
this.pendingAccessesPerType.get(type) < (type == MemoryHierarchyAccessType.STORE ? this.getNumWritePorts() : this.getNumReadPorts()) :
type != MemoryHierarchyAccessType.STORE && access.getType() != MemoryHierarchyAccessType.STORE;
}
/**
* Find the pending memory hierarchy access matching the specified physical tag.
*
* @param physicalTag the physical tag
* @return the pending memory hierarchy access matching the specified physical tag if any exists; otherwise null
*/
public MemoryHierarchyAccess findAccess(int physicalTag) {
return this.pendingAccesses.containsKey(physicalTag) ? this.pendingAccesses.get(physicalTag) : null;
}
/**
* Begin a memory hierarchy access.
*
* @param dynamicInstruction the dynamic instruction
* @param thread the thread
* @param type the type of the memory hierarchy access
* @param virtualPc the virtual address of the program counter (PC)'s value
* @param physicalAddress the physical address
* @param physicalTag the physical tag
* @param onCompletedCallback the callback action performed when the memory hierarchy access is completed
* @return the newly created memory hierarchy access
*/
public MemoryHierarchyAccess beginAccess(DynamicInstruction dynamicInstruction, Thread thread, MemoryHierarchyAccessType type, int virtualPc, int physicalAddress, int physicalTag, Runnable onCompletedCallback) {
MemoryHierarchyAccess newAccess = new MemoryHierarchyAccess(dynamicInstruction, thread, type, virtualPc, physicalAddress, physicalTag, onCompletedCallback);
MemoryHierarchyAccess access = this.findAccess(physicalTag);
if (access != null) {
access.getAliases().add(0, newAccess);
} else {
this.pendingAccesses.put(physicalTag, newAccess);
this.pendingAccessesPerType.put(type, this.pendingAccessesPerType.get(type) + 1);
}
return newAccess;
}
/**
* End the memory hierarchy access matching the specified physical tag.
*
* @param physicalTag the physical tag
*/
public void endAccess(int physicalTag) {
MemoryHierarchyAccess access = this.findAccess(physicalTag);
access.complete();
access.getAliases().forEach(MemoryHierarchyAccess::complete);
MemoryHierarchyAccessType type = access.getType();
this.pendingAccessesPerType.put(type, this.pendingAccessesPerType.get(type) - 1);
this.pendingAccesses.remove(physicalTag);
}
/**
* Receive an "instruction fetch" memory hierarchy access.
*
* @param access the memory hierarchy access
* @param onCompletedCallback the callback action performed when the access is completed
*/
public void receiveIfetch(final MemoryHierarchyAccess access, final Runnable onCompletedCallback) {
this.getCycleAccurateEventQueue().schedule(
this,
() -> onLoad(access, access.getPhysicalTag(), onCompletedCallback), this.getHitLatency()
);
}
/**
* Receive a "load" memory hierarchy access.
*
* @param access the memory hierarchy access
* @param onCompletedCallback the callback action performed when the access is completed
*/
public void receiveLoad(final MemoryHierarchyAccess access, final Runnable onCompletedCallback) {
this.getCycleAccurateEventQueue().schedule(
this,
() -> onLoad(access, access.getPhysicalTag(), onCompletedCallback), this.getHitLatency()
);
}
/**
* Receive a "store" memory hierarchy access.
*
* @param access the memory hierarchy access
* @param onCompletedCallback the callback action performed when the access is completed
*/
public void receiveStore(final MemoryHierarchyAccess access, final Runnable onCompletedCallback) {
this.getCycleAccurateEventQueue().schedule(
this,
() -> onStore(access, access.getPhysicalTag(), onCompletedCallback), this.getHitLatency()
);
}
/**
* Get the next level directory controller.
*
* @return the next level directory controller
*/
public DirectoryController getNext() {
return (DirectoryController) super.getNext();
}
/**
* Set the next level directory controller.
*
* @param next the next level directory controller
*/
public void setNext(MemoryDevice next) {
if (!(next instanceof DirectoryController)) {
throw new IllegalArgumentException();
}
((DirectoryController) next).getCacheControllers().add(this);
super.setNext(next);
}
@Override
public void receive(CoherenceMessage message) {
switch (message.getType()) {
case FWD_GETS:
onFwdGetS((FwdGetSMessage) message);
break;
case FWD_GETM:
onFwdGetM((FwdGetMMessage) message);
break;
case INV:
onInv((InvMessage) message);
break;
case RECALL:
onRecall((RecallMessage) message);
break;
case PUT_ACK:
onPutAck((PutAckMessage) message);
break;
case DATA:
onData((DataMessage) message);
break;
case INV_ACK:
onInvAck((InvAckMessage) message);
break;
default:
throw new IllegalArgumentException();
}
}
/**
* Act on a "load" memory hierarchy access.
*
* @param access the memory hierarchy access
* @param tag the tag
* @param onCompletedCallback the callback action performed when the access is completed
*/
public void onLoad(MemoryHierarchyAccess access, int tag, Runnable onCompletedCallback) {
onLoad(access, tag, new LoadFlow(this, tag, onCompletedCallback, access));
}
/**
* Act on a "load" memory hierarchy access.
*
* @param access the memory hierarchy access
* @param tag the tag
* @param loadFlow the load flow
*/
private void onLoad(final MemoryHierarchyAccess access, final int tag, final LoadFlow loadFlow) {
final Runnable onStalledCallback = () -> onLoad(access, tag, loadFlow);
this.access(loadFlow, access, tag, (set, way) -> {
CacheLine<CacheControllerState> line = getCache().getLine(set, way);
CacheControllerFiniteStateMachine fsm = (CacheControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventLoad(loadFlow, tag, loadFlow.getOnCompletedCallback(), onStalledCallback);
}, onStalledCallback);
}
/**
* Act on a "store" memory hierarchy access.
*
* @param access the memory hierarchy access
* @param tag the tag
* @param onCompletedCallback the callback action performed when the access is completed
*/
public void onStore(MemoryHierarchyAccess access, int tag, Runnable onCompletedCallback) {
onStore(access, tag, new StoreFlow(this, tag, onCompletedCallback, access));
}
/**
* Act on a "store" memory hierarchy access.
*
* @param access the memory hierarchy access
* @param tag the tag
* @param storeFlow the associated store flow
*/
private void onStore(final MemoryHierarchyAccess access, final int tag, final StoreFlow storeFlow) {
final Runnable onStalledCallback = () -> onStore(access, tag, storeFlow);
this.access(storeFlow, access, tag, (set, way) -> {
CacheLine<CacheControllerState> line = getCache().getLine(set, way);
CacheControllerFiniteStateMachine fsm = (CacheControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventStore(storeFlow, tag, storeFlow.getOnCompletedCallback(), onStalledCallback);
}, onStalledCallback);
}
/**
* Act on a "forwarded GetS" message.
*
* @param message the "forwarded GetS" message
*/
private void onFwdGetS(FwdGetSMessage message) {
int way = this.cache.findWay(message.getTag());
CacheLine<CacheControllerState> line = this.getCache().getLine(this.getCache().getSet(message.getTag()), way);
CacheControllerFiniteStateMachine fsm = (CacheControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventFwdGetS(message, message.getRequester(), message.getTag());
}
/**
* Act on a "forwarded GetM" message.
*
* @param message the "forwarded GetM" message
*/
private void onFwdGetM(FwdGetMMessage message) {
int way = this.cache.findWay(message.getTag());
CacheLine<CacheControllerState> line = this.getCache().getLine(this.getCache().getSet(message.getTag()), way);
CacheControllerFiniteStateMachine fsm = (CacheControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventFwdGetM(message, message.getRequester(), message.getTag());
}
/**
* Act on an "invalidation" message.
*
* @param message the "invalidation" message
*/
private void onInv(InvMessage message) {
int way = this.cache.findWay(message.getTag());
CacheLine<CacheControllerState> line = this.getCache().getLine(this.getCache().getSet(message.getTag()), way);
CacheControllerFiniteStateMachine fsm = (CacheControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventInv(message, message.getRequester(), message.getTag());
}
/**
* Act on a "recall" message.
*
* @param message the "recall" message
*/
private void onRecall(RecallMessage message) {
int way = this.cache.findWay(message.getTag());
CacheLine<CacheControllerState> line = this.getCache().getLine(this.getCache().getSet(message.getTag()), way);
CacheControllerFiniteStateMachine fsm = (CacheControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventRecall(message, message.getTag());
}
/**
* Act on a "Put Acknowledgement" message.
*
* @param message the "Put Acknowledgement" message
*/
private void onPutAck(PutAckMessage message) {
int way = this.cache.findWay(message.getTag());
CacheLine<CacheControllerState> line = this.getCache().getLine(this.getCache().getSet(message.getTag()), way);
CacheControllerFiniteStateMachine fsm = (CacheControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventPutAck(message, message.getTag());
}
/**
* Act on a "data" message.
*
* @param message the "data" message
*/
private void onData(DataMessage message) {
int way = this.cache.findWay(message.getTag());
CacheLine<CacheControllerState> line = this.getCache().getLine(this.getCache().getSet(message.getTag()), way);
CacheControllerFiniteStateMachine fsm = (CacheControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventData(message, message.getSender(), message.getTag(), message.getNumInvAcks());
}
/**
* Act on an "invalidation acknowledgement" message.
*
* @param message the "invalidation acknowledgement" message
*/
private void onInvAck(InvAckMessage message) {
int way = this.cache.findWay(message.getTag());
CacheLine<CacheControllerState> line = this.getCache().getLine(this.getCache().getSet(message.getTag()), way);
CacheControllerFiniteStateMachine fsm = (CacheControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventInvAck(message, message.getSender(), message.getTag());
}
/**
* Perform the memory hierarchy access.
*
* @param producerFlow the producer cache coherence flow
* @param access the memory hierarchy access
* @param tag the tag
* @param onReplacementCompletedCallback the callback action performed when the line replacement is completed
* @param onReplacementStalledCallback the callback action performed when the line replacement is stalled
*/
private void access(CacheCoherenceFlow producerFlow, MemoryHierarchyAccess access, int tag, final BiConsumer<Integer, Integer> onReplacementCompletedCallback, final Runnable onReplacementStalledCallback) {
final int set = this.cache.getSet(tag);
final CacheAccess<CacheControllerState> cacheAccess = this.getCache().newAccess(access, tag);
if (cacheAccess.isHitInCache()) {
onReplacementCompletedCallback.accept(set, cacheAccess.getWay());
} else {
if (cacheAccess.isReplacement()) {
CacheLine<CacheControllerState> line = this.getCache().getLine(set, cacheAccess.getWay());
CacheControllerFiniteStateMachine fsm = (CacheControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventReplacement(producerFlow, tag, cacheAccess,
() -> onReplacementCompletedCallback.accept(set, cacheAccess.getWay()),
() -> getCycleAccurateEventQueue().schedule(CacheController.this, onReplacementStalledCallback, 1)
);
} else {
onReplacementCompletedCallback.accept(set, cacheAccess.getWay());
}
}
}
/**
* Get the core.
*
* @return the core
*/
public MemoryHierarchyCore getCore() {
return core;
}
/**
* Set the core.
*
* @param core the core
*/
public void setCore(MemoryHierarchyCore core) {
this.core = core;
}
@Override
public EvictableCache<CacheControllerState> getCache() {
return cache;
}
/**
* Get the lower level directory controller.
*
* @return the lower level directory controller
*/
public DirectoryController getDirectoryController() {
return this.getNext();
}
@Override
public CacheControllerFiniteStateMachineFactory getFsmFactory() {
return fsmFactory;
}
/**
* Get the number of read ports.
*
* @return the number of read ports
*/
public abstract int getNumReadPorts();
/**
* Get the number of write ports.
*
* @return the number of write ports
*/
public abstract int getNumWritePorts();
@Override
public abstract int getHitLatency();
@Override
public abstract CacheReplacementPolicyType getReplacementPolicyType();
@Override
public String toString() {
return this.getCache().getName();
}
}