/**
* ****************************************************************************
* 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.uncore.MemoryDevice;
import archimulator.uncore.MemoryHierarchy;
import archimulator.uncore.MemoryHierarchyAccess;
import archimulator.uncore.cache.*;
import archimulator.uncore.cache.replacement.CacheReplacementPolicyType;
import archimulator.uncore.coherence.msi.event.directory.DirectoryControllerEventType;
import archimulator.uncore.coherence.msi.flow.CacheCoherenceFlow;
import archimulator.uncore.coherence.msi.fsm.DirectoryControllerFiniteStateMachine;
import archimulator.uncore.coherence.msi.fsm.DirectoryControllerFiniteStateMachineFactory;
import archimulator.uncore.coherence.msi.message.*;
import archimulator.uncore.coherence.msi.state.DirectoryControllerState;
import archimulator.uncore.dram.MemoryController;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
/**
* Directory controller.
*
* @author Min Cai
*/
public class DirectoryController extends GeneralCacheController<DirectoryControllerState, DirectoryControllerEventType> {
private CacheGeometry cacheGeometry;
private EvictableCache<DirectoryControllerState> cache;
private List<CacheController> cacheControllers;
private DirectoryControllerFiniteStateMachineFactory fsmFactory;
private int numPendingMemoryAccesses;
/**
* Create a directory controller.
*
* @param memoryHierarchy the memory hierarchy
* @param name the name
*/
public DirectoryController(MemoryHierarchy memoryHierarchy, final String name) {
super(memoryHierarchy, name, MemoryDeviceType.L2_CONTROLLER);
this.cacheGeometry = new CacheGeometry(
getExperiment().getConfig().getL2Size(),
getExperiment().getConfig().getL2Associativity(),
getExperiment().getConfig().getL2LineSize()
);
this.cache = new BasicEvictableCache<>(
memoryHierarchy,
name,
getGeometry(),
getReplacementPolicyType(),
args -> {
int set = (Integer) args[0];
int way = (Integer) args[1];
return new DirectoryControllerFiniteStateMachine(name, set, way, this);
}
);
this.cacheControllers = new ArrayList<>();
this.fsmFactory = DirectoryControllerFiniteStateMachineFactory.getSingleton();
}
public MemoryController getNext() {
return (MemoryController) super.getNext();
}
public void setNext(MemoryDevice next) {
if (!(next instanceof MemoryController)) {
throw new IllegalArgumentException();
}
super.setNext(next);
}
@Override
public void receive(CoherenceMessage message) {
switch (message.getType()) {
case GETS:
onGetS((GetSMessage) message);
break;
case GETM:
onGetM((GetMMessage) message);
break;
case RECALL_ACK:
onRecallAck((RecallAckMessage) message);
break;
case PUTS:
onPutS((PutSMessage) message);
break;
case PUTM_AND_DATA:
onPutMAndData((PutMAndDataMessage) message);
break;
case DATA:
onData((DataMessage) message);
break;
default:
throw new IllegalArgumentException();
}
}
/**
* Act on a "GetS" message.
*
* @param message the "GetS" message
*/
private void onGetS(final GetSMessage message) {
final Runnable onStalledCallback = () -> onGetS(message);
this.access(message, message.getAccess(), message.getRequester(), message.getTag(), (set, way) -> {
CacheLine<DirectoryControllerState> line = getCache().getLine(set, way);
DirectoryControllerFiniteStateMachine fsm = (DirectoryControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventGetS(message, message.getRequester(), message.getTag(), onStalledCallback);
}, onStalledCallback);
}
/**
* Act on a "GetM" message.
*
* @param message the "GetM" message
*/
private void onGetM(final GetMMessage message) {
final Runnable onStalledCallback = () -> onGetM(message);
this.access(message, message.getAccess(), message.getRequester(), message.getTag(), (set, way) -> {
CacheLine<DirectoryControllerState> line = getCache().getLine(set, way);
DirectoryControllerFiniteStateMachine fsm = (DirectoryControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventGetM(message, message.getRequester(), message.getTag(), onStalledCallback);
}, onStalledCallback);
}
/**
* Act on a "recall acknowledgement" message.
*
* @param message the "recall acknowledgement" message
*/
private void onRecallAck(RecallAckMessage message) {
CacheController sender = message.getSender();
int tag = message.getTag();
int way = this.cache.findWay(tag);
CacheLine<DirectoryControllerState> line = this.cache.getLine(this.cache.getSet(tag), way);
DirectoryControllerFiniteStateMachine fsm = (DirectoryControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventRecallAck(message, sender, tag);
}
/**
* Act on a "PutS" message.
*
* @param message the "PutS" message
*/
private void onPutS(PutSMessage message) {
CacheController req = message.getRequester();
int tag = message.getTag();
int way = this.cache.findWay(tag);
if (way == -1) {
DirectoryControllerFiniteStateMachine.sendPutAckToReq(message, this, req, tag);
} else {
CacheLine<DirectoryControllerState> line = this.cache.getLine(this.cache.getSet(tag), way);
DirectoryControllerFiniteStateMachine fsm = (DirectoryControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventPutS(message, req, tag);
}
}
/**
* Act on a "PutM and data" message.
*
* @param message the "PutM and data" message
*/
private void onPutMAndData(PutMAndDataMessage message) {
CacheController req = message.getRequester();
int tag = message.getTag();
int way = this.cache.findWay(tag);
if (tag == -1) {
DirectoryControllerFiniteStateMachine.sendPutAckToReq(message, this, req, tag);
} else {
CacheLine<DirectoryControllerState> line = this.cache.getLine(this.cache.getSet(tag), way);
DirectoryControllerFiniteStateMachine fsm = (DirectoryControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventPutMAndData(message, req, tag);
}
}
/**
* Act on a "data" message.
*
* @param message the "data" message
*/
private void onData(DataMessage message) {
CacheController sender = (CacheController) message.getSender();
int tag = message.getTag();
int way = this.cache.findWay(tag);
CacheLine<DirectoryControllerState> line = this.cache.getLine(this.cache.getSet(tag), way);
DirectoryControllerFiniteStateMachine fsm = (DirectoryControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventData(message, sender, tag);
}
/**
* Perform the memory hierarchy access.
*
* @param producerFlow the producer flow
* @param access the access
* @param req the requester controller
* @param tag the tag
* @param onReplacementCompletedCallback the callback action performed when the replacement is completed
* @param onReplacementStalledCallback the callback action performed when the replacement is stalled
*/
private void access(CacheCoherenceFlow producerFlow, MemoryHierarchyAccess access, CacheController req, final int tag, final BiConsumer<Integer, Integer> onReplacementCompletedCallback, final Runnable onReplacementStalledCallback) {
final int set = this.cache.getSet(tag);
for (CacheLine<DirectoryControllerState> line : this.cache.getLines(set)) {
DirectoryControllerFiniteStateMachine fsm = (DirectoryControllerFiniteStateMachine) line.getStateProvider();
if (line.getState() == DirectoryControllerState.MI_A || line.getState() == DirectoryControllerState.SI_A && fsm.getEvicterTag() == tag) {
fsm.stall(onReplacementStalledCallback);
return;
}
}
final CacheAccess<DirectoryControllerState> cacheAccess = this.cache.newAccess(access, tag);
if (cacheAccess.isHitInCache()) {
onReplacementCompletedCallback.accept(set, cacheAccess.getWay());
} else {
if (cacheAccess.isReplacement()) {
CacheLine<DirectoryControllerState> line = this.getCache().getLine(set, cacheAccess.getWay());
DirectoryControllerFiniteStateMachine fsm = (DirectoryControllerFiniteStateMachine) line.getStateProvider();
fsm.onEventReplacement(
producerFlow,
req,
tag,
cacheAccess,
() -> onReplacementCompletedCallback.accept(set, cacheAccess.getWay()),
() -> getCycleAccurateEventQueue().schedule(DirectoryController.this, onReplacementStalledCallback, 1)
);
} else {
onReplacementCompletedCallback.accept(set, cacheAccess.getWay());
}
}
}
@Override
public EvictableCache<DirectoryControllerState> getCache() {
return cache;
}
/**
* Get the list of upper level L1 cache controllers.
*
* @return the list of upper level L1 cache controllers
*/
public List<CacheController> getCacheControllers() {
return cacheControllers;
}
public DirectoryControllerFiniteStateMachineFactory getFsmFactory() {
return fsmFactory;
}
@Override
public CacheGeometry getGeometry() {
return cacheGeometry;
}
@Override
public int getHitLatency() {
return getExperiment().getConfig().getL2HitLatency();
}
@Override
public CacheReplacementPolicyType getReplacementPolicyType() {
return getExperiment().getConfig().getL2ReplacementPolicyType();
}
@Override
public String toString() {
return this.getCache().getName();
}
/**
* Increment the number of pending memory accesses.
*/
public void incrementNumPendingMemoryAccesses() {
this.numPendingMemoryAccesses++;
}
/**
* Decrement the number of pending memory accesses.
*/
public void decrementNumPendingMemoryAccesses() {
this.numPendingMemoryAccesses--;
}
/**
* Get the number of pending memory accesses.
*
* @return the number of pending memory accesses
*/
public int getNumPendingMemoryAccesses() {
return numPendingMemoryAccesses;
}
}