| /* | |
| * Copyright (c) 2010-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:RegionBuffer, "Region Buffer for AMD_Base-like protocol") | |
| : CacheMemory *cacheMemory; // stores only region addresses. Must set block size same as below | |
| bool isOnCPU; | |
| int blocksPerRegion := 64; // 4k regions | |
| Cycles toDirLatency := 5; // Latency to fwd requests to directory | |
| Cycles toRegionDirLatency := 5; // Latency for requests and acks to directory | |
| Cycles nextEvictLatency := 1; // latency added between each block while evicting region | |
| bool noTCCdir := "False"; | |
| int TCC_select_num_bits := 1; | |
| // From the Cores | |
| MessageBuffer * requestFromCore, network="From", virtual_network="0", vnet_type="request"; | |
| MessageBuffer * responseFromCore, network="From", virtual_network="2", vnet_type="response"; | |
| // Requests to the cores or directory | |
| MessageBuffer * requestToNetwork, network="To", virtual_network="0", vnet_type="request"; | |
| // From Region-Dir | |
| MessageBuffer * notifyFromRegionDir, network="From", virtual_network="7", vnet_type="request"; | |
| MessageBuffer * probeFromRegionDir, network="From", virtual_network="8", vnet_type="request"; | |
| // From the directory | |
| MessageBuffer * unblockFromDir, network="From", virtual_network="4", vnet_type="unblock"; | |
| // To the region-Dir | |
| MessageBuffer * responseToRegDir, network="To", virtual_network="2", vnet_type="response"; | |
| MessageBuffer * triggerQueue; | |
| { | |
| // States | |
| state_declaration(State, desc="Region states", default="RegionBuffer_State_NP") { | |
| NP, AccessPermission:Invalid, desc="Not present in region directory"; | |
| P, AccessPermission:Invalid, desc="Region is private to the cache"; | |
| S, AccessPermission:Invalid, desc="Region is possibly shared with others"; | |
| NP_PS, AccessPermission:Invalid, desc="Intermediate state waiting for notify from r-dir"; | |
| S_P, AccessPermission:Invalid, desc="Intermediate state while upgrading region"; | |
| P_NP, AccessPermission:Invalid, desc="Intermediate state while evicting all lines in region"; | |
| P_S, AccessPermission:Invalid, desc="Intermediate state while downgrading all lines in region"; | |
| S_NP_PS, AccessPermission:Invalid, desc="Got an inv in S_P, waiting for all inv acks, then going to since the write is already out there NP_PS"; | |
| P_NP_NP, AccessPermission:Invalid, desc="Evicting region on repl, then got an inv. Need to re-evict"; | |
| P_NP_O, AccessPermission:Invalid, desc="Waiting for all outstanding requests"; | |
| P_S_O, AccessPermission:Invalid, desc="Waiting for all outstanding requests"; | |
| S_O, AccessPermission:Invalid, desc="Waiting for all outstanding requests"; | |
| S_NP_PS_O, AccessPermission:Invalid, desc="Waiting for all outstanding requests"; | |
| SS_P, AccessPermission:Invalid, desc="Waiting for CPU write that we know is there"; | |
| P_NP_W, AccessPermission:Invalid, desc="Waiting for writeback ack"; | |
| NP_W, AccessPermission:Invalid, desc="Got a done ack before request, waiting for that victim"; | |
| } | |
| enumeration(Event, desc="Region directory events") { | |
| CPURead, desc="Access from CPU core"; | |
| CPUWrite, desc="Access from CPU core"; | |
| CPUWriteback, desc="Writeback request from CPU core"; | |
| ReplRegion, desc="Start a replace on a region"; | |
| PrivateNotify, desc="Update entry to private state"; | |
| SharedNotify, desc="Update entry to shared state"; | |
| WbNotify, desc="Writeback notification received"; | |
| InvRegion, desc="Start invalidating a region"; | |
| DowngradeRegion,desc="Start invalidating a region"; | |
| InvAck, desc="Ack from core"; | |
| DoneAck, desc="Ack from core that request has finished"; | |
| AllOutstanding, desc="All outstanding requests have now finished"; | |
| Evict, desc="Loopback to evict each block"; | |
| LastAck_PrbResp, desc="Done eviciting all the blocks, got the last ack from core, now respond to region dir"; | |
| LastAck_CleanWb, desc="Done eviciting all the blocks, got the last ack from core, now start clean writeback (note the dir has already been updated)"; | |
| StallAccess, desc="Wait for the done ack on the address before proceeding"; | |
| StallDoneAck, desc="Wait for the access on the address before proceeding"; | |
| StaleRequest, desc="Got a stale victim from the cache, fwd it without incrementing outstanding"; | |
| } | |
| enumeration(RequestType, desc="To communicate stats from transitions to recordStats") { | |
| TagArrayRead, desc="Read the data array"; | |
| TagArrayWrite, desc="Write the data array"; | |
| } | |
| structure(BoolVec, external="yes") { | |
| bool at(int); | |
| void resize(int); | |
| void clear(); | |
| int size(); | |
| } | |
| structure(Entry, desc="Region entry", interface="AbstractCacheEntry") { | |
| Addr addr, desc="Base address of this region"; | |
| State RegionState, desc="Region state"; | |
| DataBlock DataBlk, desc="Data for the block (always empty in region buffer)"; | |
| BoolVec ValidBlocks, desc="A vector to keep track of valid blocks"; | |
| int NumValidBlocks, desc="Number of trues in ValidBlocks to avoid iterating"; | |
| BoolVec UsedBlocks, desc="A vector to keep track of blocks ever valid"; | |
| bool dirty, desc="Dirty as best known by the region buffer"; | |
| // This is needed so we don't ack an invalidate until all requests are ordered | |
| int NumOutstandingReqs, desc="Total outstanding private/shared requests"; | |
| BoolVec OutstandingReqs, desc="Blocks that have outstanding private/shared requests"; | |
| bool MustDowngrade, desc="Set when we got a downgrade before the shd or pvt permissions"; | |
| Cycles ProbeRequestTime, default="Cycles(0)", desc="Time region dir started the probe"; | |
| Cycles InitialRequestTime, default="Cycles(0)", desc="Time message was sent to region dir"; | |
| bool MsgSentToDir, desc="True if the current request required a message to the dir"; | |
| bool clearOnDone, default="false", desc="clear valid bit when request completes"; | |
| Addr clearOnDoneAddr, desc="clear valid bit when request completes"; | |
| } | |
| structure(TBE, desc="...") { | |
| State TBEState, desc="Transient state"; | |
| //int NumValidBlocks, desc="Number of blocks valid so we don't have to count a BoolVec"; | |
| BoolVec ValidBlocks, desc="A vector to keep track of valid blocks"; | |
| bool AllAcksReceived, desc="Got all necessary acks from dir"; | |
| bool DoneEvicting, desc="Done iterating through blocks checking for valids"; | |
| BoolVec AcksReceived, desc="Received acks for theses blocks\n"; | |
| bool SendAck, desc="If true, send an ack to the r-dir at end of inv"; | |
| ProbeRequestType MsgType, desc="Type of message to send while 'evicting' "; | |
| int NumOutstandingReqs, desc="Total outstanding private/shared requests"; | |
| BoolVec OutstandingReqs, desc="Blocks that have outstanding private/shared requests"; | |
| MachineID Requestor, desc="Requestor for three hop transactions"; | |
| bool DemandRequest, default="false", desc="Associated with a demand request"; | |
| Addr DemandAddress, desc="Address for the demand request"; | |
| bool DoneAckReceived, default="false", desc="True if the done ack arrived before the message"; | |
| Addr DoneAckAddr, desc="Address of the done ack received early"; | |
| int OutstandingThreshold, desc="Number of outstanding requests to trigger AllOutstanding on"; | |
| ProbeRequestType NewMsgType, desc="Type of message to send while 'evicting' "; | |
| MachineID NewRequestor, desc="Requestor for three hop transactions"; | |
| bool NewDemandRequest, default="false", desc="Associated with a demand request"; | |
| Addr NewDemandAddress, desc="Address for the demand request"; | |
| bool dirty, desc="dirty"; | |
| bool AllOutstandingTriggered, default="false", desc="bit for only one all outstanding"; | |
| int OutstandingAcks, default="0", desc="number of acks to wait for"; | |
| } | |
| structure(TBETable, external="yes") { | |
| TBE lookup(Addr); | |
| void allocate(Addr); | |
| void deallocate(Addr); | |
| bool isPresent(Addr); | |
| } | |
| // Stores only region addresses | |
| TBETable TBEs, template="<RegionBuffer_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 | |
| 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; | |
| return makeNextStrideAddress(a, 1); | |
| } | |
| MachineID getPeer(MachineID mach, Addr address) { | |
| if (isOnCPU) { | |
| return createMachineID(MachineType:CorePair, intToID(0)); | |
| } else if (noTCCdir) { | |
| return mapAddressToRange(address,MachineType:TCC, | |
| TCC_select_low_bit, TCC_select_num_bits); | |
| } else { | |
| return createMachineID(MachineType:TCCdir, intToID(0)); | |
| } | |
| } | |
| bool isOutstanding(TBE tbe, Entry cache_entry, Addr addr) { | |
| if (is_valid(tbe) && tbe.OutstandingReqs.size() > 0) { | |
| DPRINTF(RubySlicc, " outstanding tbe reqs %s %s %d %d\n", | |
| tbe.OutstandingReqs, addr, getRegionOffset(addr), | |
| tbe.OutstandingReqs.at(getRegionOffset(addr))); | |
| return tbe.OutstandingReqs.at(getRegionOffset(addr)); | |
| } else if (is_valid(cache_entry)) { | |
| DPRINTF(RubySlicc, " outstanding cache reqs %s %s %d %d\n", | |
| cache_entry.OutstandingReqs, addr, getRegionOffset(addr), | |
| cache_entry.OutstandingReqs.at(getRegionOffset(addr))); | |
| return cache_entry.OutstandingReqs.at(getRegionOffset(addr)); | |
| } else { | |
| return false; | |
| } | |
| } | |
| bool isOnGPU() { | |
| if (isOnCPU) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool isRead(CoherenceRequestType type) { | |
| return (type == CoherenceRequestType:RdBlk || type == CoherenceRequestType:RdBlkS || | |
| type == CoherenceRequestType:VicClean); | |
| } | |
| bool presentOrAvail(Addr 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 RegionBuffer_State_to_permission(tbe.TBEState); | |
| } | |
| Entry cache_entry := getCacheEntry(addr); | |
| if(is_valid(cache_entry)) { | |
| return RegionBuffer_State_to_permission(cache_entry.RegionState); | |
| } | |
| return AccessPermission:NotPresent; | |
| } | |
| void functionalRead(Addr addr, Packet *pkt) { | |
| functionalMemoryRead(pkt); | |
| } | |
| int functionalWrite(Addr addr, Packet *pkt) { | |
| if (functionalMemoryWrite(pkt)) { | |
| return 1; | |
| } else { | |
| return 0; | |
| } | |
| } | |
| void setAccessPermission(Entry cache_entry, Addr addr, State state) { | |
| if (is_valid(cache_entry)) { | |
| cache_entry.changePermission(RegionBuffer_State_to_permission(state)); | |
| } | |
| } | |
| void recordRequestType(RequestType stat, Addr addr) { | |
| if (stat == RequestType:TagArrayRead) { | |
| cacheMemory.recordRequestType(CacheRequestType:TagArrayRead, addr); | |
| } else if (stat == RequestType:TagArrayWrite) { | |
| cacheMemory.recordRequestType(CacheRequestType:TagArrayWrite, addr); | |
| } | |
| } | |
| bool checkResourceAvailable(RequestType request_type, Addr addr) { | |
| 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); | |
| // Overloaded outgoing request nework for both probes to cores and reqeusts | |
| // to the directory. | |
| // Fix Me: These forwarded requests need to be on a separate virtual channel | |
| // to avoid deadlock! | |
| out_port(requestNetwork_out, CPURequestMsg, requestToNetwork); | |
| out_port(probeNetwork_out, NBProbeRequestMsg, requestToNetwork); | |
| out_port(responseNetwork_out, ResponseMsg, responseToRegDir); | |
| in_port(triggerQueue_in, TriggerMsg, triggerQueue, rank=4) { | |
| if (triggerQueue_in.isReady(clockEdge())) { | |
| peek(triggerQueue_in, TriggerMsg) { | |
| 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)); | |
| assert(is_valid(tbe)); | |
| if (in_msg.Type == TriggerType:AcksComplete) { | |
| if (tbe.SendAck) { | |
| trigger(Event:LastAck_PrbResp, in_msg.addr, cache_entry, tbe); | |
| } else { | |
| trigger(Event:LastAck_CleanWb, in_msg.addr, cache_entry, tbe); | |
| } | |
| } else if (in_msg.Type == TriggerType:AllOutstanding) { | |
| trigger(Event:AllOutstanding, in_msg.addr, cache_entry, tbe); | |
| } else { | |
| assert(in_msg.Type == TriggerType:InvNext); | |
| trigger(Event:Evict, in_msg.addr, cache_entry, tbe); | |
| } | |
| } | |
| } | |
| } | |
| in_port(unblockNetwork_in, UnblockMsg, unblockFromDir, rank=3) { | |
| if (unblockNetwork_in.isReady(clockEdge())) { | |
| peek(unblockNetwork_in, UnblockMsg) { | |
| TBE tbe := getTBE(in_msg.addr); | |
| Entry cache_entry := getCacheEntry(in_msg.addr); | |
| if (in_msg.DoneAck) { | |
| if (isOutstanding(tbe, cache_entry, in_msg.addr)) { | |
| trigger(Event:DoneAck, in_msg.addr, cache_entry, tbe); | |
| } else { | |
| trigger(Event:StallDoneAck, in_msg.addr, cache_entry, tbe); | |
| } | |
| } else { | |
| assert(is_valid(tbe)); | |
| trigger(Event:InvAck, in_msg.addr, cache_entry, tbe); | |
| } | |
| } | |
| } | |
| } | |
| in_port(probeNetwork_in, NBProbeRequestMsg, probeFromRegionDir, rank=2) { | |
| if (probeNetwork_in.isReady(clockEdge())) { | |
| peek(probeNetwork_in, NBProbeRequestMsg) { | |
| TBE tbe := getTBE(in_msg.addr); | |
| Entry cache_entry := getCacheEntry(in_msg.addr); | |
| assert(getRegionBase(in_msg.addr) == in_msg.addr); | |
| if (in_msg.Type == ProbeRequestType:PrbInv) { | |
| trigger(Event:InvRegion, in_msg.addr, cache_entry, tbe); | |
| } else if (in_msg.Type == ProbeRequestType:PrbDowngrade) { | |
| trigger(Event:DowngradeRegion, in_msg.addr, cache_entry, tbe); | |
| } else { | |
| error("Unknown probe message\n"); | |
| } | |
| } | |
| } | |
| } | |
| in_port(notifyNetwork_in, CPURequestMsg, notifyFromRegionDir, rank=1) { | |
| if (notifyNetwork_in.isReady(clockEdge())) { | |
| peek(notifyNetwork_in, CPURequestMsg) { | |
| TBE tbe := getTBE(in_msg.addr); | |
| Entry cache_entry := getCacheEntry(in_msg.addr); | |
| //Fix Me...add back in: assert(is_valid(cache_entry)); | |
| if (in_msg.Type == CoherenceRequestType:WbNotify) { | |
| trigger(Event:WbNotify, in_msg.addr, cache_entry, tbe); | |
| } else if (in_msg.Type == CoherenceRequestType:SharedNotify) { | |
| trigger(Event:SharedNotify, in_msg.addr, cache_entry, tbe); | |
| } else if (in_msg.Type == CoherenceRequestType:PrivateNotify) { | |
| trigger(Event:PrivateNotify, in_msg.addr, cache_entry, tbe); | |
| } else { | |
| error("Unknown notify message\n"); | |
| } | |
| } | |
| } | |
| } | |
| // 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, requestFromCore, rank=0) { | |
| if (requestNetwork_in.isReady(clockEdge())) { | |
| peek(requestNetwork_in, CPURequestMsg) { | |
| TBE tbe := getTBE(in_msg.addr); | |
| Entry cache_entry := getCacheEntry(in_msg.addr); | |
| if (is_valid(tbe) && tbe.DoneAckReceived && tbe.DoneAckAddr == in_msg.addr) { | |
| DPRINTF(RubySlicc, "Stale/Stall request %s\n", in_msg.Type); | |
| if (in_msg.Type == CoherenceRequestType:VicDirty || in_msg.Type == CoherenceRequestType:VicClean ) | |
| { | |
| trigger(Event:StaleRequest, in_msg.addr, cache_entry, tbe); | |
| } else { | |
| trigger(Event:StallAccess, in_msg.addr, cache_entry, tbe); | |
| } | |
| } else if (isOutstanding(tbe, cache_entry, in_msg.addr)) { | |
| DPRINTF(RubySlicc, "Stall outstanding request %s\n", in_msg.Type); | |
| trigger(Event:StallAccess, in_msg.addr, cache_entry, tbe); | |
| } else { | |
| if (presentOrAvail(in_msg.addr)) { | |
| if (in_msg.Type == CoherenceRequestType:RdBlkM ) { | |
| trigger(Event:CPUWrite, in_msg.addr, cache_entry, tbe); | |
| } else if (in_msg.Type == CoherenceRequestType:WriteThrough ) { | |
| trigger(Event:CPUWrite, in_msg.addr, cache_entry, tbe); | |
| } else if (in_msg.Type == CoherenceRequestType:Atomic ) { | |
| trigger(Event:CPUWrite, in_msg.addr, cache_entry, tbe); | |
| } else { | |
| if (in_msg.Type == CoherenceRequestType:VicDirty || | |
| in_msg.Type == CoherenceRequestType:VicClean) { | |
| trigger(Event:CPUWriteback, in_msg.addr, cache_entry, tbe); | |
| } else { | |
| trigger(Event:CPURead, in_msg.addr, cache_entry, tbe); | |
| } | |
| } | |
| } else { | |
| Addr victim := cacheMemory.cacheProbe(getRegionBase(in_msg.addr)); | |
| TBE victim_tbe := getTBE(victim); | |
| Entry victim_entry := getCacheEntry(victim); | |
| DPRINTF(RubySlicc, "Replacing region %s for %s(%s)\n", victim, in_msg.addr, getRegionBase(in_msg.addr)); | |
| trigger(Event:ReplRegion, 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; | |
| out_msg.Type := in_msg.Type; | |
| out_msg.DataBlk := in_msg.DataBlk; | |
| out_msg.Dirty := in_msg.Dirty; | |
| out_msg.Requestor := in_msg.Requestor; | |
| 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 := true; | |
| out_msg.InitialRequestTime := curCycle(); | |
| out_msg.ProbeRequestStartTime := curCycle(); | |
| if (getState(tbe, cache_entry, address) == State:S) { | |
| out_msg.ForceShared := true; | |
| } | |
| DPRINTF(RubySlicc, "Fwd: %s\n", out_msg); | |
| //assert(getState(tbe, cache_entry, address) == State:P || getState(tbe, cache_entry, address) == State:S); | |
| if (getState(tbe, cache_entry, address) == State:NP_W) { | |
| APPEND_TRANSITION_COMMENT(" fwding stale request: "); | |
| APPEND_TRANSITION_COMMENT(out_msg.Type); | |
| } | |
| } | |
| } | |
| } | |
| action(u_updateRegionEntry, "u", desc="Update the entry for profiling") { | |
| peek(requestNetwork_in, CPURequestMsg) { | |
| if (is_valid(cache_entry)) { | |
| if (in_msg.CtoDSinked == false) { | |
| APPEND_TRANSITION_COMMENT(" incr outstanding "); | |
| cache_entry.NumOutstandingReqs := 1 + cache_entry.NumOutstandingReqs; | |
| assert(cache_entry.OutstandingReqs.at(getRegionOffset(address)) == false); | |
| cache_entry.OutstandingReqs.at(getRegionOffset(address)) := true; | |
| assert(cache_entry.NumOutstandingReqs == countBoolVec(cache_entry.OutstandingReqs)); | |
| } else { | |
| APPEND_TRANSITION_COMMENT(" NOT incr outstanding "); | |
| assert(in_msg.Type == CoherenceRequestType:RdBlkM || in_msg.Type == CoherenceRequestType:RdBlkS); | |
| } | |
| APPEND_TRANSITION_COMMENT(cache_entry.NumOutstandingReqs); | |
| if (in_msg.Type == CoherenceRequestType:RdBlkM || in_msg.Type == CoherenceRequestType:Atomic || | |
| in_msg.Type == CoherenceRequestType:WriteThrough ) | |
| { | |
| cache_entry.dirty := true; | |
| } | |
| if (in_msg.Type == CoherenceRequestType:VicDirty || | |
| in_msg.Type == CoherenceRequestType:VicClean) { | |
| DPRINTF(RubySlicc, "Got %s for addr %s\n", in_msg.Type, address); | |
| //assert(cache_entry.ValidBlocks.at(getRegionOffset(address))); | |
| // can in fact be inv if core got an inv after a vicclean before it got here | |
| if (cache_entry.ValidBlocks.at(getRegionOffset(address))) { | |
| cache_entry.clearOnDone := true; | |
| cache_entry.clearOnDoneAddr := address; | |
| //cache_entry.ValidBlocks.at(getRegionOffset(address)) := false; | |
| //cache_entry.NumValidBlocks := cache_entry.NumValidBlocks - 1; | |
| } | |
| } else { | |
| if (cache_entry.ValidBlocks.at(getRegionOffset(address)) == false) { | |
| cache_entry.NumValidBlocks := cache_entry.NumValidBlocks + 1; | |
| } | |
| DPRINTF(RubySlicc, "before valid addr %s bits %s\n", | |
| in_msg.Type, address, cache_entry.ValidBlocks); | |
| cache_entry.ValidBlocks.at(getRegionOffset(address)) := true; | |
| DPRINTF(RubySlicc, "after valid addr %s bits %s\n", | |
| in_msg.Type, address, cache_entry.ValidBlocks); | |
| cache_entry.UsedBlocks.at(getRegionOffset(address)) := true; | |
| } | |
| assert(cache_entry.NumValidBlocks <= blocksPerRegion); | |
| assert(cache_entry.NumValidBlocks >= 0); | |
| APPEND_TRANSITION_COMMENT(" valid blocks "); | |
| APPEND_TRANSITION_COMMENT(cache_entry.ValidBlocks); | |
| } else { | |
| error("This shouldn't happen anymore I think"); | |
| //tbe.ValidBlocks.at(getRegionOffest(address)) := true; | |
| assert(getState(tbe, cache_entry, address) == State:P_NP); | |
| } | |
| } | |
| } | |
| action(uw_updatePossibleWriteback, "uw", desc="writeback request complete") { | |
| peek(unblockNetwork_in, UnblockMsg) { | |
| if (is_valid(cache_entry) && in_msg.validToInvalid && | |
| cache_entry.clearOnDone && cache_entry.clearOnDoneAddr == address) { | |
| DPRINTF(RubySlicc, "I have no idea what is going on here\n"); | |
| cache_entry.ValidBlocks.at(getRegionOffset(address)) := false; | |
| cache_entry.NumValidBlocks := cache_entry.NumValidBlocks - 1; | |
| cache_entry.clearOnDone := false; | |
| } | |
| } | |
| } | |
| action(rp_requestPrivate, "rp", desc="Send private request r-dir") { | |
| peek(requestNetwork_in, CPURequestMsg) { | |
| // No need to send acks on replacements | |
| assert(is_invalid(tbe)); | |
| enqueue(requestNetwork_out, CPURequestMsg, toRegionDirLatency) { | |
| out_msg.addr := address; // use the actual address so the demand request can be fulfilled | |
| out_msg.DemandAddress := address; | |
| out_msg.Type := CoherenceRequestType:PrivateRequest; | |
| out_msg.OriginalType := in_msg.Type; | |
| out_msg.Requestor := machineID; | |
| out_msg.WTRequestor := in_msg.WTRequestor; | |
| out_msg.InitialRequestTime := curCycle(); | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:RegionDir)); | |
| out_msg.MessageSize := MessageSizeType:Request_Control; | |
| DPRINTF(RubySlicc, "Private request %s\n", out_msg); | |
| } | |
| cache_entry.ProbeRequestTime := curCycle(); | |
| cache_entry.MsgSentToDir := true; | |
| APPEND_TRANSITION_COMMENT(getRegionBase(address)); | |
| } | |
| } | |
| action(ru_requestUpgrade, "ru", desc="Send upgrade request r-dir") { | |
| peek(requestNetwork_in, CPURequestMsg) { | |
| // No need to send acks on replacements | |
| assert(is_invalid(tbe)); | |
| enqueue(requestNetwork_out, CPURequestMsg, toRegionDirLatency) { | |
| out_msg.addr := address; // use the actual address so the demand request can be fulfilled | |
| out_msg.Type := CoherenceRequestType:UpgradeRequest; | |
| out_msg.OriginalType := in_msg.Type; | |
| out_msg.Requestor := machineID; | |
| out_msg.WTRequestor := in_msg.WTRequestor; | |
| out_msg.InitialRequestTime := curCycle(); | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:RegionDir)); | |
| out_msg.MessageSize := MessageSizeType:Request_Control; | |
| } | |
| cache_entry.ProbeRequestTime := curCycle(); | |
| cache_entry.MsgSentToDir := true; | |
| APPEND_TRANSITION_COMMENT(getRegionBase(address)); | |
| } | |
| } | |
| action(rw_requestWriteback, "rq", desc="Send writeback request") { | |
| // No need to send acks on replacements | |
| enqueue(requestNetwork_out, CPURequestMsg, toRegionDirLatency) { | |
| out_msg.addr := getRegionBase(address); // use the actual address so the demand request can be fulfilled | |
| out_msg.Type := CoherenceRequestType:CleanWbRequest; | |
| out_msg.Requestor := machineID; | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:RegionDir)); | |
| out_msg.MessageSize := MessageSizeType:Request_Control; | |
| out_msg.Dirty := tbe.dirty; | |
| APPEND_TRANSITION_COMMENT(getRegionBase(address)); | |
| } | |
| } | |
| action(rs_requestShared, "rs", desc="Send shared request r-dir") { | |
| peek(requestNetwork_in, CPURequestMsg) { | |
| // No need to send acks on replacements | |
| assert(is_invalid(tbe)); | |
| enqueue(requestNetwork_out, CPURequestMsg, toRegionDirLatency) { | |
| out_msg.addr := address; // use the actual address so the demand request can be fulfilled | |
| out_msg.Type := CoherenceRequestType:SharedRequest; | |
| out_msg.OriginalType := in_msg.Type; | |
| out_msg.Requestor := machineID; | |
| out_msg.WTRequestor := in_msg.WTRequestor; | |
| out_msg.InitialRequestTime := curCycle(); | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:RegionDir)); | |
| out_msg.MessageSize := MessageSizeType:Request_Control; | |
| } | |
| cache_entry.ProbeRequestTime := curCycle(); | |
| cache_entry.MsgSentToDir := true; | |
| APPEND_TRANSITION_COMMENT(getRegionBase(address)); | |
| } | |
| } | |
| action(ai_ackRegionInv, "ai", desc="Send ack to r-dir on region inv if tbe says so") { | |
| // No need to send acks on replacements | |
| assert(is_valid(tbe)); | |
| enqueue(responseNetwork_out, ResponseMsg, toRegionDirLatency) { | |
| out_msg.addr := getRegionBase(address); | |
| out_msg.Type := CoherenceResponseType:CPUPrbResp; | |
| out_msg.Sender := machineID; | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:RegionDir)); | |
| out_msg.MessageSize := MessageSizeType:Response_Control; | |
| } | |
| } | |
| action(ad_ackDircetory, "ad", desc="send probe response to directory") { | |
| if (noTCCdir && tbe.MsgType == ProbeRequestType:PrbDowngrade && isOnGPU()) { //VIPER tcc doesnt understand PrbShrData | |
| assert(tbe.DemandRequest); //So, let RegionBuffer take care of sending back ack | |
| enqueue(responseNetwork_out, ResponseMsg, toDirLatency) { | |
| out_msg.addr := tbe.DemandAddress; | |
| out_msg.Type := CoherenceResponseType:CPUPrbResp; // L3 and CPUs respond in same way to probes | |
| out_msg.Sender := getPeer(machineID,address); | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); | |
| out_msg.Dirty := false; // only true if sending back data i think | |
| out_msg.Hit := false; | |
| out_msg.Ntsl := false; | |
| out_msg.State := CoherenceState:NA; | |
| out_msg.NoAckNeeded := true; | |
| out_msg.MessageSize := MessageSizeType:Response_Control; | |
| DPRINTF(RubySlicc, "%s\n", out_msg); | |
| } | |
| } | |
| } | |
| action(aie_ackRegionExclusiveInv, "aie", desc="Send ack to r-dir on region inv if tbe says so") { | |
| // No need to send acks on replacements | |
| assert(is_valid(tbe)); | |
| enqueue(responseNetwork_out, ResponseMsg, toRegionDirLatency) { | |
| out_msg.addr := getRegionBase(address); | |
| out_msg.Type := CoherenceResponseType:CPUPrbResp; | |
| out_msg.Sender := machineID; | |
| out_msg.NotCached := true; | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:RegionDir)); | |
| out_msg.MessageSize := MessageSizeType:Response_Control; | |
| out_msg.Dirty := tbe.dirty; | |
| } | |
| } | |
| action(ain_ackRegionInvNow, "ain", desc="Send ack to r-dir on region inv") { | |
| enqueue(responseNetwork_out, ResponseMsg, toRegionDirLatency) { | |
| out_msg.addr := getRegionBase(address); | |
| out_msg.Type := CoherenceResponseType:CPUPrbResp; | |
| out_msg.Sender := machineID; | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:RegionDir)); | |
| out_msg.MessageSize := MessageSizeType:Response_Control; | |
| } | |
| } | |
| action(aine_ackRegionInvExlusiveNow, "aine", desc="Send ack to r-dir on region inv with exlusive permission") { | |
| enqueue(responseNetwork_out, ResponseMsg, toRegionDirLatency) { | |
| out_msg.addr := getRegionBase(address); | |
| out_msg.Type := CoherenceResponseType:CPUPrbResp; | |
| out_msg.Sender := machineID; | |
| out_msg.NotCached := true; | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:RegionDir)); | |
| out_msg.MessageSize := MessageSizeType:Response_Control; | |
| } | |
| } | |
| action(ap_ackPrivateNotify, "ap", desc="Send ack to r-dir on private notify") { | |
| enqueue(responseNetwork_out, ResponseMsg, toRegionDirLatency) { | |
| out_msg.addr := getRegionBase(address); | |
| out_msg.Type := CoherenceResponseType:PrivateAck; | |
| out_msg.Sender := machineID; | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:RegionDir)); | |
| out_msg.MessageSize := MessageSizeType:Response_Control; | |
| } | |
| } | |
| action(aw_ackWbNotify, "aw", desc="Send ack to r-dir on writeback notify") { | |
| peek(notifyNetwork_in, CPURequestMsg) { | |
| if (in_msg.NoAckNeeded == false) { | |
| enqueue(responseNetwork_out, ResponseMsg, toRegionDirLatency) { | |
| out_msg.addr := getRegionBase(address); | |
| out_msg.Type := CoherenceResponseType:RegionWbAck; | |
| out_msg.Sender := machineID; | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:RegionDir)); | |
| out_msg.MessageSize := MessageSizeType:Response_Control; | |
| } | |
| } | |
| } | |
| } | |
| action(e_evictCurrent, "e", desc="Evict this block in the region") { | |
| // send force invalidate message to directory to invalidate this block | |
| // must invalidate all blocks since region buffer could have privitized it | |
| if (tbe.ValidBlocks.at(getRegionOffset(address)) && | |
| (tbe.DemandRequest == false || tbe.DemandAddress != address)) { | |
| DPRINTF(RubySlicc, "trying to evict address %s (base: %s, offset: %d)\n", address, getRegionBase(address), getRegionOffset(address)); | |
| DPRINTF(RubySlicc, "tbe valid blocks %s\n", tbe.ValidBlocks); | |
| enqueue(probeNetwork_out, NBProbeRequestMsg, 1) { | |
| out_msg.addr := address; | |
| out_msg.Type := tbe.MsgType; | |
| out_msg.ReturnData := true; | |
| if (address == tbe.DemandAddress) { | |
| out_msg.DemandRequest := true; | |
| } | |
| out_msg.MessageSize := MessageSizeType:Control; | |
| out_msg.Destination.add(getPeer(machineID,address)); | |
| DPRINTF(RubySlicc, "%s\n", out_msg); | |
| } | |
| APPEND_TRANSITION_COMMENT(" current "); | |
| APPEND_TRANSITION_COMMENT(tbe.ValidBlocks.at(getRegionOffset(address))); | |
| tbe.AllAcksReceived := false; | |
| } else { | |
| DPRINTF(RubySlicc, "Not evicting demand %s\n", address); | |
| } | |
| } | |
| action(ed_evictDemand, "ed", desc="Evict the demand request if it's valid") { | |
| if (noTCCdir && tbe.MsgType == ProbeRequestType:PrbDowngrade && isOnGPU()) { | |
| tbe.OutstandingAcks := 0; | |
| tbe.AllAcksReceived := true; | |
| tbe.DoneEvicting := true; | |
| enqueue(triggerQueue_out, TriggerMsg, 1) { | |
| out_msg.Type := TriggerType:AcksComplete; | |
| out_msg.addr := getRegionBase(address); | |
| } | |
| } else if (tbe.DemandRequest) { | |
| enqueue(probeNetwork_out, NBProbeRequestMsg, 1) { | |
| out_msg.addr := tbe.DemandAddress; | |
| out_msg.Type := tbe.MsgType; | |
| out_msg.ReturnData := true; | |
| out_msg.DemandRequest := true; | |
| out_msg.MessageSize := MessageSizeType:Control; | |
| out_msg.Destination.add(getPeer(machineID,address)); | |
| DPRINTF(RubySlicc, "%s\n", out_msg); | |
| tbe.AllAcksReceived := false; | |
| } | |
| if (tbe.ValidBlocks.at(getRegionOffset(tbe.DemandAddress)) == false) { | |
| tbe.OutstandingAcks := tbe.OutstandingAcks + 1; | |
| } | |
| APPEND_TRANSITION_COMMENT("Evicting demand "); | |
| APPEND_TRANSITION_COMMENT(tbe.DemandAddress); | |
| } | |
| APPEND_TRANSITION_COMMENT("waiting acks "); | |
| APPEND_TRANSITION_COMMENT(tbe.OutstandingAcks); | |
| } | |
| action(adp_AckDemandProbe, "fp", desc="forward demand probe even if we know that the core is invalid") { | |
| peek(probeNetwork_in, NBProbeRequestMsg) { | |
| if (in_msg.DemandRequest) { | |
| enqueue(responseNetwork_out, ResponseMsg, toDirLatency) { | |
| out_msg.addr := in_msg.DemandAddress; | |
| out_msg.Type := CoherenceResponseType:CPUPrbResp; // L3 and CPUs respond in same way to probes | |
| out_msg.Sender := getPeer(machineID,address); | |
| // will this always be ok? probably not for multisocket | |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); | |
| out_msg.Dirty := false; // only true if sending back data i think | |
| out_msg.Hit := false; | |
| out_msg.Ntsl := false; | |
| out_msg.State := CoherenceState:NA; | |
| out_msg.NoAckNeeded := true; | |
| out_msg.MessageSize := MessageSizeType:Response_Control; | |
| DPRINTF(RubySlicc, "%s\n", out_msg); | |
| } | |
| } | |
| } | |
| } | |
| action(en_enqueueNextEvict, "en", desc="Queue evict the next block in the region") { | |
| // increment in_msg.addr by blockSize bytes and enqueue on triggerPort | |
| // Only enqueue if the next address doesn't overrun the region bound | |
| if (getRegionBase(getNextBlock(address)) == getRegionBase(address)) { | |
| enqueue(triggerQueue_out, TriggerMsg, nextEvictLatency) { | |
| out_msg.Type := TriggerType:InvNext; | |
| out_msg.addr := getNextBlock(address); | |
| } | |
| } else { | |
| tbe.DoneEvicting := true; | |
| DPRINTF(RubySlicc, "Done evicing region %s\n", getRegionBase(address)); | |
| DPRINTF(RubySlicc, "Waiting for %s acks\n", tbe.OutstandingAcks); | |
| if (tbe.AllAcksReceived == true) { | |
| enqueue(triggerQueue_out, TriggerMsg, 1) { | |
| out_msg.Type := TriggerType:AcksComplete; | |
| out_msg.addr := getRegionBase(address); | |
| } | |
| } | |
| } | |
| } | |
| action(ef_enqueueFirstEvict, "ef", desc="Queue the first block in the region to be evicted") { | |
| if (tbe.DoneEvicting == false) { | |
| enqueue(triggerQueue_out, TriggerMsg, nextEvictLatency) { | |
| out_msg.Type := TriggerType:InvNext; | |
| out_msg.addr := getRegionBase(address); | |
| } | |
| } | |
| } | |
| action(ra_receiveAck, "ra", desc="Mark TBE entry as received this ack") { | |
| DPRINTF(RubySlicc, "received ack for %s reg: %s vec: %s pos: %d\n", | |
| address, getRegionBase(address), tbe.ValidBlocks, getRegionOffset(address)); | |
| peek(unblockNetwork_in, UnblockMsg) { | |
| // | |
| // Note the tbe ValidBlock vec will be a conservative list of the | |
| // valid blocks since the cache entry ValidBlock vec is set on the | |
| // request | |
| // | |
| if (in_msg.wasValid) { | |
| assert(tbe.ValidBlocks.at(getRegionOffset(address))); | |
| } | |
| } | |
| tbe.OutstandingAcks := tbe.OutstandingAcks - 1; | |
| tbe.AcksReceived.at(getRegionOffset(address)) := true; | |
| assert(tbe.OutstandingAcks >= 0); | |
| if (tbe.OutstandingAcks == 0) { | |
| tbe.AllAcksReceived := true; | |
| if (tbe.DoneEvicting) { | |
| enqueue(triggerQueue_out, TriggerMsg, 1) { | |
| out_msg.Type := TriggerType:AcksComplete; | |
| out_msg.addr := getRegionBase(address); | |
| } | |
| } | |
| } | |
| APPEND_TRANSITION_COMMENT(getRegionBase(address)); | |
| APPEND_TRANSITION_COMMENT(" Acks left receive "); | |
| APPEND_TRANSITION_COMMENT(tbe.OutstandingAcks); | |
| } | |
| action(do_decrementOutstanding, "do", desc="Decrement outstanding requests") { | |
| APPEND_TRANSITION_COMMENT(" decr outstanding "); | |
| if (is_valid(cache_entry)) { | |
| cache_entry.NumOutstandingReqs := cache_entry.NumOutstandingReqs - 1; | |
| assert(cache_entry.OutstandingReqs.at(getRegionOffset(address))); | |
| cache_entry.OutstandingReqs.at(getRegionOffset(address)) := false; | |
| assert(cache_entry.NumOutstandingReqs >= 0); | |
| assert(cache_entry.NumOutstandingReqs == countBoolVec(cache_entry.OutstandingReqs)); | |
| APPEND_TRANSITION_COMMENT(cache_entry.NumOutstandingReqs); | |
| } | |
| if (is_valid(tbe)) { | |
| tbe.NumOutstandingReqs := tbe.NumOutstandingReqs - 1; | |
| assert(tbe.OutstandingReqs.at(getRegionOffset(address))); | |
| tbe.OutstandingReqs.at(getRegionOffset(address)) := false; | |
| assert(tbe.NumOutstandingReqs >= 0); | |
| assert(tbe.NumOutstandingReqs == countBoolVec(tbe.OutstandingReqs)); | |
| APPEND_TRANSITION_COMMENT(tbe.NumOutstandingReqs); | |
| } | |
| } | |
| action(co_checkOutstanding, "co", desc="check if there are no more outstanding requests") { | |
| assert(is_valid(tbe)); | |
| if ((tbe.NumOutstandingReqs <= tbe.OutstandingThreshold) && | |
| (tbe.AllOutstandingTriggered == false)) { | |
| APPEND_TRANSITION_COMMENT(" no more outstanding: "); | |
| APPEND_TRANSITION_COMMENT(tbe.NumOutstandingReqs); | |
| APPEND_TRANSITION_COMMENT(tbe.OutstandingThreshold); | |
| enqueue(triggerQueue_out, TriggerMsg, 1) { | |
| out_msg.Type := TriggerType:AllOutstanding; | |
| if (tbe.DemandRequest) { | |
| out_msg.addr := tbe.DemandAddress; | |
| } else { | |
| out_msg.addr := getRegionBase(address); | |
| } | |
| DPRINTF(RubySlicc, "co enqueuing %s\n", out_msg); | |
| tbe.AllOutstandingTriggered := true; | |
| } | |
| } else { | |
| APPEND_TRANSITION_COMMENT(" still more outstanding "); | |
| } | |
| } | |
| action(ro_resetAllOutstanding, "ro", desc="Reset all outstanding") { | |
| tbe.AllOutstandingTriggered := false; | |
| } | |
| action(so_setOutstandingCheckOne, "so", desc="Check outstanding is waiting for 1, not 0") { | |
| // Need this for S_P because one request is outstanding between here and r-dir | |
| tbe.OutstandingThreshold := 1; | |
| } | |
| action(a_allocateRegionEntry, "a", desc="Allocate a new entry") { | |
| set_cache_entry(cacheMemory.allocate(getRegionBase(address), new Entry)); | |
| cache_entry.ValidBlocks.clear(); | |
| cache_entry.ValidBlocks.resize(blocksPerRegion); | |
| cache_entry.UsedBlocks.clear(); | |
| cache_entry.UsedBlocks.resize(blocksPerRegion); | |
| cache_entry.dirty := false; | |
| cache_entry.NumOutstandingReqs := 0; | |
| cache_entry.OutstandingReqs.clear(); | |
| cache_entry.OutstandingReqs.resize(blocksPerRegion); | |
| } | |
| action(d_deallocateRegionEntry, "d", desc="Deallocate region entry") { | |
| cacheMemory.deallocate(getRegionBase(address)); | |
| unset_cache_entry(); | |
| } | |
| action(t_allocateTBE, "t", desc="allocate TBE Entry") { | |
| check_allocate(TBEs); | |
| TBEs.allocate(getRegionBase(address)); | |
| set_tbe(getTBE(address)); | |
| tbe.OutstandingAcks := 0; | |
| tbe.AllAcksReceived := true; // starts true since the region could be empty | |
| tbe.DoneEvicting := false; | |
| tbe.AcksReceived.clear(); | |
| tbe.AcksReceived.resize(blocksPerRegion); | |
| tbe.SendAck := false; | |
| tbe.OutstandingThreshold := 0; | |
| if (is_valid(cache_entry)) { | |
| tbe.NumOutstandingReqs := cache_entry.NumOutstandingReqs; | |
| tbe.OutstandingReqs := cache_entry.OutstandingReqs; | |
| assert(tbe.NumOutstandingReqs == countBoolVec(tbe.OutstandingReqs)); | |
| tbe.dirty := cache_entry.dirty; | |
| tbe.ValidBlocks := cache_entry.ValidBlocks; | |
| tbe.OutstandingAcks := countBoolVec(tbe.ValidBlocks); | |
| APPEND_TRANSITION_COMMENT(" tbe valid blocks "); | |
| APPEND_TRANSITION_COMMENT(tbe.ValidBlocks); | |
| APPEND_TRANSITION_COMMENT(" cache valid blocks "); | |
| APPEND_TRANSITION_COMMENT(cache_entry.ValidBlocks); | |
| } else { | |
| tbe.dirty := false; | |
| } | |
| } | |
| action(m_markSendAck, "m", desc="Mark TBE that we need to ack at end") { | |
| assert(is_valid(tbe)); | |
| tbe.SendAck := true; | |
| } | |
| action(db_markDirtyBit, "db", desc="Mark TBE dirty bit") { | |
| peek(unblockNetwork_in, UnblockMsg) { | |
| if (is_valid(tbe)) { | |
| tbe.dirty := tbe.dirty || in_msg.Dirty; | |
| } | |
| } | |
| } | |
| action(dr_markDoneAckReceived, "dr", desc="Mark TBE that a done ack has been received") { | |
| assert(is_valid(tbe)); | |
| tbe.DoneAckReceived := true; | |
| tbe.DoneAckAddr := address; | |
| APPEND_TRANSITION_COMMENT(" marking done ack on TBE "); | |
| } | |
| action(se_setTBE, "se", desc="Set msg type to evict") { | |
| peek(probeNetwork_in, NBProbeRequestMsg) { | |
| tbe.MsgType := in_msg.Type; | |
| tbe.Requestor := in_msg.Requestor; | |
| tbe.DemandAddress := in_msg.DemandAddress; | |
| tbe.DemandRequest := in_msg.DemandRequest; | |
| } | |
| } | |
| action(sne_setNewTBE, "sne", desc="Set msg type to evict") { | |
| peek(probeNetwork_in, NBProbeRequestMsg) { | |
| tbe.NewMsgType := in_msg.Type; | |
| tbe.NewRequestor := in_msg.Requestor; | |
| tbe.NewDemandAddress := in_msg.DemandAddress; | |
| tbe.NewDemandRequest := in_msg.DemandRequest; | |
| } | |
| } | |
| action(soe_setOldTBE, "soe", desc="Set msg type to evict") { | |
| tbe.MsgType := tbe.NewMsgType; | |
| tbe.Requestor := tbe.NewRequestor; | |
| tbe.DemandAddress := tbe.NewDemandAddress; | |
| tbe.DemandRequest := tbe.NewDemandRequest; | |
| tbe.OutstandingAcks := countBoolVec(tbe.ValidBlocks); | |
| tbe.AllAcksReceived := true; // starts true since the region could be empty | |
| tbe.DoneEvicting := false; | |
| tbe.AcksReceived.clear(); | |
| tbe.AcksReceived.resize(blocksPerRegion); | |
| tbe.SendAck := false; | |
| } | |
| action(ser_setTBE, "ser", desc="Set msg type to evict repl") { | |
| tbe.MsgType := ProbeRequestType:PrbInv; | |
| } | |
| action(md_setMustDowngrade, "md", desc="When permissions finally get here, must be shared") { | |
| assert(is_valid(cache_entry)); | |
| cache_entry.MustDowngrade := true; | |
| } | |
| action(dt_deallocateTBE, "dt", desc="deallocate TBE Entry") { | |
| TBEs.deallocate(getRegionBase(address)); | |
| unset_tbe(); | |
| } | |
| action(p_popRequestQueue, "p", desc="Pop the request queue") { | |
| requestNetwork_in.dequeue(clockEdge()); | |
| } | |
| action(pl_popUnblockQueue, "pl", desc="Pop the unblock queue") { | |
| unblockNetwork_in.dequeue(clockEdge()); | |
| } | |
| action(pn_popNotifyQueue, "pn", desc="Pop the notify queue") { | |
| notifyNetwork_in.dequeue(clockEdge()); | |
| } | |
| action(pp_popProbeQueue, "pp", desc="Pop the probe queue") { | |
| probeNetwork_in.dequeue(clockEdge()); | |
| } | |
| action(pt_popTriggerQueue, "pt", desc="Pop the trigger queue") { | |
| DPRINTF(RubySlicc, "Trigger Before Contents: %s\n", triggerQueue_in); | |
| triggerQueue_in.dequeue(clockEdge()); | |
| DPRINTF(RubySlicc, "Trigger After Contents: %s\n", triggerQueue_in); | |
| } | |
| // Must always use wake all, since non-region address wait on region addresses | |
| action(wa_wakeUpAllDependents, "wa", desc="Wake up any requests waiting for this region") { | |
| wakeUpAllBuffers(); | |
| } | |
| action(zz_stallAndWaitRequestQueue, "\z", desc="recycle request queue") { | |
| Addr regAddr := getRegionBase(address); | |
| DPRINTF(RubySlicc, "Stalling address %s\n", regAddr); | |
| stall_and_wait(requestNetwork_in, regAddr); | |
| } | |
| action(yy_stallAndWaitProbeQueue, "\y", desc="stall probe queue") { | |
| Addr regAddr := getRegionBase(address); | |
| stall_and_wait(probeNetwork_in, regAddr); | |
| } | |
| action(yyy_recycleProbeQueue, "\yy", desc="recycle probe queue") { | |
| probeNetwork_in.recycle(clockEdge(), cyclesToTicks(recycle_latency)); | |
| } | |
| action(zzz_recycleRequestQueue, "\zz", desc="recycle request queue") { | |
| requestNetwork_in.recycle(clockEdge(), cyclesToTicks(recycle_latency)); | |
| } | |
| action(www_recycleUnblockNetwork, "\ww", desc="recycle unblock queue") { | |
| unblockNetwork_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, cache_entry.NumValidBlocks); | |
| } | |
| // Transitions | |
| transition({NP_PS, S_P, S_NP_PS, P_NP, P_S, P_NP_O, S_NP_PS_O, P_S_O, S_O, P_NP_W, P_NP_NP, NP_W}, {CPURead, CPUWriteback, CPUWrite}) {} { | |
| zz_stallAndWaitRequestQueue; | |
| } | |
| transition(SS_P, {CPURead, CPUWriteback}) { | |
| zz_stallAndWaitRequestQueue; | |
| } | |
| transition({NP, S, P, NP_PS, S_P, S_NP_PS, P_NP, P_S, P_NP_O, S_NP_PS_O, P_S_O, S_O, SS_P, NP_W, P_NP_NP}, StallAccess) {} { | |
| zz_stallAndWaitRequestQueue; | |
| } | |
| transition({S, P, NP_PS, S_P, S_NP_PS, P_NP, P_S, P_NP_O, S_NP_PS_O, P_S_O, S_O, SS_P, P_NP_W, P_NP_NP, NP_W}, StallDoneAck) { | |
| www_recycleUnblockNetwork; | |
| } | |
| transition(NP, StallDoneAck, NP_W) { | |
| t_allocateTBE; | |
| db_markDirtyBit; | |
| dr_markDoneAckReceived; | |
| pl_popUnblockQueue; | |
| } | |
| transition(NP_W, StaleRequest, NP) { | |
| f_fwdReqToDir; | |
| dt_deallocateTBE; | |
| wa_wakeUpAllDependents; | |
| p_popRequestQueue; | |
| } | |
| transition(P_NP_O, DowngradeRegion) {} { | |
| z_stall; // should stall and wait | |
| } | |
| transition({NP_PS, S_NP_PS, S_P, P_S, P_NP_O, S_NP_PS_O, P_S_O, S_O, SS_P}, ReplRegion) {} { | |
| zz_stallAndWaitRequestQueue; // can't let things get out of order! | |
| } | |
| transition({P_NP_O, S_O, SS_P}, InvRegion) {} { | |
| yyy_recycleProbeQueue; // can't be z_stall because there could be a RdBlkM in the requestQueue which has the sinked flag which is blocking the inv | |
| } | |
| transition(P_NP, {InvRegion, DowngradeRegion}, P_NP_NP) {} { | |
| sne_setNewTBE; | |
| pp_popProbeQueue; | |
| } | |
| transition(S_P, DowngradeRegion) {} { | |
| adp_AckDemandProbe; | |
| ain_ackRegionInvNow; | |
| pp_popProbeQueue; | |
| } | |
| transition(P_NP_W, InvRegion) { | |
| adp_AckDemandProbe; | |
| ain_ackRegionInvNow; | |
| pp_popProbeQueue; | |
| } | |
| transition(P_NP_W, DowngradeRegion) { | |
| adp_AckDemandProbe; | |
| aine_ackRegionInvExlusiveNow; | |
| pp_popProbeQueue; | |
| } | |
| transition({P, S}, {CPURead, CPUWriteback}) {TagArrayRead, TagArrayWrite} { | |
| mru_setMRU; | |
| f_fwdReqToDir; | |
| u_updateRegionEntry; | |
| p_popRequestQueue; | |
| } | |
| transition(P, CPUWrite) {TagArrayRead, TagArrayWrite} { | |
| mru_setMRU; | |
| f_fwdReqToDir; | |
| u_updateRegionEntry; | |
| p_popRequestQueue; | |
| } | |
| transition(S, CPUWrite, S_O) {TagArrayRead} { | |
| mru_setMRU; | |
| t_allocateTBE; | |
| co_checkOutstanding; | |
| zz_stallAndWaitRequestQueue; | |
| } | |
| transition(S_O, AllOutstanding, SS_P) { | |
| wa_wakeUpAllDependents; | |
| ro_resetAllOutstanding; | |
| pt_popTriggerQueue; | |
| } | |
| transition(SS_P, CPUWrite, S_P) { | |
| mru_setMRU; | |
| dt_deallocateTBE; | |
| ru_requestUpgrade; | |
| u_updateRegionEntry; | |
| p_popRequestQueue; | |
| } | |
| transition(NP, {CPURead, CPUWriteback}, NP_PS) {TagArrayRead, TagArrayWrite} { | |
| a_allocateRegionEntry; | |
| rs_requestShared; | |
| u_updateRegionEntry; | |
| p_popRequestQueue;//zz_stallAndWaitRequestQueue; | |
| } | |
| transition(NP, CPUWrite, NP_PS) {TagArrayRead, TagArrayWrite} { | |
| a_allocateRegionEntry; | |
| rp_requestPrivate; | |
| u_updateRegionEntry; | |
| p_popRequestQueue;//zz_stallAndWaitRequestQueue; | |
| } | |
| transition(NP_PS, PrivateNotify, P) {} { | |
| ap_ackPrivateNotify; | |
| wa_wakeUpAllDependents; | |
| pn_popNotifyQueue; | |
| } | |
| transition(S_P, PrivateNotify, P) {} { | |
| ap_ackPrivateNotify; | |
| wa_wakeUpAllDependents; | |
| pn_popNotifyQueue; | |
| } | |
| transition(NP_PS, SharedNotify, S) {} { | |
| ap_ackPrivateNotify; | |
| wa_wakeUpAllDependents; | |
| pn_popNotifyQueue; | |
| } | |
| transition(P_NP_W, WbNotify, NP) {} { | |
| aw_ackWbNotify; | |
| wa_wakeUpAllDependents; | |
| dt_deallocateTBE; | |
| pn_popNotifyQueue; | |
| } | |
| transition({P, S}, ReplRegion, P_NP_O) {TagArrayRead, TagArrayWrite} { | |
| t_allocateTBE; | |
| ser_setTBE; | |
| d_deallocateRegionEntry; | |
| co_checkOutstanding; | |
| } | |
| transition({P, S}, InvRegion, P_NP_O) {TagArrayRead, TagArrayWrite} { | |
| t_allocateTBE; | |
| se_setTBE; | |
| m_markSendAck; | |
| d_deallocateRegionEntry; | |
| co_checkOutstanding; | |
| pp_popProbeQueue; | |
| } | |
| transition(P_NP_O, AllOutstanding, P_NP) {} { | |
| ed_evictDemand; | |
| ef_enqueueFirstEvict; | |
| ro_resetAllOutstanding; | |
| pt_popTriggerQueue; | |
| } | |
| transition(S_P, InvRegion, S_NP_PS_O) {TagArrayRead} { | |
| t_allocateTBE; | |
| se_setTBE; | |
| m_markSendAck; | |
| so_setOutstandingCheckOne; | |
| co_checkOutstanding; | |
| pp_popProbeQueue; | |
| } | |
| transition(S_NP_PS_O, AllOutstanding, S_NP_PS) { | |
| ed_evictDemand; | |
| ef_enqueueFirstEvict; | |
| ro_resetAllOutstanding; | |
| pt_popTriggerQueue; | |
| } | |
| transition(P, DowngradeRegion, P_S_O) {TagArrayRead, TagArrayWrite} { | |
| t_allocateTBE; | |
| se_setTBE; | |
| m_markSendAck; | |
| co_checkOutstanding; | |
| pp_popProbeQueue; | |
| } | |
| transition(P_S_O, AllOutstanding, P_S) {} { | |
| ed_evictDemand; | |
| ef_enqueueFirstEvict; | |
| ro_resetAllOutstanding; | |
| pt_popTriggerQueue; | |
| } | |
| transition({P, S}, DoneAck) {TagArrayWrite} { | |
| do_decrementOutstanding; | |
| wa_wakeUpAllDependents; | |
| db_markDirtyBit; | |
| uw_updatePossibleWriteback; | |
| pl_popUnblockQueue; | |
| } | |
| transition({S_P, NP_PS, S_NP_PS}, DoneAck) {TagArrayWrite} { | |
| www_recycleUnblockNetwork; | |
| } | |
| transition({P_NP_O, S_NP_PS_O, P_S_O, S_O}, DoneAck) {} { | |
| do_decrementOutstanding; | |
| co_checkOutstanding; | |
| db_markDirtyBit; | |
| uw_updatePossibleWriteback; | |
| pl_popUnblockQueue; | |
| } | |
| transition({P_NP, P_S, S_NP_PS, P_NP_NP}, Evict) {} { | |
| e_evictCurrent; | |
| en_enqueueNextEvict; | |
| pt_popTriggerQueue; | |
| } | |
| transition({P_NP, P_S, S_NP_PS, P_NP_NP}, InvAck) {} { | |
| ra_receiveAck; | |
| db_markDirtyBit; | |
| pl_popUnblockQueue; | |
| } | |
| transition(P_NP, LastAck_CleanWb, P_NP_W) {} { | |
| rw_requestWriteback; | |
| pt_popTriggerQueue; | |
| } | |
| transition(P_NP_NP, LastAck_CleanWb, P_NP) {} { | |
| soe_setOldTBE; | |
| m_markSendAck; | |
| ed_evictDemand; | |
| ef_enqueueFirstEvict; | |
| pt_popTriggerQueue; | |
| } | |
| transition(P_NP, LastAck_PrbResp, NP) {} { | |
| aie_ackRegionExclusiveInv; | |
| dt_deallocateTBE; | |
| wa_wakeUpAllDependents; | |
| pt_popTriggerQueue; | |
| } | |
| transition(S_NP_PS, LastAck_PrbResp, NP_PS) {} { | |
| aie_ackRegionExclusiveInv; | |
| dt_deallocateTBE; | |
| wa_wakeUpAllDependents; | |
| pt_popTriggerQueue; | |
| } | |
| transition(P_S, LastAck_PrbResp, S) {} { | |
| ai_ackRegionInv; | |
| ad_ackDircetory; | |
| dt_deallocateTBE; | |
| wa_wakeUpAllDependents; | |
| pt_popTriggerQueue; | |
| } | |
| } | |