| /* |
| * Copyright (c) 1999-2013 Mark D. Hill and David A. Wood |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * 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; |
| * neither the name of the copyright holders 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 |
| * OWNER 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. |
| */ |
| |
| /* |
| * $Id: MOESI_CMP_token-L1cache.sm 1.22 05/01/19 15:55:39-06:00 beckmann@s0-28.cs.wisc.edu $ |
| * |
| */ |
| |
| machine(L1Cache, "Token protocol") |
| : Sequencer * sequencer, |
| CacheMemory * L1Icache, |
| CacheMemory * L1Dcache, |
| int l2_select_num_bits, |
| int N_tokens, |
| |
| Cycles l1_request_latency = 2, |
| Cycles l1_response_latency = 2, |
| int retry_threshold = 1, |
| Cycles fixed_timeout_latency = 100, |
| Cycles reissue_wakeup_latency = 10, |
| Cycles use_timeout_latency = 50, |
| |
| bool dynamic_timeout_enabled = true, |
| bool no_mig_atomic = true, |
| bool send_evictions |
| { |
| |
| // From this node's L1 cache TO the network |
| |
| // a local L1 -> this L2 bank |
| MessageBuffer responseFromL1Cache, network="To", virtual_network="4", ordered="false", vnet_type="response"; |
| MessageBuffer persistentFromL1Cache, network="To", virtual_network="3", ordered="true", vnet_type="persistent"; |
| // a local L1 -> this L2 bank, currently ordered with directory forwarded requests |
| MessageBuffer requestFromL1Cache, network="To", virtual_network="1", ordered="false", vnet_type="request"; |
| |
| |
| // To this node's L1 cache FROM the network |
| // a L2 bank -> this L1 |
| MessageBuffer responseToL1Cache, network="From", virtual_network="4", ordered="false", vnet_type="response"; |
| MessageBuffer persistentToL1Cache, network="From", virtual_network="3", ordered="true", vnet_type="persistent"; |
| // a L2 bank -> this L1 |
| MessageBuffer requestToL1Cache, network="From", virtual_network="1", ordered="false", vnet_type="request"; |
| |
| // STATES |
| state_declaration(State, desc="Cache states", default="L1Cache_State_I") { |
| // Base states |
| NP, AccessPermission:Invalid, "NP", desc="Not Present"; |
| I, AccessPermission:Invalid, "I", desc="Idle"; |
| S, AccessPermission:Read_Only, "S", desc="Shared"; |
| O, AccessPermission:Read_Only, "O", desc="Owned"; |
| M, AccessPermission:Read_Only, "M", desc="Modified (dirty)"; |
| MM, AccessPermission:Read_Write, "MM", desc="Modified (dirty and locally modified)"; |
| M_W, AccessPermission:Read_Only, "M^W", desc="Modified (dirty), waiting"; |
| MM_W, AccessPermission:Read_Write, "MM^W", desc="Modified (dirty and locally modified), waiting"; |
| |
| // Transient States |
| IM, AccessPermission:Busy, "IM", desc="Issued GetX"; |
| SM, AccessPermission:Read_Only, "SM", desc="Issued GetX, we still have an old copy of the line"; |
| OM, AccessPermission:Read_Only, "OM", desc="Issued GetX, received data"; |
| IS, AccessPermission:Busy, "IS", desc="Issued GetS"; |
| |
| // Locked states |
| I_L, AccessPermission:Busy, "I^L", desc="Invalid, Locked"; |
| S_L, AccessPermission:Busy, "S^L", desc="Shared, Locked"; |
| IM_L, AccessPermission:Busy, "IM^L", desc="Invalid, Locked, trying to go to Modified"; |
| SM_L, AccessPermission:Busy, "SM^L", desc="Shared, Locked, trying to go to Modified"; |
| IS_L, AccessPermission:Busy, "IS^L", desc="Invalid, Locked, trying to go to Shared"; |
| } |
| |
| // EVENTS |
| enumeration(Event, desc="Cache events") { |
| Load, desc="Load request from the processor"; |
| Ifetch, desc="I-fetch request from the processor"; |
| Store, desc="Store request from the processor"; |
| Atomic, desc="Atomic request from the processor"; |
| L1_Replacement, desc="L1 Replacement"; |
| |
| // Responses |
| Data_Shared, desc="Received a data message, we are now a sharer"; |
| Data_Owner, desc="Received a data message, we are now the owner"; |
| Data_All_Tokens, desc="Received a data message, we are now the owner, we now have all the tokens"; |
| Ack, desc="Received an ack message"; |
| Ack_All_Tokens, desc="Received an ack message, we now have all the tokens"; |
| |
| // Requests |
| Transient_GETX, desc="A GetX from another processor"; |
| Transient_Local_GETX, desc="A GetX from another processor"; |
| Transient_GETS, desc="A GetS from another processor"; |
| Transient_Local_GETS, desc="A GetS from another processor"; |
| Transient_GETS_Last_Token, desc="A GetS from another processor"; |
| Transient_Local_GETS_Last_Token, desc="A GetS from another processor"; |
| |
| // Lock/Unlock for distributed |
| Persistent_GETX, desc="Another processor has priority to read/write"; |
| Persistent_GETS, desc="Another processor has priority to read"; |
| Persistent_GETS_Last_Token, desc="Another processor has priority to read, no more tokens"; |
| Own_Lock_or_Unlock, desc="This processor now has priority"; |
| |
| // Triggers |
| Request_Timeout, desc="Timeout"; |
| Use_TimeoutStarverX, desc="Timeout"; |
| Use_TimeoutStarverS, desc="Timeout"; |
| Use_TimeoutNoStarvers, desc="Timeout"; |
| Use_TimeoutNoStarvers_NoMig, desc="Timeout Don't Migrate"; |
| } |
| |
| // TYPES |
| |
| // CacheEntry |
| structure(Entry, desc="...", interface="AbstractCacheEntry") { |
| State CacheState, desc="cache state"; |
| bool Dirty, desc="Is the data dirty (different than memory)?"; |
| int Tokens, desc="The number of tokens we're holding for the line"; |
| DataBlock DataBlk, desc="data for the block"; |
| } |
| |
| |
| // TBE fields |
| structure(TBE, desc="...") { |
| Address Addr, desc="Physical address for this TBE"; |
| State TBEState, desc="Transient state"; |
| int IssueCount, default="0", desc="The number of times we've issued a request for this line."; |
| Address PC, desc="Program counter of request"; |
| |
| bool WentPersistent, default="false", desc="Request went persistent"; |
| bool ExternalResponse, default="false", desc="Response came from an external controller"; |
| bool IsAtomic, default="false", desc="Request was an atomic request"; |
| |
| AccessType TypeOfAccess, desc="Type of request (used for profiling)"; |
| Cycles IssueTime, desc="Time the request was issued"; |
| RubyAccessMode AccessMode, desc="user/supervisor access type"; |
| PrefetchBit Prefetch, desc="Is this a prefetch request"; |
| } |
| |
| structure(TBETable, external="yes") { |
| TBE lookup(Address); |
| void allocate(Address); |
| void deallocate(Address); |
| bool isPresent(Address); |
| } |
| |
| structure(PersistentTable, external="yes") { |
| void persistentRequestLock(Address, MachineID, AccessType); |
| void persistentRequestUnlock(Address, MachineID); |
| bool okToIssueStarving(Address, MachineID); |
| MachineID findSmallest(Address); |
| AccessType typeOfSmallest(Address); |
| void markEntries(Address); |
| bool isLocked(Address); |
| int countStarvingForAddress(Address); |
| int countReadStarvingForAddress(Address); |
| } |
| |
| void set_cache_entry(AbstractCacheEntry b); |
| void unset_cache_entry(); |
| void set_tbe(TBE b); |
| void unset_tbe(); |
| void wakeUpAllBuffers(); |
| void wakeUpBuffers(Address a); |
| Cycles curCycle(); |
| |
| TBETable L1_TBEs, template="<L1Cache_TBE>", constructor="m_number_of_TBEs"; |
| |
| MessageBuffer mandatoryQueue, ordered="false", abstract_chip_ptr="true"; |
| |
| bool starving, default="false"; |
| int l2_select_low_bit, default="RubySystem::getBlockSizeBits()"; |
| |
| PersistentTable persistentTable; |
| TimerTable useTimerTable; |
| TimerTable reissueTimerTable; |
| |
| int outstandingRequests, default="0"; |
| int outstandingPersistentRequests, default="0"; |
| |
| // Constant that provides hysteresis for calculated the estimated average |
| int averageLatencyHysteresis, default="(8)"; |
| Cycles averageLatencyCounter, |
| default="(Cycles(500) << (*m_L1Cache_averageLatencyHysteresis_ptr))"; |
| |
| Cycles averageLatencyEstimate() { |
| DPRINTF(RubySlicc, "%d\n", |
| (averageLatencyCounter >> averageLatencyHysteresis)); |
| //profile_average_latency_estimate( (averageLatencyCounter >> averageLatencyHysteresis) ); |
| return averageLatencyCounter >> averageLatencyHysteresis; |
| } |
| |
| void updateAverageLatencyEstimate(Cycles latency) { |
| DPRINTF(RubySlicc, "%d\n", latency); |
| |
| // By subtracting the current average and then adding the most |
| // recent sample, we calculate an estimate of the recent average. |
| // If we simply used a running sum and divided by the total number |
| // of entries, the estimate of the average would adapt very slowly |
| // after the execution has run for a long time. |
| // averageLatencyCounter := averageLatencyCounter - averageLatencyEstimate() + latency; |
| |
| averageLatencyCounter := averageLatencyCounter - averageLatencyEstimate() + latency; |
| } |
| |
| Entry getCacheEntry(Address addr), return_by_pointer="yes" { |
| Entry L1Dcache_entry := static_cast(Entry, "pointer", L1Dcache.lookup(addr)); |
| if(is_valid(L1Dcache_entry)) { |
| return L1Dcache_entry; |
| } |
| |
| Entry L1Icache_entry := static_cast(Entry, "pointer", L1Icache.lookup(addr)); |
| return L1Icache_entry; |
| } |
| |
| DataBlock getDataBlock(Address addr), return_by_ref="yes" { |
| return getCacheEntry(addr).DataBlk; |
| } |
| |
| Entry getL1DCacheEntry(Address addr), return_by_pointer="yes" { |
| Entry L1Dcache_entry := static_cast(Entry, "pointer", L1Dcache.lookup(addr)); |
| return L1Dcache_entry; |
| } |
| |
| Entry getL1ICacheEntry(Address addr), return_by_pointer="yes" { |
| Entry L1Icache_entry := static_cast(Entry, "pointer", L1Icache.lookup(addr)); |
| return L1Icache_entry; |
| } |
| |
| int getTokens(Entry cache_entry) { |
| if (is_valid(cache_entry)) { |
| return cache_entry.Tokens; |
| } |
| return 0; |
| } |
| |
| State getState(TBE tbe, Entry cache_entry, Address addr) { |
| |
| if (is_valid(tbe)) { |
| return tbe.TBEState; |
| } else if (is_valid(cache_entry)) { |
| return cache_entry.CacheState; |
| } else { |
| if (persistentTable.isLocked(addr) && (persistentTable.findSmallest(addr) != machineID)) { |
| // Not in cache, in persistent table, but this processor isn't highest priority |
| return State:I_L; |
| } else { |
| return State:NP; |
| } |
| } |
| } |
| |
| void setState(TBE tbe, Entry cache_entry, Address addr, State state) { |
| assert((L1Dcache.isTagPresent(addr) && L1Icache.isTagPresent(addr)) == false); |
| |
| if (is_valid(tbe)) { |
| assert(state != State:I); |
| assert(state != State:S); |
| assert(state != State:O); |
| assert(state != State:MM); |
| assert(state != State:M); |
| tbe.TBEState := state; |
| } |
| |
| if (is_valid(cache_entry)) { |
| // Make sure the token count is in range |
| assert(cache_entry.Tokens >= 0); |
| assert(cache_entry.Tokens <= max_tokens()); |
| assert(cache_entry.Tokens != (max_tokens() / 2)); |
| |
| if ((state == State:I_L) || |
| (state == State:IM_L) || |
| (state == State:IS_L)) { |
| // Make sure we have no tokens in the "Invalid, locked" states |
| assert(cache_entry.Tokens == 0); |
| |
| // Make sure the line is locked |
| // assert(persistentTable.isLocked(addr)); |
| |
| // But we shouldn't have highest priority for it |
| // assert(persistentTable.findSmallest(addr) != id); |
| |
| } else if ((state == State:S_L) || |
| (state == State:SM_L)) { |
| assert(cache_entry.Tokens >= 1); |
| assert(cache_entry.Tokens < (max_tokens() / 2)); |
| |
| // Make sure the line is locked... |
| // assert(persistentTable.isLocked(addr)); |
| |
| // ...But we shouldn't have highest priority for it... |
| // assert(persistentTable.findSmallest(addr) != id); |
| |
| // ...And it must be a GETS request |
| // assert(persistentTable.typeOfSmallest(addr) == AccessType:Read); |
| |
| } else { |
| |
| // If there is an entry in the persistent table of this block, |
| // this processor needs to have an entry in the table for this |
| // block, and that entry better be the smallest (highest |
| // priority). Otherwise, the state should have been one of |
| // locked states |
| |
| //if (persistentTable.isLocked(addr)) { |
| // assert(persistentTable.findSmallest(addr) == id); |
| //} |
| } |
| |
| // in M and E you have all the tokens |
| if (state == State:MM || state == State:M || state == State:MM_W || state == State:M_W) { |
| assert(cache_entry.Tokens == max_tokens()); |
| } |
| |
| // in NP you have no tokens |
| if (state == State:NP) { |
| assert(cache_entry.Tokens == 0); |
| } |
| |
| // You have at least one token in S-like states |
| if (state == State:S || state == State:SM) { |
| assert(cache_entry.Tokens > 0); |
| } |
| |
| // You have at least half the token in O-like states |
| if (state == State:O && state == State:OM) { |
| assert(cache_entry.Tokens > (max_tokens() / 2)); |
| } |
| |
| cache_entry.CacheState := state; |
| } |
| } |
| |
| AccessPermission getAccessPermission(Address addr) { |
| TBE tbe := L1_TBEs[addr]; |
| if(is_valid(tbe)) { |
| return L1Cache_State_to_permission(tbe.TBEState); |
| } |
| |
| Entry cache_entry := getCacheEntry(addr); |
| if(is_valid(cache_entry)) { |
| return L1Cache_State_to_permission(cache_entry.CacheState); |
| } |
| |
| return AccessPermission:NotPresent; |
| } |
| |
| void setAccessPermission(Entry cache_entry, Address addr, State state) { |
| if (is_valid(cache_entry)) { |
| cache_entry.changePermission(L1Cache_State_to_permission(state)); |
| } |
| } |
| |
| Event mandatory_request_type_to_event(RubyRequestType type) { |
| if (type == RubyRequestType:LD) { |
| return Event:Load; |
| } else if (type == RubyRequestType:IFETCH) { |
| return Event:Ifetch; |
| } else if (type == RubyRequestType:ST) { |
| return Event:Store; |
| } else if (type == RubyRequestType:ATOMIC) { |
| if (no_mig_atomic) { |
| return Event:Atomic; |
| } else { |
| return Event:Store; |
| } |
| } else { |
| error("Invalid RubyRequestType"); |
| } |
| } |
| |
| AccessType cache_request_type_to_access_type(RubyRequestType type) { |
| if ((type == RubyRequestType:LD) || (type == RubyRequestType:IFETCH)) { |
| return AccessType:Read; |
| } else if ((type == RubyRequestType:ST) || (type == RubyRequestType:ATOMIC)) { |
| return AccessType:Write; |
| } else { |
| error("Invalid RubyRequestType"); |
| } |
| } |
| |
| // NOTE: direct local hits should not call this function |
| bool isExternalHit(Address addr, MachineID sender) { |
| if (machineIDToMachineType(sender) == MachineType:L1Cache) { |
| return true; |
| } else if (machineIDToMachineType(sender) == MachineType:L2Cache) { |
| |
| if (sender == mapAddressToRange(addr, MachineType:L2Cache, |
| l2_select_low_bit, l2_select_num_bits, intToID(0))) { |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool okToIssueStarving(Address addr, MachineID machineID) { |
| return persistentTable.okToIssueStarving(addr, machineID); |
| } |
| |
| void markPersistentEntries(Address addr) { |
| persistentTable.markEntries(addr); |
| } |
| |
| void setExternalResponse(TBE tbe) { |
| assert(is_valid(tbe)); |
| tbe.ExternalResponse := true; |
| } |
| |
| bool IsAtomic(TBE tbe) { |
| assert(is_valid(tbe)); |
| return tbe.IsAtomic; |
| } |
| |
| // ** OUT_PORTS ** |
| out_port(persistentNetwork_out, PersistentMsg, persistentFromL1Cache); |
| out_port(requestNetwork_out, RequestMsg, requestFromL1Cache); |
| out_port(responseNetwork_out, ResponseMsg, responseFromL1Cache); |
| out_port(requestRecycle_out, RequestMsg, requestToL1Cache); |
| |
| // ** IN_PORTS ** |
| |
| // Use Timer |
| in_port(useTimerTable_in, Address, useTimerTable, rank=5) { |
| if (useTimerTable_in.isReady()) { |
| TBE tbe := L1_TBEs[useTimerTable.readyAddress()]; |
| |
| if (persistentTable.isLocked(useTimerTable.readyAddress()) && |
| (persistentTable.findSmallest(useTimerTable.readyAddress()) != machineID)) { |
| if (persistentTable.typeOfSmallest(useTimerTable.readyAddress()) == AccessType:Write) { |
| trigger(Event:Use_TimeoutStarverX, useTimerTable.readyAddress(), |
| getCacheEntry(useTimerTable.readyAddress()), tbe); |
| } else { |
| trigger(Event:Use_TimeoutStarverS, useTimerTable.readyAddress(), |
| getCacheEntry(useTimerTable.readyAddress()), tbe); |
| } |
| } else { |
| if (no_mig_atomic && IsAtomic(tbe)) { |
| trigger(Event:Use_TimeoutNoStarvers_NoMig, useTimerTable.readyAddress(), |
| getCacheEntry(useTimerTable.readyAddress()), tbe); |
| } else { |
| trigger(Event:Use_TimeoutNoStarvers, useTimerTable.readyAddress(), |
| getCacheEntry(useTimerTable.readyAddress()), tbe); |
| } |
| } |
| } |
| } |
| |
| // Reissue Timer |
| in_port(reissueTimerTable_in, Address, reissueTimerTable, rank=4) { |
| if (reissueTimerTable_in.isReady()) { |
| trigger(Event:Request_Timeout, reissueTimerTable.readyAddress(), |
| getCacheEntry(reissueTimerTable.readyAddress()), |
| L1_TBEs[reissueTimerTable.readyAddress()]); |
| } |
| } |
| |
| // Persistent Network |
| in_port(persistentNetwork_in, PersistentMsg, persistentToL1Cache, rank=3) { |
| if (persistentNetwork_in.isReady()) { |
| peek(persistentNetwork_in, PersistentMsg, block_on="Addr") { |
| assert(in_msg.Destination.isElement(machineID)); |
| |
| // Apply the lockdown or unlockdown message to the table |
| if (in_msg.Type == PersistentRequestType:GETX_PERSISTENT) { |
| persistentTable.persistentRequestLock(in_msg.Addr, in_msg.Requestor, AccessType:Write); |
| } else if (in_msg.Type == PersistentRequestType:GETS_PERSISTENT) { |
| persistentTable.persistentRequestLock(in_msg.Addr, in_msg.Requestor, AccessType:Read); |
| } else if (in_msg.Type == PersistentRequestType:DEACTIVATE_PERSISTENT) { |
| persistentTable.persistentRequestUnlock(in_msg.Addr, in_msg.Requestor); |
| } else { |
| error("Unexpected message"); |
| } |
| |
| // React to the message based on the current state of the table |
| Entry cache_entry := getCacheEntry(in_msg.Addr); |
| TBE tbe := L1_TBEs[in_msg.Addr]; |
| |
| if (persistentTable.isLocked(in_msg.Addr)) { |
| if (persistentTable.findSmallest(in_msg.Addr) == machineID) { |
| // Our Own Lock - this processor is highest priority |
| trigger(Event:Own_Lock_or_Unlock, in_msg.Addr, |
| cache_entry, tbe); |
| } else { |
| if (persistentTable.typeOfSmallest(in_msg.Addr) == AccessType:Read) { |
| if (getTokens(cache_entry) == 1 || |
| getTokens(cache_entry) == (max_tokens() / 2) + 1) { |
| trigger(Event:Persistent_GETS_Last_Token, in_msg.Addr, |
| cache_entry, tbe); |
| } else { |
| trigger(Event:Persistent_GETS, in_msg.Addr, |
| cache_entry, tbe); |
| } |
| } else { |
| trigger(Event:Persistent_GETX, in_msg.Addr, |
| cache_entry, tbe); |
| } |
| } |
| } else { |
| // Unlock case - no entries in the table |
| trigger(Event:Own_Lock_or_Unlock, in_msg.Addr, |
| cache_entry, tbe); |
| } |
| } |
| } |
| } |
| |
| // Response Network |
| in_port(responseNetwork_in, ResponseMsg, responseToL1Cache, rank=2) { |
| if (responseNetwork_in.isReady()) { |
| peek(responseNetwork_in, ResponseMsg, block_on="Addr") { |
| assert(in_msg.Destination.isElement(machineID)); |
| |
| Entry cache_entry := getCacheEntry(in_msg.Addr); |
| TBE tbe := L1_TBEs[in_msg.Addr]; |
| |
| // Mark TBE flag if response received off-chip. Use this to update average latency estimate |
| if ( machineIDToMachineType(in_msg.Sender) == MachineType:L2Cache ) { |
| |
| if (in_msg.Sender == mapAddressToRange(in_msg.Addr, |
| MachineType:L2Cache, l2_select_low_bit, |
| l2_select_num_bits, intToID(0))) { |
| |
| // came from an off-chip L2 cache |
| if (is_valid(tbe)) { |
| // L1_TBEs[in_msg.Addr].ExternalResponse := true; |
| // profile_offchipL2_response(in_msg.Addr); |
| } |
| } |
| else { |
| // profile_onchipL2_response(in_msg.Addr ); |
| } |
| } else if ( machineIDToMachineType(in_msg.Sender) == MachineType:Directory ) { |
| if (is_valid(tbe)) { |
| setExternalResponse(tbe); |
| // profile_memory_response( in_msg.Addr); |
| } |
| } else if ( machineIDToMachineType(in_msg.Sender) == MachineType:L1Cache) { |
| //if (isLocalProcessor(machineID, in_msg.Sender) == false) { |
| //if (is_valid(tbe)) { |
| // tbe.ExternalResponse := true; |
| // profile_offchipL1_response(in_msg.Addr ); |
| //} |
| //} |
| //else { |
| // profile_onchipL1_response(in_msg.Addr ); |
| //} |
| } else { |
| error("unexpected SenderMachine"); |
| } |
| |
| |
| if (getTokens(cache_entry) + in_msg.Tokens != max_tokens()) { |
| if (in_msg.Type == CoherenceResponseType:ACK) { |
| assert(in_msg.Tokens < (max_tokens() / 2)); |
| trigger(Event:Ack, in_msg.Addr, cache_entry, tbe); |
| } else if (in_msg.Type == CoherenceResponseType:DATA_OWNER) { |
| trigger(Event:Data_Owner, in_msg.Addr, cache_entry, tbe); |
| } else if (in_msg.Type == CoherenceResponseType:DATA_SHARED) { |
| assert(in_msg.Tokens < (max_tokens() / 2)); |
| trigger(Event:Data_Shared, in_msg.Addr, cache_entry, tbe); |
| } else { |
| error("Unexpected message"); |
| } |
| } else { |
| if (in_msg.Type == CoherenceResponseType:ACK) { |
| assert(in_msg.Tokens < (max_tokens() / 2)); |
| trigger(Event:Ack_All_Tokens, in_msg.Addr, cache_entry, tbe); |
| } else if (in_msg.Type == CoherenceResponseType:DATA_OWNER || in_msg.Type == CoherenceResponseType:DATA_SHARED) { |
| trigger(Event:Data_All_Tokens, in_msg.Addr, cache_entry, tbe); |
| } else { |
| error("Unexpected message"); |
| } |
| } |
| } |
| } |
| } |
| |
| // Request Network |
| in_port(requestNetwork_in, RequestMsg, requestToL1Cache) { |
| if (requestNetwork_in.isReady()) { |
| peek(requestNetwork_in, RequestMsg, block_on="Addr") { |
| assert(in_msg.Destination.isElement(machineID)); |
| |
| Entry cache_entry := getCacheEntry(in_msg.Addr); |
| TBE tbe := L1_TBEs[in_msg.Addr]; |
| |
| if (in_msg.Type == CoherenceRequestType:GETX) { |
| if (in_msg.isLocal) { |
| trigger(Event:Transient_Local_GETX, in_msg.Addr, |
| cache_entry, tbe); |
| } |
| else { |
| trigger(Event:Transient_GETX, in_msg.Addr, |
| cache_entry, tbe); |
| } |
| } else if (in_msg.Type == CoherenceRequestType:GETS) { |
| if (getTokens(cache_entry) == 1 || |
| getTokens(cache_entry) == (max_tokens() / 2) + 1) { |
| if (in_msg.isLocal) { |
| trigger(Event:Transient_Local_GETS_Last_Token, in_msg.Addr, |
| cache_entry, tbe); |
| } |
| else { |
| trigger(Event:Transient_GETS_Last_Token, in_msg.Addr, |
| cache_entry, tbe); |
| } |
| } |
| else { |
| if (in_msg.isLocal) { |
| trigger(Event:Transient_Local_GETS, in_msg.Addr, |
| cache_entry, tbe); |
| } |
| else { |
| trigger(Event:Transient_GETS, in_msg.Addr, |
| cache_entry, tbe); |
| } |
| } |
| } else { |
| error("Unexpected message"); |
| } |
| } |
| } |
| } |
| |
| // Mandatory Queue |
| in_port(mandatoryQueue_in, RubyRequest, mandatoryQueue, desc="...", rank=0) { |
| if (mandatoryQueue_in.isReady()) { |
| peek(mandatoryQueue_in, RubyRequest, block_on="LineAddress") { |
| // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache |
| |
| TBE tbe := L1_TBEs[in_msg.LineAddress]; |
| |
| if (in_msg.Type == RubyRequestType:IFETCH) { |
| // ** INSTRUCTION ACCESS *** |
| |
| Entry L1Icache_entry := getL1ICacheEntry(in_msg.LineAddress); |
| if (is_valid(L1Icache_entry)) { |
| // The tag matches for the L1, so the L1 fetches the line. |
| // We know it can't be in the L2 due to exclusion. |
| trigger(mandatory_request_type_to_event(in_msg.Type), |
| in_msg.LineAddress, L1Icache_entry, tbe); |
| } else { |
| |
| // Check to see if it is in the OTHER L1 |
| Entry L1Dcache_entry := getL1DCacheEntry(in_msg.LineAddress); |
| if (is_valid(L1Dcache_entry)) { |
| // The block is in the wrong L1, try to write it to the L2 |
| trigger(Event:L1_Replacement, in_msg.LineAddress, |
| L1Dcache_entry, tbe); |
| } |
| |
| if (L1Icache.cacheAvail(in_msg.LineAddress)) { |
| // L1 does't have the line, but we have space for it in the L1 |
| trigger(mandatory_request_type_to_event(in_msg.Type), |
| in_msg.LineAddress, L1Icache_entry, tbe); |
| } else { |
| // No room in the L1, so we need to make room |
| trigger(Event:L1_Replacement, |
| L1Icache.cacheProbe(in_msg.LineAddress), |
| getL1ICacheEntry(L1Icache.cacheProbe(in_msg.LineAddress)), |
| L1_TBEs[L1Icache.cacheProbe(in_msg.LineAddress)]); |
| } |
| } |
| } else { |
| // *** DATA ACCESS *** |
| |
| Entry L1Dcache_entry := getL1DCacheEntry(in_msg.LineAddress); |
| if (is_valid(L1Dcache_entry)) { |
| // The tag matches for the L1, so the L1 fetches the line. |
| // We know it can't be in the L2 due to exclusion. |
| trigger(mandatory_request_type_to_event(in_msg.Type), |
| in_msg.LineAddress, L1Dcache_entry, tbe); |
| } else { |
| |
| // Check to see if it is in the OTHER L1 |
| Entry L1Icache_entry := getL1ICacheEntry(in_msg.LineAddress); |
| if (is_valid(L1Icache_entry)) { |
| // The block is in the wrong L1, try to write it to the L2 |
| trigger(Event:L1_Replacement, in_msg.LineAddress, |
| L1Icache_entry, tbe); |
| } |
| |
| if (L1Dcache.cacheAvail(in_msg.LineAddress)) { |
| // L1 does't have the line, but we have space for it in the L1 |
| trigger(mandatory_request_type_to_event(in_msg.Type), |
| in_msg.LineAddress, L1Dcache_entry, tbe); |
| } else { |
| // No room in the L1, so we need to make room |
| trigger(Event:L1_Replacement, |
| L1Dcache.cacheProbe(in_msg.LineAddress), |
| getL1DCacheEntry(L1Dcache.cacheProbe(in_msg.LineAddress)), |
| L1_TBEs[L1Dcache.cacheProbe(in_msg.LineAddress)]); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // ACTIONS |
| |
| action(a_issueReadRequest, "a", desc="Issue GETS") { |
| assert(is_valid(tbe)); |
| if (tbe.IssueCount == 0) { |
| // Update outstanding requests |
| //profile_outstanding_request(outstandingRequests); |
| outstandingRequests := outstandingRequests + 1; |
| } |
| |
| if (tbe.IssueCount >= retry_threshold) { |
| // Issue a persistent request if possible |
| if (okToIssueStarving(address, machineID) && (starving == false)) { |
| enqueue(persistentNetwork_out, PersistentMsg, l1_request_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := PersistentRequestType:GETS_PERSISTENT; |
| out_msg.Requestor := machineID; |
| out_msg.Destination.broadcast(MachineType:L1Cache); |
| |
| // |
| // Currently the configuration system limits the system to only one |
| // chip. Therefore, if we assume one shared L2 cache, then only one |
| // pertinent L2 cache exist. |
| // |
| //out_msg.Destination.addNetDest(getAllPertinentL2Banks(address)); |
| |
| out_msg.Destination.add(mapAddressToRange(address, |
| MachineType:L2Cache, l2_select_low_bit, |
| l2_select_num_bits, intToID(0))); |
| |
| out_msg.Destination.add(map_Address_to_Directory(address)); |
| out_msg.MessageSize := MessageSizeType:Persistent_Control; |
| out_msg.Prefetch := tbe.Prefetch; |
| out_msg.AccessMode := tbe.AccessMode; |
| } |
| markPersistentEntries(address); |
| starving := true; |
| |
| if (tbe.IssueCount == 0) { |
| //profile_persistent_prediction(address, tbe.TypeOfAccess); |
| } |
| |
| // Update outstanding requests |
| //profile_outstanding_persistent_request(outstandingPersistentRequests); |
| outstandingPersistentRequests := outstandingPersistentRequests + 1; |
| |
| // Increment IssueCount |
| tbe.IssueCount := tbe.IssueCount + 1; |
| |
| tbe.WentPersistent := true; |
| |
| // Do not schedule a wakeup, a persistent requests will always complete |
| } |
| else { |
| |
| // We'd like to issue a persistent request, but are not allowed |
| // to issue a P.R. right now. This, we do not increment the |
| // IssueCount. |
| |
| // Set a wakeup timer |
| reissueTimerTable.set(address, reissue_wakeup_latency); |
| |
| } |
| } else { |
| // Make a normal request |
| enqueue(requestNetwork_out, RequestMsg, l1_request_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceRequestType:GETS; |
| out_msg.Requestor := machineID; |
| out_msg.Destination.add(mapAddressToRange(address, |
| MachineType:L2Cache, l2_select_low_bit, |
| l2_select_num_bits, intToID(0))); |
| |
| out_msg.RetryNum := tbe.IssueCount; |
| if (tbe.IssueCount == 0) { |
| out_msg.MessageSize := MessageSizeType:Request_Control; |
| } else { |
| out_msg.MessageSize := MessageSizeType:Reissue_Control; |
| } |
| out_msg.Prefetch := tbe.Prefetch; |
| out_msg.AccessMode := tbe.AccessMode; |
| } |
| |
| // send to other local L1s, with local bit set |
| enqueue(requestNetwork_out, RequestMsg, l1_request_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceRequestType:GETS; |
| out_msg.Requestor := machineID; |
| // |
| // Since only one chip, assuming all L1 caches are local |
| // |
| //out_msg.Destination := getOtherLocalL1IDs(machineID); |
| out_msg.Destination.broadcast(MachineType:L1Cache); |
| out_msg.Destination.remove(machineID); |
| |
| out_msg.RetryNum := tbe.IssueCount; |
| out_msg.isLocal := true; |
| if (tbe.IssueCount == 0) { |
| out_msg.MessageSize := MessageSizeType:Broadcast_Control; |
| } else { |
| out_msg.MessageSize := MessageSizeType:Broadcast_Control; |
| } |
| out_msg.Prefetch := tbe.Prefetch; |
| out_msg.AccessMode := tbe.AccessMode; |
| } |
| |
| // Increment IssueCount |
| tbe.IssueCount := tbe.IssueCount + 1; |
| |
| // Set a wakeup timer |
| |
| if (dynamic_timeout_enabled) { |
| reissueTimerTable.set(address, (5 * averageLatencyEstimate()) / 4); |
| } else { |
| reissueTimerTable.set(address, fixed_timeout_latency); |
| } |
| |
| } |
| } |
| |
| action(b_issueWriteRequest, "b", desc="Issue GETX") { |
| |
| assert(is_valid(tbe)); |
| if (tbe.IssueCount == 0) { |
| // Update outstanding requests |
| //profile_outstanding_request(outstandingRequests); |
| outstandingRequests := outstandingRequests + 1; |
| } |
| |
| if (tbe.IssueCount >= retry_threshold) { |
| // Issue a persistent request if possible |
| if ( okToIssueStarving(address, machineID) && (starving == false)) { |
| enqueue(persistentNetwork_out, PersistentMsg, l1_request_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := PersistentRequestType:GETX_PERSISTENT; |
| out_msg.Requestor := machineID; |
| out_msg.Destination.broadcast(MachineType:L1Cache); |
| |
| // |
| // Currently the configuration system limits the system to only one |
| // chip. Therefore, if we assume one shared L2 cache, then only one |
| // pertinent L2 cache exist. |
| // |
| //out_msg.Destination.addNetDest(getAllPertinentL2Banks(address)); |
| |
| out_msg.Destination.add(mapAddressToRange(address, |
| MachineType:L2Cache, l2_select_low_bit, |
| l2_select_num_bits, intToID(0))); |
| |
| out_msg.Destination.add(map_Address_to_Directory(address)); |
| out_msg.MessageSize := MessageSizeType:Persistent_Control; |
| out_msg.Prefetch := tbe.Prefetch; |
| out_msg.AccessMode := tbe.AccessMode; |
| } |
| markPersistentEntries(address); |
| starving := true; |
| |
| // Update outstanding requests |
| //profile_outstanding_persistent_request(outstandingPersistentRequests); |
| outstandingPersistentRequests := outstandingPersistentRequests + 1; |
| |
| if (tbe.IssueCount == 0) { |
| //profile_persistent_prediction(address, tbe.TypeOfAccess); |
| } |
| |
| // Increment IssueCount |
| tbe.IssueCount := tbe.IssueCount + 1; |
| |
| tbe.WentPersistent := true; |
| |
| // Do not schedule a wakeup, a persistent requests will always complete |
| } |
| else { |
| |
| // We'd like to issue a persistent request, but are not allowed |
| // to issue a P.R. right now. This, we do not increment the |
| // IssueCount. |
| |
| // Set a wakeup timer |
| reissueTimerTable.set(address, reissue_wakeup_latency); |
| } |
| |
| } else { |
| // Make a normal request |
| enqueue(requestNetwork_out, RequestMsg, l1_request_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceRequestType:GETX; |
| out_msg.Requestor := machineID; |
| |
| out_msg.Destination.add(mapAddressToRange(address, |
| MachineType:L2Cache, l2_select_low_bit, |
| l2_select_num_bits, intToID(0))); |
| |
| out_msg.RetryNum := tbe.IssueCount; |
| |
| if (tbe.IssueCount == 0) { |
| out_msg.MessageSize := MessageSizeType:Request_Control; |
| } else { |
| out_msg.MessageSize := MessageSizeType:Reissue_Control; |
| } |
| out_msg.Prefetch := tbe.Prefetch; |
| out_msg.AccessMode := tbe.AccessMode; |
| } |
| |
| // send to other local L1s too |
| enqueue(requestNetwork_out, RequestMsg, l1_request_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceRequestType:GETX; |
| out_msg.Requestor := machineID; |
| out_msg.isLocal := true; |
| |
| // |
| // Since only one chip, assuming all L1 caches are local |
| // |
| //out_msg.Destination := getOtherLocalL1IDs(machineID); |
| out_msg.Destination.broadcast(MachineType:L1Cache); |
| out_msg.Destination.remove(machineID); |
| |
| out_msg.RetryNum := tbe.IssueCount; |
| if (tbe.IssueCount == 0) { |
| out_msg.MessageSize := MessageSizeType:Broadcast_Control; |
| } else { |
| out_msg.MessageSize := MessageSizeType:Broadcast_Control; |
| } |
| out_msg.Prefetch := tbe.Prefetch; |
| out_msg.AccessMode := tbe.AccessMode; |
| } |
| |
| // Increment IssueCount |
| tbe.IssueCount := tbe.IssueCount + 1; |
| |
| DPRINTF(RubySlicc, "incremented issue count to %d\n", |
| tbe.IssueCount); |
| |
| // Set a wakeup timer |
| if (dynamic_timeout_enabled) { |
| reissueTimerTable.set(address, (5 * averageLatencyEstimate()) / 4); |
| } else { |
| reissueTimerTable.set(address, fixed_timeout_latency); |
| } |
| } |
| } |
| |
| action(bb_bounceResponse, "\b", desc="Bounce tokens and data to memory") { |
| peek(responseNetwork_in, ResponseMsg) { |
| // FIXME, should use a 3rd vnet |
| enqueue(responseNetwork_out, ResponseMsg, 1) { |
| out_msg.Addr := address; |
| out_msg.Type := in_msg.Type; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(map_Address_to_Directory(address)); |
| out_msg.Tokens := in_msg.Tokens; |
| out_msg.MessageSize := in_msg.MessageSize; |
| out_msg.DataBlk := in_msg.DataBlk; |
| out_msg.Dirty := in_msg.Dirty; |
| } |
| } |
| } |
| |
| action(c_ownedReplacement, "c", desc="Issue writeback") { |
| assert(is_valid(cache_entry)); |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Sender := machineID; |
| |
| out_msg.Destination.add(mapAddressToRange(address, |
| MachineType:L2Cache, l2_select_low_bit, |
| l2_select_num_bits, intToID(0))); |
| |
| out_msg.Tokens := cache_entry.Tokens; |
| out_msg.DataBlk := cache_entry.DataBlk; |
| out_msg.Dirty := cache_entry.Dirty; |
| out_msg.Type := CoherenceResponseType:WB_OWNED; |
| |
| // always send the data? |
| out_msg.MessageSize := MessageSizeType:Writeback_Data; |
| } |
| cache_entry.Tokens := 0; |
| } |
| |
| action(cc_sharedReplacement, "\c", desc="Issue shared writeback") { |
| |
| // don't send writeback if replacing block with no tokens |
| assert(is_valid(cache_entry)); |
| assert (cache_entry.Tokens > 0); |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Sender := machineID; |
| |
| out_msg.Destination.add(mapAddressToRange(address, |
| MachineType:L2Cache, l2_select_low_bit, |
| l2_select_num_bits, intToID(0))); |
| |
| out_msg.Tokens := cache_entry.Tokens; |
| out_msg.DataBlk := cache_entry.DataBlk; |
| // assert(cache_entry.Dirty == false); |
| out_msg.Dirty := false; |
| |
| out_msg.MessageSize := MessageSizeType:Writeback_Data; |
| out_msg.Type := CoherenceResponseType:WB_SHARED_DATA; |
| } |
| cache_entry.Tokens := 0; |
| } |
| |
| action(tr_tokenReplacement, "tr", desc="Issue token writeback") { |
| assert(is_valid(cache_entry)); |
| if (cache_entry.Tokens > 0) { |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Sender := machineID; |
| |
| out_msg.Destination.add(mapAddressToRange(address, |
| MachineType:L2Cache, l2_select_low_bit, |
| l2_select_num_bits, intToID(0))); |
| |
| out_msg.Tokens := cache_entry.Tokens; |
| out_msg.DataBlk := cache_entry.DataBlk; |
| // assert(cache_entry.Dirty == false); |
| out_msg.Dirty := false; |
| |
| // always send the data? |
| out_msg.MessageSize := MessageSizeType:Writeback_Control; |
| out_msg.Type := CoherenceResponseType:WB_TOKENS; |
| } |
| } |
| cache_entry.Tokens := 0; |
| } |
| |
| |
| action(d_sendDataWithToken, "d", desc="Send data and a token from cache to requestor") { |
| assert(is_valid(cache_entry)); |
| peek(requestNetwork_in, RequestMsg) { |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceResponseType:DATA_SHARED; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(in_msg.Requestor); |
| out_msg.Tokens := 1; |
| out_msg.DataBlk := cache_entry.DataBlk; |
| // out_msg.Dirty := cache_entry.Dirty; |
| out_msg.Dirty := false; |
| if (in_msg.isLocal) { |
| out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; |
| } else { |
| out_msg.MessageSize := MessageSizeType:Response_Data; |
| } |
| } |
| } |
| cache_entry.Tokens := cache_entry.Tokens - 1; |
| assert(cache_entry.Tokens >= 1); |
| } |
| |
| action(d_sendDataWithNTokenIfAvail, "\dd", desc="Send data and a token from cache to requestor") { |
| assert(is_valid(cache_entry)); |
| peek(requestNetwork_in, RequestMsg) { |
| if (cache_entry.Tokens > (N_tokens + (max_tokens() / 2))) { |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceResponseType:DATA_SHARED; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(in_msg.Requestor); |
| out_msg.Tokens := N_tokens; |
| out_msg.DataBlk := cache_entry.DataBlk; |
| // out_msg.Dirty := cache_entry.Dirty; |
| out_msg.Dirty := false; |
| if (in_msg.isLocal) { |
| out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; |
| } else { |
| out_msg.MessageSize := MessageSizeType:Response_Data; |
| } |
| } |
| cache_entry.Tokens := cache_entry.Tokens - N_tokens; |
| } |
| else if (cache_entry.Tokens > 1) { |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceResponseType:DATA_SHARED; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(in_msg.Requestor); |
| out_msg.Tokens := 1; |
| out_msg.DataBlk := cache_entry.DataBlk; |
| // out_msg.Dirty := cache_entry.Dirty; |
| out_msg.Dirty := false; |
| if (in_msg.isLocal) { |
| out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; |
| } else { |
| out_msg.MessageSize := MessageSizeType:Response_Data; |
| } |
| } |
| cache_entry.Tokens := cache_entry.Tokens - 1; |
| } |
| } |
| // assert(cache_entry.Tokens >= 1); |
| } |
| |
| action(dd_sendDataWithAllTokens, "\d", desc="Send data and all tokens from cache to requestor") { |
| peek(requestNetwork_in, RequestMsg) { |
| assert(is_valid(cache_entry)); |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceResponseType:DATA_OWNER; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(in_msg.Requestor); |
| assert(cache_entry.Tokens > (max_tokens() / 2)); |
| out_msg.Tokens := cache_entry.Tokens; |
| out_msg.DataBlk := cache_entry.DataBlk; |
| out_msg.Dirty := cache_entry.Dirty; |
| if (in_msg.isLocal) { |
| out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; |
| } else { |
| out_msg.MessageSize := MessageSizeType:Response_Data; |
| } |
| } |
| } |
| cache_entry.Tokens := 0; |
| } |
| |
| action(e_sendAckWithCollectedTokens, "e", desc="Send ack with the tokens we've collected thus far.") { |
| // assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself |
| assert(is_valid(cache_entry)); |
| if (cache_entry.Tokens > 0) { |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| if (cache_entry.Tokens > (max_tokens() / 2)) { |
| out_msg.Type := CoherenceResponseType:DATA_OWNER; |
| } else { |
| out_msg.Type := CoherenceResponseType:ACK; |
| } |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(persistentTable.findSmallest(address)); |
| assert(cache_entry.Tokens >= 1); |
| out_msg.Tokens := cache_entry.Tokens; |
| out_msg.DataBlk := cache_entry.DataBlk; |
| out_msg.MessageSize := MessageSizeType:Response_Control; |
| } |
| } |
| cache_entry.Tokens := 0; |
| } |
| |
| action(ee_sendDataWithAllTokens, "\e", desc="Send data and all tokens from cache to starver") { |
| //assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself |
| assert(is_valid(cache_entry)); |
| assert(cache_entry.Tokens > 0); |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceResponseType:DATA_OWNER; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(persistentTable.findSmallest(address)); |
| assert(cache_entry.Tokens > (max_tokens() / 2)); |
| out_msg.Tokens := cache_entry.Tokens; |
| out_msg.DataBlk := cache_entry.DataBlk; |
| out_msg.Dirty := cache_entry.Dirty; |
| out_msg.MessageSize := MessageSizeType:Response_Data; |
| } |
| cache_entry.Tokens := 0; |
| } |
| |
| action(f_sendAckWithAllButNorOneTokens, "f", desc="Send ack with all our tokens but one to starver.") { |
| //assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself |
| assert(is_valid(cache_entry)); |
| assert(cache_entry.Tokens > 0); |
| if (cache_entry.Tokens > 1) { |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| if (cache_entry.Tokens > (max_tokens() / 2)) { |
| out_msg.Type := CoherenceResponseType:DATA_OWNER; |
| } else { |
| out_msg.Type := CoherenceResponseType:ACK; |
| } |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(persistentTable.findSmallest(address)); |
| assert(cache_entry.Tokens >= 1); |
| if (cache_entry.Tokens > N_tokens) { |
| out_msg.Tokens := cache_entry.Tokens - N_tokens; |
| } else { |
| out_msg.Tokens := cache_entry.Tokens - 1; |
| } |
| out_msg.DataBlk := cache_entry.DataBlk; |
| out_msg.MessageSize := MessageSizeType:Response_Control; |
| } |
| } |
| if (cache_entry.Tokens > N_tokens) { |
| cache_entry.Tokens := N_tokens; |
| } else { |
| cache_entry.Tokens := 1; |
| } |
| } |
| |
| action(ff_sendDataWithAllButNorOneTokens, "\f", desc="Send data and out tokens but one to starver") { |
| //assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself |
| assert(is_valid(cache_entry)); |
| assert(cache_entry.Tokens > ((max_tokens() / 2) + 1)); |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceResponseType:DATA_OWNER; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(persistentTable.findSmallest(address)); |
| if (cache_entry.Tokens > (N_tokens + (max_tokens() / 2))) { |
| out_msg.Tokens := cache_entry.Tokens - N_tokens; |
| } else { |
| out_msg.Tokens := cache_entry.Tokens - 1; |
| } |
| assert(out_msg.Tokens > (max_tokens() / 2)); |
| out_msg.DataBlk := cache_entry.DataBlk; |
| out_msg.Dirty := cache_entry.Dirty; |
| out_msg.MessageSize := MessageSizeType:Response_Data; |
| } |
| if (cache_entry.Tokens > (N_tokens + (max_tokens() / 2))) { |
| cache_entry.Tokens := N_tokens; |
| } else { |
| cache_entry.Tokens := 1; |
| } |
| } |
| |
| action(fo_sendDataWithOwnerToken, "fo", desc="Send data and owner tokens") { |
| assert(is_valid(cache_entry)); |
| assert(cache_entry.Tokens == ((max_tokens() / 2) + 1)); |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceResponseType:DATA_OWNER; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(persistentTable.findSmallest(address)); |
| out_msg.Tokens := cache_entry.Tokens; |
| assert(out_msg.Tokens > (max_tokens() / 2)); |
| out_msg.DataBlk := cache_entry.DataBlk; |
| out_msg.Dirty := cache_entry.Dirty; |
| out_msg.MessageSize := MessageSizeType:Response_Data; |
| } |
| cache_entry.Tokens := 0; |
| } |
| |
| action(g_bounceResponseToStarver, "g", desc="Redirect response to starving processor") { |
| // assert(persistentTable.isLocked(address)); |
| |
| peek(responseNetwork_in, ResponseMsg) { |
| // assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself |
| // FIXME, should use a 3rd vnet in some cases |
| enqueue(responseNetwork_out, ResponseMsg, 1) { |
| out_msg.Addr := address; |
| out_msg.Type := in_msg.Type; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(persistentTable.findSmallest(address)); |
| out_msg.Tokens := in_msg.Tokens; |
| out_msg.DataBlk := in_msg.DataBlk; |
| out_msg.Dirty := in_msg.Dirty; |
| out_msg.MessageSize := in_msg.MessageSize; |
| } |
| } |
| } |
| |
| |
| action(h_load_hit, "h", desc="Notify sequencer the load completed.") { |
| assert(is_valid(cache_entry)); |
| DPRINTF(RubySlicc, "Address: %s, Data Block: %s\n", |
| address, cache_entry.DataBlk); |
| |
| sequencer.readCallback(address, cache_entry.DataBlk, false, |
| MachineType:L1Cache); |
| } |
| |
| action(x_external_load_hit, "x", desc="Notify sequencer the load completed.") { |
| assert(is_valid(cache_entry)); |
| DPRINTF(RubySlicc, "Address: %s, Data Block: %s\n", |
| address, cache_entry.DataBlk); |
| peek(responseNetwork_in, ResponseMsg) { |
| sequencer.readCallback(address, cache_entry.DataBlk, |
| isExternalHit(address, in_msg.Sender), |
| machineIDToMachineType(in_msg.Sender)); |
| } |
| } |
| |
| action(hh_store_hit, "\h", desc="Notify sequencer that store completed.") { |
| assert(is_valid(cache_entry)); |
| DPRINTF(RubySlicc, "Address: %s, Data Block: %s\n", |
| address, cache_entry.DataBlk); |
| |
| sequencer.writeCallback(address, cache_entry.DataBlk, false, |
| MachineType:L1Cache); |
| cache_entry.Dirty := true; |
| DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk); |
| } |
| |
| action(xx_external_store_hit, "\x", desc="Notify sequencer that store completed.") { |
| assert(is_valid(cache_entry)); |
| DPRINTF(RubySlicc, "Address: %s, Data Block: %s\n", |
| address, cache_entry.DataBlk); |
| peek(responseNetwork_in, ResponseMsg) { |
| sequencer.writeCallback(address, cache_entry.DataBlk, |
| isExternalHit(address, in_msg.Sender), |
| machineIDToMachineType(in_msg.Sender)); |
| } |
| cache_entry.Dirty := true; |
| DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk); |
| } |
| |
| action(i_allocateTBE, "i", desc="Allocate TBE") { |
| check_allocate(L1_TBEs); |
| L1_TBEs.allocate(address); |
| set_tbe(L1_TBEs[address]); |
| tbe.IssueCount := 0; |
| peek(mandatoryQueue_in, RubyRequest) { |
| tbe.PC := in_msg.ProgramCounter; |
| tbe.TypeOfAccess := cache_request_type_to_access_type(in_msg.Type); |
| if (in_msg.Type == RubyRequestType:ATOMIC) { |
| tbe.IsAtomic := true; |
| } |
| tbe.Prefetch := in_msg.Prefetch; |
| tbe.AccessMode := in_msg.AccessMode; |
| } |
| tbe.IssueTime := curCycle(); |
| } |
| |
| action(ta_traceStalledAddress, "ta", desc="Trace Stalled Address") { |
| peek(mandatoryQueue_in, RubyRequest) { |
| APPEND_TRANSITION_COMMENT(in_msg.LineAddress); |
| } |
| } |
| |
| action(j_unsetReissueTimer, "j", desc="Unset reissue timer.") { |
| if (reissueTimerTable.isSet(address)) { |
| reissueTimerTable.unset(address); |
| } |
| } |
| |
| action(jj_unsetUseTimer, "\j", desc="Unset use timer.") { |
| useTimerTable.unset(address); |
| } |
| |
| action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { |
| mandatoryQueue_in.dequeue(); |
| } |
| |
| action(l_popPersistentQueue, "l", desc="Pop persistent queue.") { |
| persistentNetwork_in.dequeue(); |
| } |
| |
| action(m_popRequestQueue, "m", desc="Pop request queue.") { |
| requestNetwork_in.dequeue(); |
| } |
| |
| action(n_popResponseQueue, "n", desc="Pop response queue") { |
| responseNetwork_in.dequeue(); |
| } |
| |
| action(o_scheduleUseTimeout, "o", desc="Schedule a use timeout.") { |
| useTimerTable.set(address, use_timeout_latency); |
| } |
| |
| action(p_informL2AboutTokenLoss, "p", desc="Inform L2 about loss of all tokens") { |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := CoherenceResponseType:INV; |
| out_msg.Tokens := 0; |
| out_msg.Sender := machineID; |
| |
| out_msg.Destination.add(mapAddressToRange(address, |
| MachineType:L2Cache, l2_select_low_bit, |
| l2_select_num_bits, intToID(0))); |
| out_msg.MessageSize := MessageSizeType:Response_Control; |
| } |
| } |
| |
| action(q_updateTokensFromResponse, "q", desc="Update the token count based on the incoming response message") { |
| peek(responseNetwork_in, ResponseMsg) { |
| assert(is_valid(cache_entry)); |
| assert(in_msg.Tokens != 0); |
| DPRINTF(RubySlicc, "L1 received tokens for address: %s, tokens: %d\n", |
| in_msg.Addr, in_msg.Tokens); |
| cache_entry.Tokens := cache_entry.Tokens + in_msg.Tokens; |
| DPRINTF(RubySlicc, "%d\n", cache_entry.Tokens); |
| |
| if (cache_entry.Dirty == false && in_msg.Dirty) { |
| cache_entry.Dirty := true; |
| } |
| } |
| } |
| |
| action(s_deallocateTBE, "s", desc="Deallocate TBE") { |
| |
| assert(is_valid(tbe)); |
| if (tbe.WentPersistent) { |
| // assert(starving); |
| outstandingRequests := outstandingRequests - 1; |
| enqueue(persistentNetwork_out, PersistentMsg, l1_request_latency) { |
| out_msg.Addr := address; |
| out_msg.Type := PersistentRequestType:DEACTIVATE_PERSISTENT; |
| out_msg.Requestor := machineID; |
| out_msg.Destination.broadcast(MachineType:L1Cache); |
| |
| // |
| // Currently the configuration system limits the system to only one |
| // chip. Therefore, if we assume one shared L2 cache, then only one |
| // pertinent L2 cache exist. |
| // |
| //out_msg.Destination.addNetDest(getAllPertinentL2Banks(address)); |
| |
| out_msg.Destination.add(mapAddressToRange(address, |
| MachineType:L2Cache, l2_select_low_bit, |
| l2_select_num_bits, intToID(0))); |
| |
| out_msg.Destination.add(map_Address_to_Directory(address)); |
| out_msg.MessageSize := MessageSizeType:Persistent_Control; |
| } |
| starving := false; |
| } |
| |
| // Update average latency |
| if (tbe.IssueCount <= 1) { |
| if (tbe.ExternalResponse) { |
| updateAverageLatencyEstimate(curCycle() - tbe.IssueTime); |
| } |
| } |
| |
| // Profile |
| //if (tbe.WentPersistent) { |
| // profile_token_retry(address, tbe.TypeOfAccess, 2); |
| //} |
| //else { |
| // profile_token_retry(address, tbe.TypeOfAccess, 1); |
| //} |
| |
| //profile_token_retry(address, tbe.TypeOfAccess, tbe.IssueCount); |
| L1_TBEs.deallocate(address); |
| unset_tbe(); |
| } |
| |
| action(t_sendAckWithCollectedTokens, "t", desc="Send ack with the tokens we've collected thus far.") { |
| assert(is_valid(cache_entry)); |
| if (cache_entry.Tokens > 0) { |
| peek(requestNetwork_in, RequestMsg) { |
| enqueue(responseNetwork_out, ResponseMsg, l1_response_latency) { |
| out_msg.Addr := address; |
| if (cache_entry.Tokens > (max_tokens() / 2)) { |
| out_msg.Type := CoherenceResponseType:DATA_OWNER; |
| } else { |
| out_msg.Type := CoherenceResponseType:ACK; |
| } |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(in_msg.Requestor); |
| assert(cache_entry.Tokens >= 1); |
| out_msg.Tokens := cache_entry.Tokens; |
| out_msg.DataBlk := cache_entry.DataBlk; |
| out_msg.MessageSize := MessageSizeType:Response_Control; |
| } |
| } |
| } |
| cache_entry.Tokens := 0; |
| } |
| |
| action(u_writeDataToCache, "u", desc="Write data to cache") { |
| peek(responseNetwork_in, ResponseMsg) { |
| assert(is_valid(cache_entry)); |
| cache_entry.DataBlk := in_msg.DataBlk; |
| if (cache_entry.Dirty == false && in_msg.Dirty) { |
| cache_entry.Dirty := in_msg.Dirty; |
| } |
| |
| } |
| } |
| |
| action(gg_deallocateL1CacheBlock, "\g", desc="Deallocate cache block. Sets the cache to invalid, allowing a replacement in parallel with a fetch.") { |
| assert(getTokens(cache_entry) == 0); |
| if (L1Dcache.isTagPresent(address)) { |
| L1Dcache.deallocate(address); |
| } else { |
| L1Icache.deallocate(address); |
| } |
| unset_cache_entry(); |
| } |
| |
| action(ii_allocateL1DCacheBlock, "\i", desc="Set L1 D-cache tag equal to tag of block B.") { |
| if (is_valid(cache_entry)) { |
| } else { |
| set_cache_entry(L1Dcache.allocate(address, new Entry)); |
| } |
| } |
| |
| action(pp_allocateL1ICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") { |
| if (is_valid(cache_entry)) { |
| } else { |
| set_cache_entry(L1Icache.allocate(address, new Entry)); |
| } |
| } |
| |
| action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") { |
| if (send_evictions) { |
| DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address); |
| sequencer.evictionCallback(address); |
| } |
| } |
| |
| action(uu_profileInstMiss, "\uim", desc="Profile the demand miss") { |
| ++L1Icache.demand_misses; |
| } |
| |
| action(uu_profileInstHit, "\uih", desc="Profile the demand hit") { |
| ++L1Icache.demand_hits; |
| } |
| |
| action(uu_profileDataMiss, "\udm", desc="Profile the demand miss") { |
| ++L1Dcache.demand_misses; |
| } |
| |
| action(uu_profileDataHit, "\udh", desc="Profile the demand hit") { |
| ++L1Dcache.demand_hits; |
| } |
| |
| action(w_assertIncomingDataAndCacheDataMatch, "w", desc="Assert that the incoming data and the data in the cache match") { |
| peek(responseNetwork_in, ResponseMsg) { |
| assert(is_valid(cache_entry)); |
| assert(cache_entry.DataBlk == in_msg.DataBlk); |
| } |
| } |
| |
| action(zz_stallAndWaitMandatoryQueue, "\z", desc="Send the head of the mandatory queue to the back of the queue.") { |
| peek(mandatoryQueue_in, RubyRequest) { |
| APPEND_TRANSITION_COMMENT(in_msg.LineAddress); |
| } |
| stall_and_wait(mandatoryQueue_in, address); |
| } |
| |
| action(kd_wakeUpDependents, "kd", desc="wake-up dependents") { |
| wakeUpBuffers(address); |
| } |
| |
| action(ka_wakeUpAllDependents, "ka", desc="wake-up all dependents") { |
| wakeUpAllBuffers(); |
| } |
| |
| //***************************************************** |
| // TRANSITIONS |
| //***************************************************** |
| |
| // Transitions for Load/Store/L2_Replacement from transient states |
| transition({IM, SM, OM, IS, IM_L, IS_L, I_L, S_L, SM_L, M_W, MM_W}, L1_Replacement) { |
| ta_traceStalledAddress; |
| zz_stallAndWaitMandatoryQueue; |
| } |
| |
| transition({IM, SM, OM, IS, IM_L, IS_L, SM_L}, {Store, Atomic}) { |
| zz_stallAndWaitMandatoryQueue; |
| } |
| |
| transition({IM, IS, IM_L, IS_L}, {Load, Ifetch}) { |
| zz_stallAndWaitMandatoryQueue; |
| } |
| |
| // Lockdowns |
| transition({NP, I, S, O, M, MM, M_W, MM_W, IM, SM, OM, IS}, Own_Lock_or_Unlock) { |
| l_popPersistentQueue; |
| } |
| |
| // Transitions from NP |
| transition(NP, Load, IS) { |
| ii_allocateL1DCacheBlock; |
| i_allocateTBE; |
| a_issueReadRequest; |
| uu_profileDataMiss; |
| k_popMandatoryQueue; |
| } |
| |
| transition(NP, Ifetch, IS) { |
| pp_allocateL1ICacheBlock; |
| i_allocateTBE; |
| a_issueReadRequest; |
| uu_profileInstMiss; |
| k_popMandatoryQueue; |
| } |
| |
| transition(NP, {Store, Atomic}, IM) { |
| ii_allocateL1DCacheBlock; |
| i_allocateTBE; |
| b_issueWriteRequest; |
| uu_profileDataMiss; |
| k_popMandatoryQueue; |
| } |
| |
| transition(NP, {Ack, Data_Shared, Data_Owner, Data_All_Tokens}) { |
| bb_bounceResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(NP, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_Local_GETS}) { |
| m_popRequestQueue; |
| } |
| |
| transition(NP, {Persistent_GETX, Persistent_GETS, Persistent_GETS_Last_Token}, I_L) { |
| l_popPersistentQueue; |
| } |
| |
| // Transitions from Idle |
| transition(I, Load, IS) { |
| i_allocateTBE; |
| a_issueReadRequest; |
| uu_profileDataMiss; |
| k_popMandatoryQueue; |
| } |
| |
| transition(I, Ifetch, IS) { |
| i_allocateTBE; |
| a_issueReadRequest; |
| uu_profileInstMiss; |
| k_popMandatoryQueue; |
| } |
| |
| transition(I, {Store, Atomic}, IM) { |
| i_allocateTBE; |
| b_issueWriteRequest; |
| uu_profileDataMiss; |
| k_popMandatoryQueue; |
| } |
| |
| transition(I, L1_Replacement) { |
| ta_traceStalledAddress; |
| tr_tokenReplacement; |
| gg_deallocateL1CacheBlock; |
| ka_wakeUpAllDependents; |
| } |
| |
| transition(I, {Transient_GETX, Transient_Local_GETX}) { |
| t_sendAckWithCollectedTokens; |
| m_popRequestQueue; |
| } |
| |
| transition(I, {Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS}) { |
| m_popRequestQueue; |
| } |
| |
| transition(I, {Persistent_GETX, Persistent_GETS, Persistent_GETS_Last_Token}, I_L) { |
| e_sendAckWithCollectedTokens; |
| l_popPersistentQueue; |
| } |
| |
| transition(I_L, {Persistent_GETX, Persistent_GETS, Persistent_GETS_Last_Token}) { |
| l_popPersistentQueue; |
| } |
| |
| transition(I, Ack) { |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(I, Data_Shared, S) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(I, Data_Owner, O) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(I, Data_All_Tokens, M) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| // Transitions from Shared |
| transition({S, SM, S_L, SM_L}, Load) { |
| h_load_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition({S, SM, S_L, SM_L}, Ifetch) { |
| h_load_hit; |
| uu_profileInstHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition(S, {Store, Atomic}, SM) { |
| i_allocateTBE; |
| b_issueWriteRequest; |
| uu_profileDataMiss; |
| k_popMandatoryQueue; |
| } |
| |
| transition(S, L1_Replacement, I) { |
| ta_traceStalledAddress; |
| cc_sharedReplacement; // Only needed in some cases |
| forward_eviction_to_cpu; |
| gg_deallocateL1CacheBlock; |
| ka_wakeUpAllDependents; |
| } |
| |
| transition(S, {Transient_GETX, Transient_Local_GETX}, I) { |
| t_sendAckWithCollectedTokens; |
| p_informL2AboutTokenLoss; |
| forward_eviction_to_cpu |
| m_popRequestQueue; |
| } |
| |
| // only owner responds to non-local requests |
| transition(S, Transient_GETS) { |
| m_popRequestQueue; |
| } |
| |
| transition(S, Transient_Local_GETS) { |
| d_sendDataWithToken; |
| m_popRequestQueue; |
| } |
| |
| transition(S, {Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token}) { |
| m_popRequestQueue; |
| } |
| |
| transition({S, S_L}, Persistent_GETX, I_L) { |
| e_sendAckWithCollectedTokens; |
| p_informL2AboutTokenLoss; |
| forward_eviction_to_cpu |
| l_popPersistentQueue; |
| } |
| |
| transition(S, {Persistent_GETS, Persistent_GETS_Last_Token}, S_L) { |
| f_sendAckWithAllButNorOneTokens; |
| l_popPersistentQueue; |
| } |
| |
| transition(S_L, {Persistent_GETS, Persistent_GETS_Last_Token}) { |
| l_popPersistentQueue; |
| } |
| |
| transition(S, Ack) { |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(S, Data_Shared) { |
| w_assertIncomingDataAndCacheDataMatch; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(S, Data_Owner, O) { |
| w_assertIncomingDataAndCacheDataMatch; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(S, Data_All_Tokens, M) { |
| w_assertIncomingDataAndCacheDataMatch; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| // Transitions from Owned |
| transition({O, OM}, Ifetch) { |
| h_load_hit; |
| uu_profileInstHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition({O, OM}, Load) { |
| h_load_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition(O, {Store, Atomic}, OM) { |
| i_allocateTBE; |
| b_issueWriteRequest; |
| uu_profileDataMiss; |
| k_popMandatoryQueue; |
| } |
| |
| transition(O, L1_Replacement, I) { |
| ta_traceStalledAddress; |
| c_ownedReplacement; |
| forward_eviction_to_cpu |
| gg_deallocateL1CacheBlock; |
| ka_wakeUpAllDependents; |
| } |
| |
| transition(O, {Transient_GETX, Transient_Local_GETX}, I) { |
| dd_sendDataWithAllTokens; |
| p_informL2AboutTokenLoss; |
| forward_eviction_to_cpu |
| m_popRequestQueue; |
| } |
| |
| transition(O, Persistent_GETX, I_L) { |
| ee_sendDataWithAllTokens; |
| p_informL2AboutTokenLoss; |
| forward_eviction_to_cpu |
| l_popPersistentQueue; |
| } |
| |
| transition(O, Persistent_GETS, S_L) { |
| ff_sendDataWithAllButNorOneTokens; |
| l_popPersistentQueue; |
| } |
| |
| transition(O, Persistent_GETS_Last_Token, I_L) { |
| fo_sendDataWithOwnerToken; |
| forward_eviction_to_cpu |
| l_popPersistentQueue; |
| } |
| |
| transition(O, Transient_GETS) { |
| d_sendDataWithToken; |
| m_popRequestQueue; |
| } |
| |
| transition(O, Transient_Local_GETS) { |
| d_sendDataWithToken; |
| m_popRequestQueue; |
| } |
| |
| // ran out of tokens, wait for it to go persistent |
| transition(O, {Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token}) { |
| m_popRequestQueue; |
| } |
| |
| transition(O, Ack) { |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(O, Ack_All_Tokens, M) { |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(O, Data_Shared) { |
| w_assertIncomingDataAndCacheDataMatch; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(O, Data_All_Tokens, M) { |
| w_assertIncomingDataAndCacheDataMatch; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| // Transitions from Modified |
| transition({MM, MM_W}, Ifetch) { |
| h_load_hit; |
| uu_profileInstHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition({MM, MM_W}, Load) { |
| h_load_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition({MM_W}, {Store, Atomic}) { |
| hh_store_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition(MM, Store) { |
| hh_store_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition(MM, Atomic, M) { |
| hh_store_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition(MM, L1_Replacement, I) { |
| ta_traceStalledAddress; |
| c_ownedReplacement; |
| forward_eviction_to_cpu |
| gg_deallocateL1CacheBlock; |
| ka_wakeUpAllDependents; |
| } |
| |
| transition(MM, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_Local_GETS}, I) { |
| dd_sendDataWithAllTokens; |
| p_informL2AboutTokenLoss; |
| forward_eviction_to_cpu |
| m_popRequestQueue; |
| } |
| |
| transition({MM_W}, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_Local_GETS}) { // Ignore the request |
| m_popRequestQueue; |
| } |
| |
| // Implement the migratory sharing optimization, even for persistent requests |
| transition(MM, {Persistent_GETX, Persistent_GETS}, I_L) { |
| ee_sendDataWithAllTokens; |
| p_informL2AboutTokenLoss; |
| forward_eviction_to_cpu |
| l_popPersistentQueue; |
| } |
| |
| // ignore persistent requests in lockout period |
| transition(MM_W, {Persistent_GETX, Persistent_GETS}) { |
| l_popPersistentQueue; |
| } |
| |
| transition(MM_W, Use_TimeoutNoStarvers, MM) { |
| s_deallocateTBE; |
| jj_unsetUseTimer; |
| kd_wakeUpDependents; |
| } |
| |
| transition(MM_W, Use_TimeoutNoStarvers_NoMig, M) { |
| s_deallocateTBE; |
| jj_unsetUseTimer; |
| kd_wakeUpDependents; |
| } |
| |
| // Transitions from Dirty Exclusive |
| transition({M, M_W}, Ifetch) { |
| h_load_hit; |
| uu_profileInstHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition({M, M_W}, Load) { |
| h_load_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition(M, Store, MM) { |
| hh_store_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition(M, Atomic) { |
| hh_store_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition(M_W, Store, MM_W) { |
| hh_store_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition(M_W, Atomic) { |
| hh_store_hit; |
| uu_profileDataHit; |
| k_popMandatoryQueue; |
| } |
| |
| transition(M, L1_Replacement, I) { |
| ta_traceStalledAddress; |
| c_ownedReplacement; |
| forward_eviction_to_cpu |
| gg_deallocateL1CacheBlock; |
| ka_wakeUpAllDependents; |
| } |
| |
| transition(M, {Transient_GETX, Transient_Local_GETX}, I) { |
| dd_sendDataWithAllTokens; |
| p_informL2AboutTokenLoss; |
| forward_eviction_to_cpu |
| m_popRequestQueue; |
| } |
| |
| transition(M, Transient_Local_GETS, O) { |
| d_sendDataWithToken; |
| m_popRequestQueue; |
| } |
| |
| transition(M, Transient_GETS, O) { |
| d_sendDataWithNTokenIfAvail; |
| m_popRequestQueue; |
| } |
| |
| transition(M_W, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_Local_GETS}) { // Ignore the request |
| m_popRequestQueue; |
| } |
| |
| transition(M, Persistent_GETX, I_L) { |
| ee_sendDataWithAllTokens; |
| p_informL2AboutTokenLoss; |
| forward_eviction_to_cpu |
| l_popPersistentQueue; |
| } |
| |
| transition(M, Persistent_GETS, S_L) { |
| ff_sendDataWithAllButNorOneTokens; |
| l_popPersistentQueue; |
| } |
| |
| // ignore persistent requests in lockout period |
| transition(M_W, {Persistent_GETX, Persistent_GETS}) { |
| l_popPersistentQueue; |
| } |
| |
| transition(M_W, Use_TimeoutStarverS, S_L) { |
| s_deallocateTBE; |
| ff_sendDataWithAllButNorOneTokens; |
| jj_unsetUseTimer; |
| } |
| |
| // someone unlocked during timeout |
| transition(M_W, {Use_TimeoutNoStarvers, Use_TimeoutNoStarvers_NoMig}, M) { |
| s_deallocateTBE; |
| jj_unsetUseTimer; |
| kd_wakeUpDependents; |
| } |
| |
| transition(M_W, Use_TimeoutStarverX, I_L) { |
| s_deallocateTBE; |
| ee_sendDataWithAllTokens; |
| forward_eviction_to_cpu; |
| p_informL2AboutTokenLoss; |
| jj_unsetUseTimer; |
| } |
| |
| // migratory |
| transition(MM_W, {Use_TimeoutStarverX, Use_TimeoutStarverS}, I_L) { |
| s_deallocateTBE; |
| ee_sendDataWithAllTokens; |
| forward_eviction_to_cpu; |
| p_informL2AboutTokenLoss; |
| jj_unsetUseTimer; |
| |
| } |
| |
| // Transient_GETX and Transient_GETS in transient states |
| transition(OM, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS}) { |
| m_popRequestQueue; // Even if we have the data, we can pretend we don't have it yet. |
| } |
| |
| transition(IS, {Transient_GETX, Transient_Local_GETX}) { |
| t_sendAckWithCollectedTokens; |
| m_popRequestQueue; |
| } |
| |
| transition(IS, {Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS}) { |
| m_popRequestQueue; |
| } |
| |
| transition(IS, {Persistent_GETX, Persistent_GETS, Persistent_GETS_Last_Token}, IS_L) { |
| e_sendAckWithCollectedTokens; |
| l_popPersistentQueue; |
| } |
| |
| transition(IS_L, {Persistent_GETX, Persistent_GETS}) { |
| l_popPersistentQueue; |
| } |
| |
| transition(IM, {Persistent_GETX, Persistent_GETS, Persistent_GETS_Last_Token}, IM_L) { |
| e_sendAckWithCollectedTokens; |
| l_popPersistentQueue; |
| } |
| |
| transition(IM_L, {Persistent_GETX, Persistent_GETS}) { |
| l_popPersistentQueue; |
| } |
| |
| transition({SM, SM_L}, Persistent_GETX, IM_L) { |
| e_sendAckWithCollectedTokens; |
| forward_eviction_to_cpu |
| l_popPersistentQueue; |
| } |
| |
| transition(SM, {Persistent_GETS, Persistent_GETS_Last_Token}, SM_L) { |
| f_sendAckWithAllButNorOneTokens; |
| l_popPersistentQueue; |
| } |
| |
| transition(SM_L, {Persistent_GETS, Persistent_GETS_Last_Token}) { |
| l_popPersistentQueue; |
| } |
| |
| transition(OM, Persistent_GETX, IM_L) { |
| ee_sendDataWithAllTokens; |
| forward_eviction_to_cpu |
| l_popPersistentQueue; |
| } |
| |
| transition(OM, Persistent_GETS, SM_L) { |
| ff_sendDataWithAllButNorOneTokens; |
| l_popPersistentQueue; |
| } |
| |
| transition(OM, Persistent_GETS_Last_Token, IM_L) { |
| fo_sendDataWithOwnerToken; |
| l_popPersistentQueue; |
| } |
| |
| // Transitions from IM/SM |
| |
| transition({IM, SM}, Ack) { |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(IM, Data_Shared, SM) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(IM, Data_Owner, OM) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(IM, Data_All_Tokens, MM_W) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| xx_external_store_hit; |
| o_scheduleUseTimeout; |
| j_unsetReissueTimer; |
| n_popResponseQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(SM, Data_Shared) { |
| w_assertIncomingDataAndCacheDataMatch; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(SM, Data_Owner, OM) { |
| w_assertIncomingDataAndCacheDataMatch; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(SM, Data_All_Tokens, MM_W) { |
| w_assertIncomingDataAndCacheDataMatch; |
| q_updateTokensFromResponse; |
| xx_external_store_hit; |
| o_scheduleUseTimeout; |
| j_unsetReissueTimer; |
| n_popResponseQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition({IM, SM}, {Transient_GETX, Transient_Local_GETX}, IM) { // We don't have the data yet, but we might have collected some tokens. We give them up here to avoid livelock |
| t_sendAckWithCollectedTokens; |
| forward_eviction_to_cpu; |
| m_popRequestQueue; |
| } |
| |
| transition({IM, SM}, {Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS}) { |
| m_popRequestQueue; |
| } |
| |
| transition({IM, SM}, Request_Timeout) { |
| j_unsetReissueTimer; |
| b_issueWriteRequest; |
| } |
| |
| // Transitions from OM |
| |
| transition(OM, Ack) { |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(OM, Ack_All_Tokens, MM_W) { |
| q_updateTokensFromResponse; |
| xx_external_store_hit; |
| o_scheduleUseTimeout; |
| j_unsetReissueTimer; |
| n_popResponseQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(OM, Data_Shared) { |
| w_assertIncomingDataAndCacheDataMatch; |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(OM, Data_All_Tokens, MM_W) { |
| w_assertIncomingDataAndCacheDataMatch; |
| q_updateTokensFromResponse; |
| xx_external_store_hit; |
| o_scheduleUseTimeout; |
| j_unsetReissueTimer; |
| n_popResponseQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(OM, Request_Timeout) { |
| j_unsetReissueTimer; |
| b_issueWriteRequest; |
| } |
| |
| // Transitions from IS |
| |
| transition(IS, Ack) { |
| q_updateTokensFromResponse; |
| n_popResponseQueue; |
| } |
| |
| transition(IS, Data_Shared, S) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| x_external_load_hit; |
| s_deallocateTBE; |
| j_unsetReissueTimer; |
| n_popResponseQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(IS, Data_Owner, O) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| x_external_load_hit; |
| s_deallocateTBE; |
| j_unsetReissueTimer; |
| n_popResponseQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(IS, Data_All_Tokens, M_W) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| x_external_load_hit; |
| o_scheduleUseTimeout; |
| j_unsetReissueTimer; |
| n_popResponseQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(IS, Request_Timeout) { |
| j_unsetReissueTimer; |
| a_issueReadRequest; |
| } |
| |
| // Transitions from I_L |
| |
| transition(I_L, Load, IS_L) { |
| ii_allocateL1DCacheBlock; |
| i_allocateTBE; |
| a_issueReadRequest; |
| uu_profileDataMiss; |
| k_popMandatoryQueue; |
| } |
| |
| transition(I_L, Ifetch, IS_L) { |
| pp_allocateL1ICacheBlock; |
| i_allocateTBE; |
| a_issueReadRequest; |
| uu_profileInstMiss; |
| k_popMandatoryQueue; |
| } |
| |
| transition(I_L, {Store, Atomic}, IM_L) { |
| ii_allocateL1DCacheBlock; |
| i_allocateTBE; |
| b_issueWriteRequest; |
| uu_profileDataMiss; |
| k_popMandatoryQueue; |
| } |
| |
| |
| // Transitions from S_L |
| |
| transition(S_L, {Store, Atomic}, SM_L) { |
| i_allocateTBE; |
| b_issueWriteRequest; |
| uu_profileDataMiss; |
| k_popMandatoryQueue; |
| } |
| |
| // Other transitions from *_L states |
| |
| transition({I_L, IM_L, IS_L, S_L, SM_L}, {Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS, Transient_GETX, Transient_Local_GETX}) { |
| m_popRequestQueue; |
| } |
| |
| transition({I_L, IM_L, IS_L, S_L, SM_L}, Ack) { |
| g_bounceResponseToStarver; |
| n_popResponseQueue; |
| } |
| |
| transition({I_L, IM_L, S_L, SM_L}, {Data_Shared, Data_Owner}) { |
| g_bounceResponseToStarver; |
| n_popResponseQueue; |
| } |
| |
| transition({I_L, S_L}, Data_All_Tokens) { |
| g_bounceResponseToStarver; |
| n_popResponseQueue; |
| } |
| |
| transition(IS_L, Request_Timeout) { |
| j_unsetReissueTimer; |
| a_issueReadRequest; |
| } |
| |
| transition({IM_L, SM_L}, Request_Timeout) { |
| j_unsetReissueTimer; |
| b_issueWriteRequest; |
| } |
| |
| // Opportunisticly Complete the memory operation in the following |
| // cases. Note: these transitions could just use |
| // g_bounceResponseToStarver, but if we have the data and tokens, we |
| // might as well complete the memory request while we have the |
| // chance (and then immediately forward on the data) |
| |
| transition(IM_L, Data_All_Tokens, MM_W) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| xx_external_store_hit; |
| j_unsetReissueTimer; |
| o_scheduleUseTimeout; |
| n_popResponseQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(SM_L, Data_All_Tokens, S_L) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| xx_external_store_hit; |
| ff_sendDataWithAllButNorOneTokens; |
| s_deallocateTBE; |
| j_unsetReissueTimer; |
| n_popResponseQueue; |
| } |
| |
| transition(IS_L, Data_Shared, I_L) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| x_external_load_hit; |
| s_deallocateTBE; |
| e_sendAckWithCollectedTokens; |
| p_informL2AboutTokenLoss; |
| j_unsetReissueTimer; |
| n_popResponseQueue; |
| } |
| |
| transition(IS_L, Data_Owner, I_L) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| x_external_load_hit; |
| ee_sendDataWithAllTokens; |
| s_deallocateTBE; |
| p_informL2AboutTokenLoss; |
| j_unsetReissueTimer; |
| n_popResponseQueue; |
| } |
| |
| transition(IS_L, Data_All_Tokens, M_W) { |
| u_writeDataToCache; |
| q_updateTokensFromResponse; |
| x_external_load_hit; |
| j_unsetReissueTimer; |
| o_scheduleUseTimeout; |
| n_popResponseQueue; |
| kd_wakeUpDependents; |
| } |
| |
| // Own_Lock_or_Unlock |
| |
| transition(I_L, Own_Lock_or_Unlock, I) { |
| l_popPersistentQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(S_L, Own_Lock_or_Unlock, S) { |
| l_popPersistentQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(IM_L, Own_Lock_or_Unlock, IM) { |
| l_popPersistentQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(IS_L, Own_Lock_or_Unlock, IS) { |
| l_popPersistentQueue; |
| kd_wakeUpDependents; |
| } |
| |
| transition(SM_L, Own_Lock_or_Unlock, SM) { |
| l_popPersistentQueue; |
| kd_wakeUpDependents; |
| } |
| } |