blob: f0bb31cb7575b9a641f476a1f408d14cd1342029 [file] [log] [blame]
/*
* Copyright (c) 2012-2015 Advanced Micro Devices, Inc.
* All rights reserved.
*
* For use for simulation and test purposes only
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Jason Power
*/
machine(MachineType:RegionDir, "Region Directory for AMD_Base-like protocol")
: CacheMemory *cacheMemory; // stores only region addresses. Must set block size same as below
NodeID cpuRegionBufferNum;
NodeID gpuRegionBufferNum;
int blocksPerRegion := 64; // 4k regions
Cycles toDirLatency := 10; // Latency to fwd requests and send invs to directory
bool always_migrate := "False";
bool sym_migrate := "False";
bool asym_migrate := "False";
bool noTCCdir := "False";
int TCC_select_num_bits := 1;
// To the directory
MessageBuffer * requestToDir, network="To", virtual_network="5", vnet_type="request";
// To the region buffers
MessageBuffer * notifyToRBuffer, network="To", virtual_network="7", vnet_type="request";
MessageBuffer * probeToRBuffer, network="To", virtual_network="8", vnet_type="request";
// From the region buffers
MessageBuffer * responseFromRBuffer, network="From", virtual_network="2", vnet_type="response";
MessageBuffer * requestFromRegBuf, network="From", virtual_network="0", vnet_type="request";
MessageBuffer * triggerQueue;
{
// States
state_declaration(State, desc="Region states", default="RegionDir_State_NP") {
NP, AccessPermission:Invalid, desc="Not present in region directory";
P, AccessPermission:Invalid, desc="Region is private to owner";
S, AccessPermission:Invalid, desc="Region is shared between CPU and GPU";
P_NP, AccessPermission:Invalid, desc="Evicting the region";
NP_P, AccessPermission:Invalid, desc="Must wait for ack from R-buf";
NP_S, AccessPermission:Invalid, desc="Must wait for ack from R-buf";
P_P, AccessPermission:Invalid, desc="Waiting for ack from R-buf";
S_S, AccessPermission:Invalid, desc="Waiting for ack from R-buf";
P_S, AccessPermission:Invalid, desc="Downgrading the region";
S_P, AccessPermission:Invalid, desc="Upgrading the region";
P_AS, AccessPermission:Invalid, desc="Sent invalidates, waiting for acks";
S_AP, AccessPermission:Invalid, desc="Sent invalidates, waiting for acks";
P_AP, AccessPermission:Invalid, desc="Sent invalidates, waiting for acks";
SP_NP_W, AccessPermission:Invalid, desc="Last sharer writing back, waiting for ack";
S_W, AccessPermission:Invalid, desc="Sharer writing back, waiting for ack";
P_AP_W, AccessPermission:Invalid, desc="Fwded request to dir, waiting for ack";
P_AS_W, AccessPermission:Invalid, desc="Fwded request to dir, waiting for ack";
S_AP_W, AccessPermission:Invalid, desc="Fwded request to dir, waiting for ack";
}
enumeration(Event, desc="Region directory events") {
SendInv, desc="Send inv message to any machine that has a region buffer";
SendUpgrade, desc="Send upgrade message to any machine that has a region buffer";
SendDowngrade, desc="Send downgrade message to any machine that has a region buffer";
Evict, desc="Evict this region";
UpgradeRequest, desc="Request from r-buf for an upgrade";
SharedRequest, desc="Request from r-buf for read";
PrivateRequest, desc="Request from r-buf for write";
InvAckCore, desc="Ack from region buffer to order the invalidate";
InvAckCoreNoShare, desc="Ack from region buffer to order the invalidate, and it does not have the region";
CPUPrivateAck, desc="Ack from region buffer to order private notification";
LastAck, desc="Done eviciting all the blocks";
StaleCleanWbRequest, desc="stale clean writeback reqeust";
StaleCleanWbRequestNoShare, desc="stale clean wb req from a cache which should be removed from sharers";
CleanWbRequest, desc="clean writeback reqeust, multiple sharers";
CleanWbRequest_LastSharer, desc="clean writeback reqeust, last sharer";
WritebackAck, desc="Writeback Ack from region buffer";
DirReadyAck, desc="Directory is ready, waiting Ack from region buffer";
TriggerInv, desc="trigger invalidate message";
TriggerDowngrade, desc="trigger downgrade message";
}
enumeration(RequestType, desc="To communicate stats from transitions to recordStats") {
DataArrayRead, desc="Read the data array";
DataArrayWrite, desc="Write the data array";
TagArrayRead, desc="Read the data array";
TagArrayWrite, desc="Write the data array";
}
structure(BoolVec, external="yes") {
bool at(int);
void resize(int);
void clear();
}
structure(Entry, desc="Region entry", interface="AbstractCacheEntry") {
Addr addr, desc="Base address of this region";
NetDest Sharers, desc="Set of machines that are sharing, but not owners";
State RegionState, desc="Region state";
DataBlock DataBlk, desc="Data for the block (always empty in region dir)";
MachineID Owner, desc="Machine which owns all blocks in this region";
Cycles ProbeStart, desc="Time when the first probe request was issued";
bool LastWriten, default="false", desc="The last time someone accessed this region, it wrote it";
bool LastWritenByCpu, default="false", desc="The last time the CPU accessed this region, it wrote it";
bool LastWritenByGpu, default="false", desc="The last time the GPU accessed this region, it wrote it";
}
structure(TBE, desc="...") {
State TBEState, desc="Transient state";
MachineID Owner, desc="Machine which owns all blocks in this region";
NetDest Sharers, desc="Set of machines to send evicts";
int NumValidBlocks, desc="Number of blocks valid so we don't have to count a BoolVec";
bool AllAcksReceived, desc="Got all necessary acks from dir";
CoherenceRequestType MsgType, desc="Msg type for the evicts could be inv or dwngrd";
Cycles ProbeRequestTime, default="Cycles(0)", desc="Start of probe request";
Cycles InitialRequestTime, default="Cycles(0)", desc="To forward back on out msg";
Addr DemandAddress, desc="Demand address from original request";
uint64_t probe_id, desc="probe id for lifetime profiling";
}
structure(TBETable, external="yes") {
TBE lookup(Addr);
void allocate(Addr);
void deallocate(Addr);
bool isPresent(Addr);
}
// Stores only region addresses
TBETable TBEs, template="<RegionDir_TBE>", constructor="m_number_of_TBEs";
int TCC_select_low_bit, default="RubySystem::getBlockSizeBits()";
Tick clockEdge();
Tick cyclesToTicks(Cycles c);
void set_cache_entry(AbstractCacheEntry b);
void unset_cache_entry();
void set_tbe(TBE b);
void unset_tbe();
void wakeUpAllBuffers();
void wakeUpBuffers(Addr a);
Cycles curCycle();
MachineID mapAddressToMachine(Addr addr, MachineType mtype);
int blockBits, default="RubySystem::getBlockSizeBits()";
int blockBytes, default="RubySystem::getBlockSizeBytes()";
int regionBits, default="log2(m_blocksPerRegion)";
// Functions
MachineID getCoreMachine(MachineID rBuf, Addr address) {
if (machineIDToNodeID(rBuf) == cpuRegionBufferNum) {
return createMachineID(MachineType:CorePair, intToID(0));
} else if (machineIDToNodeID(rBuf) == gpuRegionBufferNum) {
if (noTCCdir) {
return mapAddressToRange(address,MachineType:TCC,
TCC_select_low_bit, TCC_select_num_bits);
} else {
return createMachineID(MachineType:TCCdir, intToID(0));
}
} else {
error("Unexpected region buffer number");
}
}
bool isCpuMachine(MachineID rBuf) {
if (machineIDToNodeID(rBuf) == cpuRegionBufferNum) {
return true;
} else if (machineIDToNodeID(rBuf) == gpuRegionBufferNum) {
return false;
} else {
error("Unexpected region buffer number");
}
}
bool symMigrate(Entry cache_entry) {
return cache_entry.LastWriten;
}
bool asymMigrate(Entry cache_entry, MachineID requestor) {
if (isCpuMachine(requestor)) {
return cache_entry.LastWritenByCpu;
} else {
return cache_entry.LastWritenByGpu;
}
}
int getRegionOffset(Addr addr) {
if (blocksPerRegion > 1) {
Addr offset := bitSelect(addr, blockBits, regionBits+blockBits-1);
int ret := addressToInt(offset);
assert(ret < blocksPerRegion);
return ret;
} else {
return 0;
}
}
Addr getRegionBase(Addr addr) {
return maskLowOrderBits(addr, blockBits+regionBits);
}
Addr getNextBlock(Addr addr) {
Addr a := addr;
makeNextStrideAddress(a, 1);
return a;
}
bool presentOrAvail(Addr addr) {
DPRINTF(RubySlicc, "Present? %s, avail? %s\n", cacheMemory.isTagPresent(getRegionBase(addr)), cacheMemory.cacheAvail(getRegionBase(addr)));
return cacheMemory.isTagPresent(getRegionBase(addr)) || cacheMemory.cacheAvail(getRegionBase(addr));
}
// Returns a region entry!
Entry getCacheEntry(Addr addr), return_by_pointer="yes" {
return static_cast(Entry, "pointer", cacheMemory.lookup(getRegionBase(addr)));
}
TBE getTBE(Addr addr), return_by_pointer="yes" {
return TBEs.lookup(getRegionBase(addr));
}
DataBlock getDataBlock(Addr addr), return_by_ref="yes" {
return getCacheEntry(getRegionBase(addr)).DataBlk;
}
State getState(TBE tbe, Entry cache_entry, Addr addr) {
if (is_valid(tbe)) {
return tbe.TBEState;
} else if (is_valid(cache_entry)) {
return cache_entry.RegionState;
}
return State:NP;
}
void setState(TBE tbe, Entry cache_entry, Addr addr, State state) {
if (is_valid(tbe)) {
tbe.TBEState := state;
}
if (is_valid(cache_entry)) {
cache_entry.RegionState := state;
}
}
AccessPermission getAccessPermission(Addr addr) {
TBE tbe := getTBE(addr);
if(is_valid(tbe)) {
return RegionDir_State_to_permission(tbe.TBEState);
}
Entry cache_entry := getCacheEntry(addr);
if(is_valid(cache_entry)) {
return RegionDir_State_to_permission(cache_entry.RegionState);
}
return AccessPermission:NotPresent;
}
void setAccessPermission(Entry cache_entry, Addr addr, State state) {
if (is_valid(cache_entry)) {
cache_entry.changePermission(RegionDir_State_to_permission(state));
}
}
void functionalRead(Addr addr, Packet *pkt) {
functionalMemoryRead(pkt);
}
int functionalWrite(Addr addr, Packet *pkt) {
if (functionalMemoryWrite(pkt)) {
return 1;
} else {
return 0;
}
}
void recordRequestType(RequestType request_type, Addr addr) {
if (request_type == RequestType:DataArrayRead) {
cacheMemory.recordRequestType(CacheRequestType:DataArrayRead, addr);
} else if (request_type == RequestType:DataArrayWrite) {
cacheMemory.recordRequestType(CacheRequestType:DataArrayWrite, addr);
} else if (request_type == RequestType:TagArrayRead) {
cacheMemory.recordRequestType(CacheRequestType:TagArrayRead, addr);
} else if (request_type == RequestType:TagArrayWrite) {
cacheMemory.recordRequestType(CacheRequestType:TagArrayWrite, addr);
}
}
bool checkResourceAvailable(RequestType request_type, Addr addr) {
if (request_type == RequestType:DataArrayRead) {
return cacheMemory.checkResourceAvailable(CacheResourceType:DataArray, addr);
} else if (request_type == RequestType:DataArrayWrite) {
return cacheMemory.checkResourceAvailable(CacheResourceType:DataArray, addr);
} else if (request_type == RequestType:TagArrayRead) {
return cacheMemory.checkResourceAvailable(CacheResourceType:TagArray, addr);
} else if (request_type == RequestType:TagArrayWrite) {
return cacheMemory.checkResourceAvailable(CacheResourceType:TagArray, addr);
} else {
error("Invalid RequestType type in checkResourceAvailable");
return true;
}
}
out_port(triggerQueue_out, TriggerMsg, triggerQueue);
out_port(requestNetwork_out, CPURequestMsg, requestToDir);
out_port(notifyNetwork_out, CPURequestMsg, notifyToRBuffer);
out_port(probeNetwork_out, NBProbeRequestMsg, probeToRBuffer);
in_port(triggerQueue_in, TriggerMsg, triggerQueue, rank=2) {
if (triggerQueue_in.isReady(clockEdge())) {
peek(triggerQueue_in, TriggerMsg) {
assert(in_msg.addr == getRegionBase(in_msg.addr));
Entry cache_entry := getCacheEntry(in_msg.addr);
TBE tbe := getTBE(in_msg.addr);
DPRINTF(RubySlicc, "trigger msg: %s (%s)\n", in_msg, getRegionBase(in_msg.addr));
if (in_msg.Type == TriggerType:AcksComplete) {
assert(is_valid(tbe));
trigger(Event:LastAck, in_msg.addr, cache_entry, tbe);
} else if (in_msg.Type == TriggerType:InvRegion) {
assert(is_valid(tbe));
trigger(Event:TriggerInv, in_msg.addr, cache_entry, tbe);
} else if (in_msg.Type == TriggerType:DowngradeRegion) {
assert(is_valid(tbe));
trigger(Event:TriggerDowngrade, in_msg.addr, cache_entry, tbe);
} else {
error("Unknown trigger message");
}
}
}
}
in_port(responseNetwork_in, ResponseMsg, responseFromRBuffer, rank=1) {
if (responseNetwork_in.isReady(clockEdge())) {
peek(responseNetwork_in, ResponseMsg) {
TBE tbe := getTBE(in_msg.addr);
Entry cache_entry := getCacheEntry(in_msg.addr);
if (in_msg.Type == CoherenceResponseType:CPUPrbResp) {
assert(in_msg.addr == getRegionBase(in_msg.addr));
assert(is_valid(tbe));
if (in_msg.NotCached) {
trigger(Event:InvAckCoreNoShare, in_msg.addr, cache_entry, tbe);
} else {
trigger(Event:InvAckCore, in_msg.addr, cache_entry, tbe);
}
} else if (in_msg.Type == CoherenceResponseType:PrivateAck) {
assert(in_msg.addr == getRegionBase(in_msg.addr));
assert(is_valid(cache_entry));
//Fix Me...add back in: assert(cache_entry.Sharers.isElement(in_msg.Sender));
trigger(Event:CPUPrivateAck, in_msg.addr, cache_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:RegionWbAck) {
//Fix Me...add back in: assert(cache_entry.Sharers.isElement(in_msg.Sender) == false);
assert(in_msg.addr == getRegionBase(in_msg.addr));
trigger(Event:WritebackAck, in_msg.addr, cache_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:DirReadyAck) {
assert(is_valid(tbe));
trigger(Event:DirReadyAck, getRegionBase(in_msg.addr), cache_entry, tbe);
} else {
error("Invalid response type");
}
}
}
}
// In from cores
// NOTE: We get the cache / TBE entry based on the region address,
// but pass the block address to the actions
in_port(requestNetwork_in, CPURequestMsg, requestFromRegBuf, rank=0) {
if (requestNetwork_in.isReady(clockEdge())) {
peek(requestNetwork_in, CPURequestMsg) {
//assert(in_msg.addr == getRegionBase(in_msg.addr));
Addr address := getRegionBase(in_msg.addr);
DPRINTF(RubySlicc, "Got %s, base %s\n", in_msg.addr, address);
if (presentOrAvail(address)) {
TBE tbe := getTBE(address);
Entry cache_entry := getCacheEntry(address);
if (in_msg.Type == CoherenceRequestType:PrivateRequest) {
if (is_valid(cache_entry) && (cache_entry.Owner != in_msg.Requestor ||
getState(tbe, cache_entry, address) == State:S)) {
trigger(Event:SendInv, address, cache_entry, tbe);
} else {
trigger(Event:PrivateRequest, address, cache_entry, tbe);
}
} else if (in_msg.Type == CoherenceRequestType:SharedRequest) {
if (is_invalid(cache_entry)) {
// If no one has ever requested this region give private permissions
trigger(Event:PrivateRequest, address, cache_entry, tbe);
} else {
if (always_migrate ||
(sym_migrate && symMigrate(cache_entry)) ||
(asym_migrate && asymMigrate(cache_entry, in_msg.Requestor))) {
if (cache_entry.Sharers.count() == 1 &&
cache_entry.Sharers.isElement(in_msg.Requestor)) {
trigger(Event:UpgradeRequest, address, cache_entry, tbe);
} else {
trigger(Event:SendInv, address, cache_entry, tbe);
}
} else { // don't migrate
if(cache_entry.Sharers.isElement(in_msg.Requestor) ||
getState(tbe, cache_entry, address) == State:S) {
trigger(Event:SharedRequest, address, cache_entry, tbe);
} else {
trigger(Event:SendDowngrade, address, cache_entry, tbe);
}
}
}
} else if (in_msg.Type == CoherenceRequestType:UpgradeRequest) {
if (is_invalid(cache_entry)) {
trigger(Event:PrivateRequest, address, cache_entry, tbe);
} else if (cache_entry.Sharers.count() == 1 && cache_entry.Sharers.isElement(in_msg.Requestor)) {
trigger(Event:UpgradeRequest, address, cache_entry, tbe);
} else {
trigger(Event:SendUpgrade, address, cache_entry, tbe);
}
} else if (in_msg.Type == CoherenceRequestType:CleanWbRequest) {
if (is_invalid(cache_entry) || cache_entry.Sharers.isElement(in_msg.Requestor) == false) {
trigger(Event:StaleCleanWbRequest, address, cache_entry, tbe);
} else {
DPRINTF(RubySlicc, "wb address %s(%s) owner %s sharers %s requestor %s %d %d\n", in_msg.addr, getRegionBase(in_msg.addr), cache_entry.Owner, cache_entry.Sharers, in_msg.Requestor, cache_entry.Sharers.isElement(in_msg.Requestor), cache_entry.Sharers.count());
if (cache_entry.Sharers.isElement(in_msg.Requestor) && cache_entry.Sharers.count() == 1) {
DPRINTF(RubySlicc, "last wb\n");
trigger(Event:CleanWbRequest_LastSharer, address, cache_entry, tbe);
} else {
DPRINTF(RubySlicc, "clean wb\n");
trigger(Event:CleanWbRequest, address, cache_entry, tbe);
}
}
} else {
error("unknown region dir request type");
}
} else {
Addr victim := cacheMemory.cacheProbe(getRegionBase(in_msg.addr));
TBE victim_tbe := getTBE(victim);
Entry victim_entry := getCacheEntry(victim);
DPRINTF(RubySlicc, "Evicting address %s for new region at address %s(%s)\n", victim, in_msg.addr, getRegionBase(in_msg.addr));
assert(is_valid(victim_entry));
trigger(Event:Evict, victim, victim_entry, victim_tbe);
}
}
}
}
// Actions
action(f_fwdReqToDir, "f", desc="Forward CPU request to directory") {
peek(requestNetwork_in, CPURequestMsg) {
enqueue(requestNetwork_out, CPURequestMsg, toDirLatency) {
out_msg.addr := in_msg.addr; // This is the block address. "address" is the region address
out_msg.Type := in_msg.OriginalType;
out_msg.DataBlk := in_msg.DataBlk;
out_msg.Dirty := in_msg.Dirty;
out_msg.Requestor := getCoreMachine(in_msg.Requestor,address);
out_msg.WTRequestor := in_msg.WTRequestor;
out_msg.Destination.add(mapAddressToMachine(in_msg.addr, MachineType:Directory));
out_msg.Shared := in_msg.Shared;
out_msg.MessageSize := in_msg.MessageSize;
out_msg.Private := in_msg.Private;
out_msg.NoAckNeeded := true;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ProbeRequestStartTime := curCycle();
out_msg.DemandRequest := true;
if (is_valid(cache_entry) && getState(tbe, cache_entry, address) != State:S) {
out_msg.Acks := cache_entry.Sharers.count();
} else {
out_msg.Acks := 0;
}
}
}
}
action(f_fwdReqToDirShared, "fs", desc="Forward CPU request to directory (shared)") {
peek(requestNetwork_in, CPURequestMsg) {
enqueue(requestNetwork_out, CPURequestMsg, toDirLatency) {
out_msg.addr := in_msg.addr; // This is the block address. "address" is the region address
out_msg.Type := in_msg.OriginalType;
out_msg.DataBlk := in_msg.DataBlk;
out_msg.Dirty := in_msg.Dirty;
out_msg.Requestor := getCoreMachine(in_msg.Requestor,address);
out_msg.WTRequestor := in_msg.WTRequestor;
out_msg.Destination.add(mapAddressToMachine(in_msg.addr, MachineType:Directory));
out_msg.Shared := in_msg.Shared;
out_msg.MessageSize := in_msg.MessageSize;
out_msg.Private := in_msg.Private;
out_msg.NoAckNeeded := true;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ProbeRequestStartTime := curCycle();
out_msg.DemandRequest := true;
out_msg.ForceShared := true;
if (is_valid(cache_entry) && getState(tbe, cache_entry, address) != State:S) {
out_msg.Acks := cache_entry.Sharers.count();
} else {
out_msg.Acks := 0;
}
}
}
}
action(f_fwdReqToDirWithAck, "fa", desc="Forward CPU request to directory with ack request") {
peek(requestNetwork_in, CPURequestMsg) {
enqueue(requestNetwork_out, CPURequestMsg, toDirLatency) {
out_msg.addr := in_msg.addr; // This is the block address. "address" is the region address
out_msg.Type := in_msg.OriginalType;
out_msg.DataBlk := in_msg.DataBlk;
out_msg.Dirty := in_msg.Dirty;
out_msg.Requestor := getCoreMachine(in_msg.Requestor,address);
out_msg.WTRequestor := in_msg.WTRequestor;
out_msg.Destination.add(mapAddressToMachine(in_msg.addr, MachineType:Directory));
out_msg.Shared := in_msg.Shared;
out_msg.MessageSize := in_msg.MessageSize;
out_msg.Private := in_msg.Private;
out_msg.NoAckNeeded := false;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ProbeRequestStartTime := curCycle();
out_msg.DemandRequest := true;
if (is_valid(cache_entry)) {
out_msg.Acks := cache_entry.Sharers.count();
// Don't need an ack from the requestor!
if (cache_entry.Sharers.isElement(in_msg.Requestor)) {
out_msg.Acks := out_msg.Acks - 1;
}
} else {
out_msg.Acks := 0;
}
}
}
}
action(f_fwdReqToDirWithAckShared, "fas", desc="Forward CPU request to directory with ack request") {
peek(requestNetwork_in, CPURequestMsg) {
enqueue(requestNetwork_out, CPURequestMsg, toDirLatency) {
out_msg.addr := in_msg.addr; // This is the block address. "address" is the region address
out_msg.Type := in_msg.OriginalType;
out_msg.DataBlk := in_msg.DataBlk;
out_msg.Dirty := in_msg.Dirty;
out_msg.Requestor := getCoreMachine(in_msg.Requestor,address);
out_msg.WTRequestor := in_msg.WTRequestor;
out_msg.Destination.add(mapAddressToMachine(in_msg.addr, MachineType:Directory));
out_msg.Shared := in_msg.Shared;
out_msg.MessageSize := in_msg.MessageSize;
out_msg.Private := in_msg.Private;
out_msg.NoAckNeeded := false;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ProbeRequestStartTime := curCycle();
out_msg.DemandRequest := true;
out_msg.ForceShared := true;
if (is_valid(cache_entry)) {
out_msg.Acks := cache_entry.Sharers.count();
// Don't need an ack from the requestor!
if (cache_entry.Sharers.isElement(in_msg.Requestor)) {
out_msg.Acks := out_msg.Acks - 1;
}
} else {
out_msg.Acks := 0;
}
}
}
}
action(a_allocateRegionEntry, "a", desc="Allocate a new entry") {
set_cache_entry(cacheMemory.allocate(getRegionBase(address), new Entry));
peek(requestNetwork_in, CPURequestMsg) {
APPEND_TRANSITION_COMMENT(in_msg.Requestor);
}
}
action(d_deallocateRegionEntry, "d", desc="Deallocate region entry") {
cacheMemory.deallocate(getRegionBase(address));
unset_cache_entry();
}
action(ra_receiveAck, "ra", desc="Mark TBE entry as received this ack") {
//assert(tbe.ValidBlocks.at(getRegionOffset(address)));
DPRINTF(RubySlicc, "received ack for %s reg: %s\n", address, getRegionBase(address));
tbe.NumValidBlocks := tbe.NumValidBlocks - 1;
assert(tbe.NumValidBlocks >= 0);
if (tbe.NumValidBlocks == 0) {
tbe.AllAcksReceived := true;
enqueue(triggerQueue_out, TriggerMsg, 1) {
out_msg.Type := TriggerType:AcksComplete;
out_msg.addr := address;
}
}
APPEND_TRANSITION_COMMENT(getRegionBase(address));
APPEND_TRANSITION_COMMENT(" Acks left receive ");
APPEND_TRANSITION_COMMENT(tbe.NumValidBlocks);
}
action(ca_checkAcks, "ca", desc="Check to see if we need more acks") {
if (tbe.NumValidBlocks == 0) {
tbe.AllAcksReceived := true;
enqueue(triggerQueue_out, TriggerMsg, 1) {
out_msg.Type := TriggerType:AcksComplete;
out_msg.addr := address;
}
}
}
action(ti_triggerInv, "ti", desc="") {
enqueue(triggerQueue_out, TriggerMsg, 1) {
out_msg.Type := TriggerType:InvRegion;
out_msg.addr := address;
}
}
action(td_triggerDowngrade, "td", desc="") {
enqueue(triggerQueue_out, TriggerMsg, 1) {
out_msg.Type := TriggerType:DowngradeRegion;
out_msg.addr := address;
}
}
action(t_allocateTBE, "t", desc="allocate TBE Entry") {
check_allocate(TBEs);
TBEs.allocate(getRegionBase(address));
set_tbe(getTBE(address));
if (is_valid(cache_entry)) {
tbe.Owner := cache_entry.Owner;
tbe.Sharers := cache_entry.Sharers;
tbe.AllAcksReceived := true; // assume no acks are required
}
tbe.ProbeRequestTime := curCycle();
peek(requestNetwork_in, CPURequestMsg) {
tbe.InitialRequestTime := in_msg.InitialRequestTime;
tbe.DemandAddress := in_msg.addr;
}
APPEND_TRANSITION_COMMENT(getRegionBase(address));
APPEND_TRANSITION_COMMENT(" Acks left ");
APPEND_TRANSITION_COMMENT(tbe.NumValidBlocks);
APPEND_TRANSITION_COMMENT(" Owner, ");
APPEND_TRANSITION_COMMENT(tbe.Owner);
APPEND_TRANSITION_COMMENT(" sharers, ");
APPEND_TRANSITION_COMMENT(tbe.Sharers);
}
action(ss_setSharers, "ss", desc="Add requestor to sharers") {
peek(requestNetwork_in, CPURequestMsg) {
cache_entry.Sharers.add(in_msg.Requestor);
APPEND_TRANSITION_COMMENT(cache_entry.Sharers);
}
}
action(rs_removeSharer, "rs", desc="Remove requestor to sharers") {
peek(requestNetwork_in, CPURequestMsg) {
cache_entry.Sharers.remove(in_msg.Requestor);
APPEND_TRANSITION_COMMENT(" removing ");
APPEND_TRANSITION_COMMENT(in_msg.Requestor);
APPEND_TRANSITION_COMMENT(" sharers ");
APPEND_TRANSITION_COMMENT(cache_entry.Sharers);
}
}
action(rsr_removeSharerResponse, "rsr", desc="Remove requestor to sharers") {
peek(responseNetwork_in, ResponseMsg) {
cache_entry.Sharers.remove(in_msg.Sender);
APPEND_TRANSITION_COMMENT(cache_entry.Sharers);
}
}
action(cs_clearSharers, "cs", desc="Add requestor to sharers") {
cache_entry.Sharers.clear();
}
action(so_setOwner, "so", desc="Set the owner to the requestor") {
peek(requestNetwork_in, CPURequestMsg) {
cache_entry.Owner := in_msg.Requestor;
APPEND_TRANSITION_COMMENT(" Owner now: ");
APPEND_TRANSITION_COMMENT(cache_entry.Owner);
}
}
action(rr_removeRequestorFromTBE, "rr", desc="Remove requestor from TBE sharers") {
peek(requestNetwork_in, CPURequestMsg) {
tbe.Sharers.remove(in_msg.Requestor);
}
}
action(ur_updateDirtyStatusOnRequest, "ur", desc="Update dirty status on demand request") {
peek(requestNetwork_in, CPURequestMsg) {
if (is_valid(cache_entry)) {
if ((in_msg.Type == CoherenceRequestType:SharedRequest) &&
(cache_entry.Sharers.isElement(in_msg.Requestor) == false)) {
cache_entry.LastWriten := false;
if (isCpuMachine(in_msg.Requestor)) {
cache_entry.LastWritenByCpu := false;
} else {
cache_entry.LastWritenByGpu := false;
}
} else if ((in_msg.Type == CoherenceRequestType:PrivateRequest) ||
(in_msg.Type == CoherenceRequestType:UpgradeRequest)) {
cache_entry.LastWriten := true;
if (isCpuMachine(in_msg.Requestor)) {
cache_entry.LastWritenByCpu := true;
} else {
cache_entry.LastWritenByGpu := true;
}
}
}
}
}
action(ud_updateDirtyStatusWithWb, "ud", desc="Update dirty status on writeback") {
peek(requestNetwork_in, CPURequestMsg) {
if (is_valid(cache_entry) && in_msg.Dirty) {
cache_entry.LastWriten := true;
if (isCpuMachine(in_msg.Requestor)) {
cache_entry.LastWritenByCpu := true;
} else {
cache_entry.LastWritenByGpu := true;
}
}
}
}
action(sns_setNumAcksSharers, "sns", desc="Set number of acks to one per shared region buffer") {
assert(is_valid(tbe));
assert(is_valid(cache_entry));
tbe.NumValidBlocks := tbe.Sharers.count();
}
action(sno_setNumAcksOne, "sno", desc="Set number of acks to one per shared region buffer") {
assert(is_valid(tbe));
assert(is_valid(cache_entry));
tbe.NumValidBlocks := 1;
}
action(dt_deallocateTBE, "dt", desc="deallocate TBE Entry") {
TBEs.deallocate(getRegionBase(address));
APPEND_TRANSITION_COMMENT(" reg: ");
APPEND_TRANSITION_COMMENT(getRegionBase(address));
unset_tbe();
}
action(wb_sendWbNotice, "wb", desc="Send notice to cache that writeback is acknowledged") {
peek(requestNetwork_in, CPURequestMsg) {
enqueue(notifyNetwork_out, CPURequestMsg, 1) {
out_msg.addr := getRegionBase(address);
out_msg.Type := CoherenceRequestType:WbNotify;
out_msg.Destination.add(in_msg.Requestor);
out_msg.Requestor := machineID;
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
}
}
}
action(wbn_sendWbNoticeNoAck, "wbn", desc="Send notice to cache that writeback is acknowledged (no ack needed)") {
peek(requestNetwork_in, CPURequestMsg) {
enqueue(notifyNetwork_out, CPURequestMsg, 1) {
out_msg.addr := getRegionBase(address);
out_msg.Type := CoherenceRequestType:WbNotify;
out_msg.Destination.add(in_msg.Requestor);
out_msg.Requestor := machineID;
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.NoAckNeeded := true;
}
}
}
action(b_sendPrivateNotice, "b", desc="Send notice to private cache that it has private access") {
peek(requestNetwork_in, CPURequestMsg) {
enqueue(notifyNetwork_out, CPURequestMsg, 1) {
out_msg.addr := getRegionBase(address);
out_msg.Type := CoherenceRequestType:PrivateNotify;
out_msg.Destination.add(in_msg.Requestor);
out_msg.Requestor := machineID;
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
}
}
}
action(bs_sendSharedNotice, "bs", desc="Send notice to private cache that it has private access") {
peek(requestNetwork_in, CPURequestMsg) {
enqueue(notifyNetwork_out, CPURequestMsg, 1) {
out_msg.addr := getRegionBase(address);
out_msg.Type := CoherenceRequestType:SharedNotify;
out_msg.Destination.add(in_msg.Requestor);
out_msg.Requestor := machineID;
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
}
}
}
action(c_sendSharedNoticeToOrigReq, "c", desc="Send notice to private cache that it has shared access") {
assert(is_valid(tbe));
enqueue(notifyNetwork_out, CPURequestMsg, 1) {
out_msg.addr := getRegionBase(address);
out_msg.Type := CoherenceRequestType:SharedNotify;
out_msg.Destination.add(tbe.Owner);
out_msg.Requestor := machineID;
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.ProbeRequestStartTime := tbe.ProbeRequestTime;
out_msg.InitialRequestTime := tbe.InitialRequestTime;
APPEND_TRANSITION_COMMENT("dest: ");
APPEND_TRANSITION_COMMENT(out_msg.Destination);
}
}
action(sp_sendPrivateNoticeToOrigReq, "sp", desc="Send notice to private cache that it has private access") {
assert(is_valid(tbe));
enqueue(notifyNetwork_out, CPURequestMsg, 1) {
out_msg.addr := getRegionBase(address);
out_msg.Type := CoherenceRequestType:PrivateNotify;
out_msg.Destination.add(tbe.Owner);
out_msg.Requestor := machineID;
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.ProbeRequestStartTime := tbe.ProbeRequestTime;
out_msg.InitialRequestTime := tbe.InitialRequestTime;
APPEND_TRANSITION_COMMENT("dest: ");
APPEND_TRANSITION_COMMENT(out_msg.Destination);
}
}
action(i_RegionInvNotify, "i", desc="Send notice to private cache that it no longer has private access") {
enqueue(probeNetwork_out, NBProbeRequestMsg, 1) {
out_msg.addr := address;
out_msg.DemandAddress := tbe.DemandAddress;
//out_msg.Requestor := tbe.Requestor;
out_msg.Requestor := machineID;
out_msg.Type := ProbeRequestType:PrbInv;
//Fix me: assert(tbe.Sharers.count() > 0);
out_msg.DemandRequest := true;
out_msg.Destination := tbe.Sharers;
out_msg.MessageSize := MessageSizeType:Request_Control;
APPEND_TRANSITION_COMMENT("dest: ");
APPEND_TRANSITION_COMMENT(out_msg.Destination);
}
}
action(i0_RegionInvNotifyDemand0, "i0", desc="Send notice to private cache that it no longer has private access") {
enqueue(probeNetwork_out, NBProbeRequestMsg, 1) {
out_msg.addr := address;
// Demand address should default to 0 -> out_msg.DemandAddress := 0;
out_msg.Requestor := machineID;
out_msg.Type := ProbeRequestType:PrbInv;
out_msg.Destination := tbe.Sharers;
out_msg.MessageSize := MessageSizeType:Request_Control;
APPEND_TRANSITION_COMMENT("dest: ");
APPEND_TRANSITION_COMMENT(out_msg.Destination);
}
}
action(rd_RegionDowngrade, "rd", desc="Send notice to private cache that it only has shared access") {
enqueue(probeNetwork_out, NBProbeRequestMsg, 1) {
out_msg.addr := address;
out_msg.DemandAddress := tbe.DemandAddress;
out_msg.Requestor := machineID;
out_msg.Type := ProbeRequestType:PrbDowngrade;
out_msg.DemandRequest := true;
out_msg.Destination := tbe.Sharers;
out_msg.MessageSize := MessageSizeType:Request_Control;
APPEND_TRANSITION_COMMENT("dest: ");
APPEND_TRANSITION_COMMENT(out_msg.Destination);
}
}
action(p_popRequestQueue, "p", desc="Pop the request queue") {
requestNetwork_in.dequeue(clockEdge());
}
action(pt_popTriggerQueue, "pt", desc="Pop the trigger queue") {
triggerQueue_in.dequeue(clockEdge());
}
action(pr_popResponseQueue, "pr", desc="Pop the response queue") {
responseNetwork_in.dequeue(clockEdge());
}
action(s_stallAndWaitRequest, "s", desc="Stall and wait on the region address") {
Addr regAddr := getRegionBase(address);
stall_and_wait(requestNetwork_in, regAddr);
}
action(w_wakeUpRegionDependents, "w", desc="Wake up any requests waiting for this region") {
wakeUpBuffers(getRegionBase(address));
}
action(wa_wakeUpAllDependents, "wa", desc="Wake up any requests waiting for this region") {
wakeUpAllBuffers();
}
action(zz_recycleRequestQueue, "\z", desc="...") {
requestNetwork_in.recycle(clockEdge(), cyclesToTicks(recycle_latency));
}
action(z_stall, "z", desc="stall request queue") {
// fake state
}
action(mru_setMRU, "mru", desc="set MRU") {
cacheMemory.setMRU(address);
}
// Transistions
transition({NP_P, P_P, NP_S, S_S, S_P, P_S, P_NP, S_AP, P_AS, P_AP, SP_NP_W, S_W, P_AP_W, P_AS_W, S_AP_W}, {PrivateRequest, SharedRequest, UpgradeRequest, SendInv, SendUpgrade, SendDowngrade, CleanWbRequest, CleanWbRequest_LastSharer, StaleCleanWbRequest}) {
s_stallAndWaitRequest
}
transition({NP_P, P_P, NP_S, S_S, S_P, S_W, P_S, P_NP, S_AP, P_AS, P_AP, P_AP_W, P_AS_W, S_AP_W}, Evict) {
zz_recycleRequestQueue;
}
transition(NP, {PrivateRequest, SendUpgrade}, NP_P) {TagArrayRead, TagArrayWrite} {
a_allocateRegionEntry;
ur_updateDirtyStatusOnRequest;
f_fwdReqToDir;
b_sendPrivateNotice;
so_setOwner;
ss_setSharers;
t_allocateTBE;
p_popRequestQueue;
}
transition(P, {PrivateRequest, UpgradeRequest}, P_P) {TagArrayRead} {
mru_setMRU;
ur_updateDirtyStatusOnRequest;
f_fwdReqToDir;
b_sendPrivateNotice;
t_allocateTBE;
p_popRequestQueue;
}
transition({NP_P, P_P}, CPUPrivateAck, P) {
dt_deallocateTBE;
w_wakeUpRegionDependents;
pr_popResponseQueue;
}
transition({NP, P, S}, StaleCleanWbRequest) {TagArrayRead, TagArrayWrite} {
wbn_sendWbNoticeNoAck;
ud_updateDirtyStatusWithWb;
p_popRequestQueue;
}
transition(NP, SharedRequest, NP_S) {TagArrayRead, TagArrayWrite} {
a_allocateRegionEntry;
ur_updateDirtyStatusOnRequest;
f_fwdReqToDirShared;
bs_sendSharedNotice;
so_setOwner;
ss_setSharers;
t_allocateTBE;
p_popRequestQueue;
}
// Could probably do this in parallel with other shared requests
transition(S, SharedRequest, S_S) {TagArrayRead, TagArrayWrite} {
mru_setMRU;
ur_updateDirtyStatusOnRequest;
f_fwdReqToDirShared;
bs_sendSharedNotice;
ss_setSharers;
t_allocateTBE;
p_popRequestQueue;
}
transition({P, S}, CleanWbRequest_LastSharer, SP_NP_W) {TagArrayRead, TagArrayWrite} {
ud_updateDirtyStatusWithWb;
wb_sendWbNotice;
rs_removeSharer;
t_allocateTBE;
d_deallocateRegionEntry;
p_popRequestQueue;
}
transition(S, CleanWbRequest, S_W) {TagArrayRead, TagArrayWrite} {
ud_updateDirtyStatusWithWb;
wb_sendWbNotice;
rs_removeSharer;
t_allocateTBE;
p_popRequestQueue;
}
transition(SP_NP_W, WritebackAck, NP) {
dt_deallocateTBE;
w_wakeUpRegionDependents;
pr_popResponseQueue;
}
transition(S_W, WritebackAck, S) {
dt_deallocateTBE;
w_wakeUpRegionDependents;
pr_popResponseQueue;
}
transition({NP_S, S_S}, CPUPrivateAck, S) {
dt_deallocateTBE;
w_wakeUpRegionDependents;
pr_popResponseQueue;
}
transition(S, UpgradeRequest, S_P) {TagArrayRead, TagArrayWrite} {
mru_setMRU;
ur_updateDirtyStatusOnRequest;
f_fwdReqToDir;
b_sendPrivateNotice;
so_setOwner;
t_allocateTBE;
p_popRequestQueue;
}
transition(S_P, CPUPrivateAck, P) {
dt_deallocateTBE;
w_wakeUpRegionDependents;
pr_popResponseQueue;
}
transition(P, SendInv, P_AP_W) {TagArrayRead, TagArrayWrite} {
mru_setMRU;
ur_updateDirtyStatusOnRequest;
f_fwdReqToDirWithAck;
so_setOwner;
t_allocateTBE;
rr_removeRequestorFromTBE;
sns_setNumAcksSharers;
cs_clearSharers;
ss_setSharers;
//i_RegionInvNotify;
p_popRequestQueue;
}
transition({P_AP_W, S_AP_W}, DirReadyAck) {
ti_triggerInv;
pr_popResponseQueue;
}
transition(P_AS_W, DirReadyAck) {
td_triggerDowngrade;
pr_popResponseQueue;
}
transition(P_AS_W, TriggerDowngrade, P_AS) {
rd_RegionDowngrade;
pt_popTriggerQueue;
}
transition(P_AP_W, TriggerInv, P_AP) {
i_RegionInvNotify;
pt_popTriggerQueue;
}
transition(S_AP_W, TriggerInv, S_AP) {
i_RegionInvNotify;
pt_popTriggerQueue;
}
transition(P, SendUpgrade, P_AP_W) {TagArrayRead, TagArrayWrite} {
mru_setMRU;
ur_updateDirtyStatusOnRequest;
f_fwdReqToDirWithAck;
so_setOwner;
t_allocateTBE;
rr_removeRequestorFromTBE;
sns_setNumAcksSharers;
cs_clearSharers;
ss_setSharers;
p_popRequestQueue;
}
transition(P, Evict, P_NP) {TagArrayRead, TagArrayWrite} {
t_allocateTBE;
sns_setNumAcksSharers;
i0_RegionInvNotifyDemand0;
d_deallocateRegionEntry;
}
transition(S, SendInv, P_AP_W) {TagArrayRead, TagArrayWrite} {
mru_setMRU;
ur_updateDirtyStatusOnRequest;
f_fwdReqToDirWithAck;
so_setOwner;
t_allocateTBE;
rr_removeRequestorFromTBE;
sns_setNumAcksSharers;
cs_clearSharers;
ss_setSharers;
p_popRequestQueue;
}
transition(S, Evict, P_NP) {TagArrayRead, TagArrayWrite} {
t_allocateTBE;
sns_setNumAcksSharers;
i0_RegionInvNotifyDemand0;
d_deallocateRegionEntry;
}
transition(P_NP, LastAck, NP) {
dt_deallocateTBE;
wa_wakeUpAllDependents;
pt_popTriggerQueue;
}
transition(S, SendUpgrade, S_AP_W) {TagArrayRead, TagArrayWrite} {
mru_setMRU;
ur_updateDirtyStatusOnRequest;
f_fwdReqToDirWithAck;
so_setOwner;
t_allocateTBE;
rr_removeRequestorFromTBE;
sns_setNumAcksSharers;
cs_clearSharers;
ss_setSharers;
p_popRequestQueue;
}
transition(S_AP, LastAck, S_P) {
sp_sendPrivateNoticeToOrigReq;
pt_popTriggerQueue;
}
transition(P_AP, LastAck, P_P) {
sp_sendPrivateNoticeToOrigReq;
pt_popTriggerQueue;
}
transition(P, SendDowngrade, P_AS_W) {TagArrayRead, TagArrayWrite} {
mru_setMRU;
ur_updateDirtyStatusOnRequest;
f_fwdReqToDirWithAckShared;
so_setOwner;
t_allocateTBE;
sns_setNumAcksSharers;
ss_setSharers; //why do we set the sharers before sending the downgrade? Are we sending a downgrade to the requestor?
p_popRequestQueue;
}
transition(P_AS, LastAck, P_S) {
c_sendSharedNoticeToOrigReq;
pt_popTriggerQueue;
}
transition(P_S, CPUPrivateAck, S) {
dt_deallocateTBE;
w_wakeUpRegionDependents;
pr_popResponseQueue;
}
transition({P_NP, P_AS, S_AP, P_AP}, InvAckCore) {} {
ra_receiveAck;
pr_popResponseQueue;
}
transition({P_NP, S_AP, P_AP}, InvAckCoreNoShare) {} {
ra_receiveAck;
pr_popResponseQueue;
}
transition(P_AS, InvAckCoreNoShare) {} {
ra_receiveAck;
rsr_removeSharerResponse;
pr_popResponseQueue;
}
}