/* * 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="", 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; } }