/* * Copyright (c) 1999-2005 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") : int l1_request_latency, int l1_response_latency, int l2_select_low_bit, int l2_select_num_bits, int N_tokens, int retry_threshold, int fixed_timeout_latency, bool dynamic_timeout_enabled { // From this node's L1 cache TO the network // a local L1 -> this L2 bank MessageBuffer responseFromL1Cache, network="To", virtual_network="1", ordered="false"; MessageBuffer persistentFromL1Cache, network="To", virtual_network="2", ordered="true"; // a local L1 -> this L2 bank, currently ordered with directory forwarded requests MessageBuffer requestFromL1Cache, network="To", virtual_network="4", ordered="false"; // To this node's L1 cache FROM the network // a L2 bank -> this L1 MessageBuffer responseToL1Cache, network="From", virtual_network="1", ordered="false"; MessageBuffer persistentToL1Cache, network="From", virtual_network="2", ordered="true"; // a L2 bank -> this L1 MessageBuffer requestToL1Cache, network="From", virtual_network="4", ordered="false"; // STATES enumeration(State, desc="Cache states", default="L1Cache_State_I") { // Base states NP, "NP", desc="Not Present"; I, "I", desc="Idle"; S, "S", desc="Shared"; O, "O", desc="Owned"; M, "M", desc="Modified (dirty)"; MM, "MM", desc="Modified (dirty and locally modified)"; M_W, "M^W", desc="Modified (dirty), waiting"; MM_W, "MM^W", desc="Modified (dirty and locally modified), waiting"; // Transient States IM, "IM", desc="Issued GetX"; SM, "SM", desc="Issued GetX, we still have an old copy of the line"; OM, "OM", desc="Issued GetX, received data"; IS, "IS", desc="Issued GetS"; // Locked states I_L, "I^L", desc="Invalid, Locked"; S_L, "S^L", desc="Shared, Locked"; IM_L, "IM^L", desc="Invalid, Locked, trying to go to Modified"; SM_L, "SM^L", desc="Shared, Locked, trying to go to Modified"; IS_L, "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"; 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"; 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"; } // 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 Address, 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"; AccessType AccessType, desc="Type of request (used for profiling)"; Time IssueTime, desc="Time the request was issued"; AccessModeType AccessMode, desc="user/supervisor access type"; PrefetchBit Prefetch, desc="Is this a prefetch request"; } external_type(CacheMemory) { bool cacheAvail(Address); Address cacheProbe(Address); void allocate(Address, Entry); void deallocate(Address); Entry lookup(Address); void changePermission(Address, AccessPermission); bool isTagPresent(Address); } external_type(TBETable) { TBE lookup(Address); void allocate(Address); void deallocate(Address); bool isPresent(Address); } external_type(PersistentTable) { 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); } TBETable L1_TBEs, template_hack=""; CacheMemory L1IcacheMemory, factory='RubySystem::getCache(m_cfg["icache"])'; CacheMemory L1DcacheMemory, factory='RubySystem::getCache(m_cfg["dcache"])'; MessageBuffer mandatoryQueue, ordered="false", abstract_chip_ptr="true"; Sequencer sequencer, factory='RubySystem::getSequencer(m_cfg["sequencer"])'; bool starving, default="false"; PersistentTable persistentTable; TimerTable useTimerTable; TimerTable reissueTimerTable; int outstandingRequests, default="0"; int outstandingPersistentRequests, default="0"; int averageLatencyHysteresis, default="(8)"; // Constant that provides hysteresis for calculated the estimated average int averageLatencyCounter, default="(500 << (*m_L1Cache_averageLatencyHysteresis_ptr))"; int averageLatencyEstimate() { DEBUG_EXPR( (averageLatencyCounter >> averageLatencyHysteresis) ); //profile_average_latency_estimate( (averageLatencyCounter >> averageLatencyHysteresis) ); return averageLatencyCounter >> averageLatencyHysteresis; } void updateAverageLatencyEstimate(int latency) { DEBUG_EXPR( latency ); assert(latency >= 0); // 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_ref="yes" { if (L1DcacheMemory.isTagPresent(addr)) { return L1DcacheMemory[addr]; } else { return L1IcacheMemory[addr]; } } int getTokens(Address addr) { if (L1DcacheMemory.isTagPresent(addr)) { return L1DcacheMemory[addr].Tokens; } else if (L1IcacheMemory.isTagPresent(addr)) { return L1IcacheMemory[addr].Tokens; } else { return 0; } } void changePermission(Address addr, AccessPermission permission) { if (L1DcacheMemory.isTagPresent(addr)) { return L1DcacheMemory.changePermission(addr, permission); } else { return L1IcacheMemory.changePermission(addr, permission); } } bool isCacheTagPresent(Address addr) { return (L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr)); } State getState(Address addr) { assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); if (L1_TBEs.isPresent(addr)) { return L1_TBEs[addr].TBEState; } else if (isCacheTagPresent(addr)) { return getCacheEntry(addr).CacheState; } else { if ((persistentTable.isLocked(addr) == true) && (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(Address addr, State state) { assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); if (L1_TBEs.isPresent(addr)) { assert(state != State:I); assert(state != State:S); assert(state != State:O); assert(state != State:MM); assert(state != State:M); L1_TBEs[addr].TBEState := state; } if (isCacheTagPresent(addr)) { // Make sure the token count is in range assert(getCacheEntry(addr).Tokens >= 0); assert(getCacheEntry(addr).Tokens <= max_tokens()); if ((state == State:I_L) || (state == State:IM_L) || (state == State:IS_L)) { // Make sure we have no tokens in the "Invalid, locked" states if (isCacheTagPresent(addr)) { assert(getCacheEntry(addr).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(getCacheEntry(addr).Tokens >= 1); // 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(getCacheEntry(addr).Tokens == max_tokens()); } // in NP you have no tokens if (state == State:NP) { assert(getCacheEntry(addr).Tokens == 0); } // You have at least one token in S-like states if (state == State:S || state == State:SM) { assert(getCacheEntry(addr).Tokens > 0); } // You have at least half the token in O-like states if (state == State:O && state == State:OM) { assert(getCacheEntry(addr).Tokens >= 1); // Must have at least one token assert(getCacheEntry(addr).Tokens >= (max_tokens() / 2)); // Only mostly true; this might not always hold } getCacheEntry(addr).CacheState := state; // Set permission if (state == State:MM || state == State:MM_W) { changePermission(addr, AccessPermission:Read_Write); } else if ((state == State:S) || (state == State:O) || (state == State:M) || (state == State:M_W) || (state == State:SM) || (state == State:S_L) || (state == State:SM_L) || (state == State:OM)) { changePermission(addr, AccessPermission:Read_Only); } else { changePermission(addr, AccessPermission:Invalid); } } } Event mandatory_request_type_to_event(CacheRequestType type) { if (type == CacheRequestType:LD) { return Event:Load; } else if (type == CacheRequestType:IFETCH) { return Event:Ifetch; } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { return Event:Store; } else { error("Invalid CacheRequestType"); } } AccessType cache_request_type_to_access_type(CacheRequestType type) { if ((type == CacheRequestType:LD) || (type == CacheRequestType:IFETCH)) { return AccessType:Read; } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { return AccessType:Write; } else { error("Invalid CacheRequestType"); } } // GenericMachineType getNondirectHitMachType(Address addr, MachineID sender) { // if (machineIDToMachineType(sender) == MachineType:L1Cache) { // return GenericMachineType:L1Cache_wCC; // NOTE direct L1 hits should not call this // } else if (machineIDToMachineType(sender) == MachineType:L2Cache) { // // if (sender == (mapAddressToRange(addr, // MachineType:L2Cache, // l2_select_low_bit, // l2_select_num_bits))) { // // return GenericMachineType:L2Cache; // } else { // return GenericMachineType:L2Cache_wCC; // } // } else { // return ConvertMachToGenericMach(machineIDToMachineType(sender)); // } // } bool okToIssueStarving(Address addr, MachineID machinID) { return persistentTable.okToIssueStarving(addr, machineID); } void markPersistentEntries(Address addr) { persistentTable.markEntries(addr); } // ** 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) { if (useTimerTable_in.isReady()) { if (persistentTable.isLocked(useTimerTable.readyAddress()) && (persistentTable.findSmallest(useTimerTable.readyAddress()) != machineID)) { if (persistentTable.typeOfSmallest(useTimerTable.readyAddress()) == AccessType:Write) { trigger(Event:Use_TimeoutStarverX, useTimerTable.readyAddress()); } else { trigger(Event:Use_TimeoutStarverS, useTimerTable.readyAddress()); } } else { trigger(Event:Use_TimeoutNoStarvers, useTimerTable.readyAddress()); } } } // Reissue Timer in_port(reissueTimerTable_in, Address, reissueTimerTable) { if (reissueTimerTable_in.isReady()) { trigger(Event:Request_Timeout, reissueTimerTable.readyAddress()); } } // Persistent Network in_port(persistentNetwork_in, PersistentMsg, persistentToL1Cache) { if (persistentNetwork_in.isReady()) { peek(persistentNetwork_in, PersistentMsg) { 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.Address, in_msg.Requestor, AccessType:Write); } else if (in_msg.Type == PersistentRequestType:GETS_PERSISTENT) { persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Read); } else if (in_msg.Type == PersistentRequestType:DEACTIVATE_PERSISTENT) { persistentTable.persistentRequestUnlock(in_msg.Address, in_msg.Requestor); } else { error("Unexpected message"); } // React to the message based on the current state of the table if (persistentTable.isLocked(in_msg.Address)) { if (persistentTable.findSmallest(in_msg.Address) == machineID) { // Our Own Lock - this processor is highest priority trigger(Event:Own_Lock_or_Unlock, in_msg.Address); } else { if (persistentTable.typeOfSmallest(in_msg.Address) == AccessType:Read) { trigger(Event:Persistent_GETS, in_msg.Address); } else { trigger(Event:Persistent_GETX, in_msg.Address); } } } else { // Unlock case - no entries in the table trigger(Event:Own_Lock_or_Unlock, in_msg.Address); } } } } // Request Network in_port(requestNetwork_in, RequestMsg, requestToL1Cache) { if (requestNetwork_in.isReady()) { peek(requestNetwork_in, RequestMsg) { assert(in_msg.Destination.isElement(machineID)); if (in_msg.Type == CoherenceRequestType:GETX) { if (in_msg.isLocal) { trigger(Event:Transient_Local_GETX, in_msg.Address); } else { trigger(Event:Transient_GETX, in_msg.Address); } } else if (in_msg.Type == CoherenceRequestType:GETS) { if ( (L1DcacheMemory.isTagPresent(in_msg.Address) || L1IcacheMemory.isTagPresent(in_msg.Address)) && getCacheEntry(in_msg.Address).Tokens == 1) { if (in_msg.isLocal) { trigger(Event:Transient_Local_GETS_Last_Token, in_msg.Address); } else { trigger(Event:Transient_GETS_Last_Token, in_msg.Address); } } else { if (in_msg.isLocal) { trigger(Event:Transient_Local_GETS, in_msg.Address); } else { trigger(Event:Transient_GETS, in_msg.Address); } } } else { error("Unexpected message"); } } } } // Response Network in_port(responseNetwork_in, ResponseMsg, responseToL1Cache) { if (responseNetwork_in.isReady()) { peek(responseNetwork_in, ResponseMsg) { assert(in_msg.Destination.isElement(machineID)); // Mark TBE flag if response received off-chip. Use this to update average latency estimate if ( in_msg.SenderMachine == MachineType:L2Cache ) { if (in_msg.Sender == mapAddressToRange(in_msg.Address, MachineType:L2Cache, l2_select_low_bit, l2_select_num_bits)) { // came from an off-chip L2 cache if (L1_TBEs.isPresent(in_msg.Address)) { // L1_TBEs[in_msg.Address].ExternalResponse := true; // profile_offchipL2_response(in_msg.Address); } } else { // profile_onchipL2_response(in_msg.Address ); } } else if ( in_msg.SenderMachine == MachineType:Directory ) { if (L1_TBEs.isPresent(in_msg.Address)) { L1_TBEs[in_msg.Address].ExternalResponse := true; // profile_memory_response( in_msg.Address); } } else if ( in_msg.SenderMachine == MachineType:L1Cache) { //if (isLocalProcessor(machineID, in_msg.Sender) == false) { //if (L1_TBEs.isPresent(in_msg.Address)) { // L1_TBEs[in_msg.Address].ExternalResponse := true; // profile_offchipL1_response(in_msg.Address ); //} //} //else { // profile_onchipL1_response(in_msg.Address ); //} } else { error("unexpected SenderMachine"); } if (getTokens(in_msg.Address) + in_msg.Tokens != max_tokens()) { if (in_msg.Type == CoherenceResponseType:ACK) { trigger(Event:Ack, in_msg.Address); } else if (in_msg.Type == CoherenceResponseType:DATA_OWNER) { trigger(Event:Data_Owner, in_msg.Address); } else if (in_msg.Type == CoherenceResponseType:DATA_SHARED) { trigger(Event:Data_Shared, in_msg.Address); } else { error("Unexpected message"); } } else { if (in_msg.Type == CoherenceResponseType:ACK) { trigger(Event:Ack_All_Tokens, in_msg.Address); } else if (in_msg.Type == CoherenceResponseType:DATA_OWNER || in_msg.Type == CoherenceResponseType:DATA_SHARED) { trigger(Event:Data_All_Tokens, in_msg.Address); } else { error("Unexpected message"); } } } } } // Mandatory Queue in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { if (mandatoryQueue_in.isReady()) { peek(mandatoryQueue_in, CacheMsg) { // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache if (in_msg.Type == CacheRequestType:IFETCH) { // ** INSTRUCTION ACCESS *** // Check to see if it is in the OTHER L1 if (L1DcacheMemory.isTagPresent(in_msg.LineAddress)) { // The block is in the wrong L1, try to write it to the L2 trigger(Event:L1_Replacement, in_msg.LineAddress); } if (L1IcacheMemory.isTagPresent(in_msg.LineAddress)) { // 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); } else { if (L1IcacheMemory.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); } else { // No room in the L1, so we need to make room trigger(Event:L1_Replacement, L1IcacheMemory.cacheProbe(in_msg.LineAddress)); } } } else { // *** DATA ACCESS *** // Check to see if it is in the OTHER L1 if (L1IcacheMemory.isTagPresent(in_msg.LineAddress)) { // The block is in the wrong L1, try to write it to the L2 trigger(Event:L1_Replacement, in_msg.LineAddress); } if (L1DcacheMemory.isTagPresent(in_msg.LineAddress)) { // 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); } else { if (L1DcacheMemory.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); } else { // No room in the L1, so we need to make room trigger(Event:L1_Replacement, L1DcacheMemory.cacheProbe(in_msg.LineAddress)); } } } } } } // ACTIONS action(a_issueReadRequest, "a", desc="Issue GETS") { if (L1_TBEs[address].IssueCount == 0) { // Update outstanding requests //profile_outstanding_request(outstandingRequests); outstandingRequests := outstandingRequests + 1; } if (L1_TBEs[address].IssueCount >= retry_threshold) { // Issue a persistent request if possible if (okToIssueStarving(address, machineID) && (starving == false)) { enqueue(persistentNetwork_out, PersistentMsg, latency = l1_request_latency) { out_msg.Address := 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)); out_msg.Destination.add(map_Address_to_Directory(address)); out_msg.MessageSize := MessageSizeType:Persistent_Control; out_msg.Prefetch := L1_TBEs[address].Prefetch; out_msg.AccessMode := L1_TBEs[address].AccessMode; } markPersistentEntries(address); starving := true; if (L1_TBEs[address].IssueCount == 0) { //profile_persistent_prediction(address, L1_TBEs[address].AccessType); } // Update outstanding requests //profile_outstanding_persistent_request(outstandingPersistentRequests); outstandingPersistentRequests := outstandingPersistentRequests + 1; // Increment IssueCount L1_TBEs[address].IssueCount := L1_TBEs[address].IssueCount + 1; L1_TBEs[address].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, 10); } } else { // Make a normal request enqueue(requestNetwork_out, RequestMsg, latency = l1_request_latency) { out_msg.Address := 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)); out_msg.RetryNum := L1_TBEs[address].IssueCount; if (L1_TBEs[address].IssueCount == 0) { out_msg.MessageSize := MessageSizeType:Request_Control; } else { out_msg.MessageSize := MessageSizeType:Reissue_Control; } out_msg.Prefetch := L1_TBEs[address].Prefetch; out_msg.AccessMode := L1_TBEs[address].AccessMode; } // send to other local L1s, with local bit set enqueue(requestNetwork_out, RequestMsg, latency = l1_request_latency) { out_msg.Address := 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 := L1_TBEs[address].IssueCount; out_msg.isLocal := true; if (L1_TBEs[address].IssueCount == 0) { out_msg.MessageSize := MessageSizeType:Request_Control; } else { out_msg.MessageSize := MessageSizeType:Reissue_Control; } out_msg.Prefetch := L1_TBEs[address].Prefetch; out_msg.AccessMode := L1_TBEs[address].AccessMode; } // Increment IssueCount L1_TBEs[address].IssueCount := L1_TBEs[address].IssueCount + 1; // Set a wakeup timer if (dynamic_timeout_enabled) { reissueTimerTable.set(address, 1.25 * averageLatencyEstimate()); } else { reissueTimerTable.set(address, fixed_timeout_latency); } } } action(b_issueWriteRequest, "b", desc="Issue GETX") { if (L1_TBEs[address].IssueCount == 0) { // Update outstanding requests //profile_outstanding_request(outstandingRequests); outstandingRequests := outstandingRequests + 1; } if (L1_TBEs[address].IssueCount >= retry_threshold) { // Issue a persistent request if possible if ( okToIssueStarving(address, machineID) && (starving == false)) { enqueue(persistentNetwork_out, PersistentMsg, latency = l1_request_latency) { out_msg.Address := address; out_msg.Type := PersistentRequestType:GETX_PERSISTENT; out_msg.Requestor := machineID; out_msg.RequestorMachine := MachineType:L1Cache; 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)); out_msg.Destination.add(map_Address_to_Directory(address)); out_msg.MessageSize := MessageSizeType:Persistent_Control; out_msg.Prefetch := L1_TBEs[address].Prefetch; out_msg.AccessMode := L1_TBEs[address].AccessMode; } markPersistentEntries(address); starving := true; // Update outstanding requests //profile_outstanding_persistent_request(outstandingPersistentRequests); outstandingPersistentRequests := outstandingPersistentRequests + 1; if (L1_TBEs[address].IssueCount == 0) { //profile_persistent_prediction(address, L1_TBEs[address].AccessType); } // Increment IssueCount L1_TBEs[address].IssueCount := L1_TBEs[address].IssueCount + 1; L1_TBEs[address].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, 10); } } else { // Make a normal request enqueue(requestNetwork_out, RequestMsg, latency = l1_request_latency) { out_msg.Address := address; out_msg.Type := CoherenceRequestType:GETX; out_msg.Requestor := machineID; out_msg.RequestorMachine := MachineType:L1Cache; out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache, l2_select_low_bit, l2_select_num_bits)); out_msg.RetryNum := L1_TBEs[address].IssueCount; if (L1_TBEs[address].IssueCount == 0) { out_msg.MessageSize := MessageSizeType:Request_Control; } else { out_msg.MessageSize := MessageSizeType:Reissue_Control; } out_msg.Prefetch := L1_TBEs[address].Prefetch; out_msg.AccessMode := L1_TBEs[address].AccessMode; } // send to other local L1s too enqueue(requestNetwork_out, RequestMsg, latency = l1_request_latency) { out_msg.Address := 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 := L1_TBEs[address].IssueCount; if (L1_TBEs[address].IssueCount == 0) { out_msg.MessageSize := MessageSizeType:Request_Control; } else { out_msg.MessageSize := MessageSizeType:Reissue_Control; } out_msg.Prefetch := L1_TBEs[address].Prefetch; out_msg.AccessMode := L1_TBEs[address].AccessMode; } // Increment IssueCount L1_TBEs[address].IssueCount := L1_TBEs[address].IssueCount + 1; DEBUG_EXPR("incremented issue count"); DEBUG_EXPR(L1_TBEs[address].IssueCount); // Set a wakeup timer if (dynamic_timeout_enabled) { reissueTimerTable.set(address, 1.25 * averageLatencyEstimate()); } 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, latency="1") { out_msg.Address := address; out_msg.Type := in_msg.Type; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; 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") { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache, l2_select_low_bit, l2_select_num_bits)); out_msg.Tokens := getCacheEntry(address).Tokens; out_msg.DataBlk := getCacheEntry(address).DataBlk; out_msg.Dirty := getCacheEntry(address).Dirty; out_msg.Type := CoherenceResponseType:WB_OWNED; // always send the data? out_msg.MessageSize := MessageSizeType:Writeback_Data; } getCacheEntry(address).Tokens := 0; } action(cc_sharedReplacement, "\c", desc="Issue dirty writeback") { // don't send writeback if replacing block with no tokens if (getCacheEntry(address).Tokens != 0) { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache, l2_select_low_bit, l2_select_num_bits)); out_msg.Tokens := getCacheEntry(address).Tokens; out_msg.DataBlk := getCacheEntry(address).DataBlk; // assert(getCacheEntry(address).Dirty == false); out_msg.Dirty := false; // always send the data? if (getCacheEntry(address).Tokens > 1) { out_msg.MessageSize := MessageSizeType:Writeback_Data; out_msg.Type := CoherenceResponseType:WB_SHARED_DATA; } else { out_msg.MessageSize := MessageSizeType:Writeback_Control; out_msg.Type := CoherenceResponseType:WB_TOKENS; } } getCacheEntry(address).Tokens := 0; } } action(d_sendDataWithToken, "d", desc="Send data and a token from cache to requestor") { peek(requestNetwork_in, RequestMsg) { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Type := CoherenceResponseType:DATA_SHARED; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(in_msg.Requestor); out_msg.Tokens := 1; out_msg.DataBlk := getCacheEntry(address).DataBlk; // out_msg.Dirty := getCacheEntry(address).Dirty; out_msg.Dirty := false; if (in_msg.isLocal) { out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; } else { out_msg.MessageSize := MessageSizeType:Response_Data; } } } getCacheEntry(address).Tokens := getCacheEntry(address).Tokens - 1; assert(getCacheEntry(address).Tokens >= 1); } action(d_sendDataWithNTokenIfAvail, "\dd", desc="Send data and a token from cache to requestor") { peek(requestNetwork_in, RequestMsg) { if (getCacheEntry(address).Tokens > N_tokens) { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Type := CoherenceResponseType:DATA_SHARED; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(in_msg.Requestor); out_msg.Tokens := N_tokens; out_msg.DataBlk := getCacheEntry(address).DataBlk; // out_msg.Dirty := getCacheEntry(address).Dirty; out_msg.Dirty := false; if (in_msg.isLocal) { out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; } else { out_msg.MessageSize := MessageSizeType:Response_Data; } } getCacheEntry(address).Tokens := getCacheEntry(address).Tokens - N_tokens; } else if (getCacheEntry(address).Tokens > 1) { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Type := CoherenceResponseType:DATA_SHARED; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(in_msg.Requestor); out_msg.Tokens := 1; out_msg.DataBlk := getCacheEntry(address).DataBlk; // out_msg.Dirty := getCacheEntry(address).Dirty; out_msg.Dirty := false; if (in_msg.isLocal) { out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; } else { out_msg.MessageSize := MessageSizeType:Response_Data; } } getCacheEntry(address).Tokens := getCacheEntry(address).Tokens - 1; } } // assert(getCacheEntry(address).Tokens >= 1); } action(dd_sendDataWithAllTokens, "\d", desc="Send data and all tokens from cache to requestor") { peek(requestNetwork_in, RequestMsg) { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Type := CoherenceResponseType:DATA_OWNER; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(in_msg.Requestor); assert(getCacheEntry(address).Tokens >= 1); out_msg.Tokens := getCacheEntry(address).Tokens; out_msg.DataBlk := getCacheEntry(address).DataBlk; out_msg.Dirty := getCacheEntry(address).Dirty; if (in_msg.isLocal) { out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; } else { out_msg.MessageSize := MessageSizeType:Response_Data; } } } getCacheEntry(address).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 if (getCacheEntry(address).Tokens > 0) { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Type := CoherenceResponseType:ACK; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(persistentTable.findSmallest(address)); assert(getCacheEntry(address).Tokens >= 1); out_msg.Tokens := getCacheEntry(address).Tokens; out_msg.MessageSize := MessageSizeType:Response_Control; } } getCacheEntry(address).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(getCacheEntry(address).Tokens > 0); enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Type := CoherenceResponseType:DATA_OWNER; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(persistentTable.findSmallest(address)); assert(getCacheEntry(address).Tokens >= 1); out_msg.Tokens := getCacheEntry(address).Tokens; out_msg.DataBlk := getCacheEntry(address).DataBlk; out_msg.Dirty := getCacheEntry(address).Dirty; out_msg.MessageSize := MessageSizeType:Response_Data; } getCacheEntry(address).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(getCacheEntry(address).Tokens > 0); if (getCacheEntry(address).Tokens > 1) { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Type := CoherenceResponseType:ACK; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(persistentTable.findSmallest(address)); assert(getCacheEntry(address).Tokens >= 1); if (getCacheEntry(address).Tokens > N_tokens) { out_msg.Tokens := getCacheEntry(address).Tokens - N_tokens; } else { out_msg.Tokens := getCacheEntry(address).Tokens - 1; } out_msg.MessageSize := MessageSizeType:Response_Control; } } if (getCacheEntry(address).Tokens > N_tokens) { getCacheEntry(address).Tokens := N_tokens; } else { getCacheEntry(address).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(getCacheEntry(address).Tokens > 0); if (getCacheEntry(address).Tokens > 1) { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Type := CoherenceResponseType:DATA_OWNER; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(persistentTable.findSmallest(address)); assert(getCacheEntry(address).Tokens >= 1); if (getCacheEntry(address).Tokens > N_tokens) { out_msg.Tokens := getCacheEntry(address).Tokens - N_tokens; } else { out_msg.Tokens := getCacheEntry(address).Tokens - 1; } out_msg.DataBlk := getCacheEntry(address).DataBlk; out_msg.Dirty := getCacheEntry(address).Dirty; out_msg.MessageSize := MessageSizeType:Response_Data; } if (getCacheEntry(address).Tokens > N_tokens) { getCacheEntry(address).Tokens := N_tokens; } else { getCacheEntry(address).Tokens := 1; } } } 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, latency="1") { out_msg.Address := address; out_msg.Type := in_msg.Type; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; 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.") { DEBUG_EXPR(address); DEBUG_EXPR(getCacheEntry(address).DataBlk); //sequencer.readCallback(address, getCacheEntry(address).DataBlk, GenericMachineType:L1Cache, PrefetchBit:No); sequencer.readCallback(address, getCacheEntry(address).DataBlk); } action(x_external_load_hit, "x", desc="Notify sequencer the load completed.") { DEBUG_EXPR(address); DEBUG_EXPR(getCacheEntry(address).DataBlk); peek(responseNetwork_in, ResponseMsg) { //sequencer.readCallback(address, getCacheEntry(address).DataBlk, getNondirectHitMachType(in_msg.Address, in_msg.Sender), PrefetchBit:No); sequencer.readCallback(address, getCacheEntry(address).DataBlk); } } action(hh_store_hit, "\h", desc="Notify sequencer that store completed.") { DEBUG_EXPR(address); DEBUG_EXPR(getCacheEntry(address).DataBlk); //sequencer.writeCallback(address, getCacheEntry(address).DataBlk, GenericMachineType:L1Cache, PrefetchBit:No); sequencer.writeCallback(address, getCacheEntry(address).DataBlk); getCacheEntry(address).Dirty := true; DEBUG_EXPR(getCacheEntry(address).DataBlk); } action(xx_external_store_hit, "\x", desc="Notify sequencer that store completed.") { DEBUG_EXPR(address); DEBUG_EXPR(getCacheEntry(address).DataBlk); peek(responseNetwork_in, ResponseMsg) { //sequencer.writeCallback(address, getCacheEntry(address).DataBlk, getNondirectHitMachType(in_msg.Address, in_msg.Sender), PrefetchBit:No); sequencer.writeCallback(address, getCacheEntry(address).DataBlk); } getCacheEntry(address).Dirty := true; DEBUG_EXPR(getCacheEntry(address).DataBlk); } action(i_allocateTBE, "i", desc="Allocate TBE") { check_allocate(L1_TBEs); L1_TBEs.allocate(address); L1_TBEs[address].IssueCount := 0; peek(mandatoryQueue_in, CacheMsg) { L1_TBEs[address].PC := in_msg.ProgramCounter; L1_TBEs[address].AccessType := cache_request_type_to_access_type(in_msg.Type); L1_TBEs[address].Prefetch := in_msg.Prefetch; L1_TBEs[address].AccessMode := in_msg.AccessMode; } L1_TBEs[address].IssueTime := get_time(); } 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, 50); } action(p_informL2AboutTokenLoss, "p", desc="Inform L2 about loss of all tokens") { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Type := CoherenceResponseType:INV; out_msg.Tokens := 0; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.DestMachine := MachineType:L2Cache; out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache, l2_select_low_bit, l2_select_num_bits)); 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(in_msg.Tokens != 0); DEBUG_EXPR("MRM_DEBUG L1 received tokens"); DEBUG_EXPR(in_msg.Address); DEBUG_EXPR(in_msg.Tokens); getCacheEntry(address).Tokens := getCacheEntry(address).Tokens + in_msg.Tokens; DEBUG_EXPR(getCacheEntry(address).Tokens); if (getCacheEntry(address).Dirty == false && in_msg.Dirty) { getCacheEntry(address).Dirty := true; } } } action(s_deallocateTBE, "s", desc="Deallocate TBE") { if (L1_TBEs[address].WentPersistent) { // assert(starving == true); outstandingRequests := outstandingRequests - 1; enqueue(persistentNetwork_out, PersistentMsg, latency = l1_request_latency) { out_msg.Address := address; out_msg.Type := PersistentRequestType:DEACTIVATE_PERSISTENT; out_msg.Requestor := machineID; out_msg.RequestorMachine := MachineType:L1Cache; 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)); out_msg.Destination.add(map_Address_to_Directory(address)); out_msg.MessageSize := MessageSizeType:Persistent_Control; } starving := false; } // Update average latency if (L1_TBEs[address].IssueCount <= 1) { if (L1_TBEs[address].ExternalResponse == true) { updateAverageLatencyEstimate(time_to_int(get_time()) - time_to_int(L1_TBEs[address].IssueTime)); } } // Profile //if (L1_TBEs[address].WentPersistent) { // profile_token_retry(address, L1_TBEs[address].AccessType, 2); //} //else { // profile_token_retry(address, L1_TBEs[address].AccessType, 1); //} //profile_token_retry(address, L1_TBEs[address].AccessType, L1_TBEs[address].IssueCount); L1_TBEs.deallocate(address); } action(t_sendAckWithCollectedTokens, "t", desc="Send ack with the tokens we've collected thus far.") { if (getCacheEntry(address).Tokens > 0) { peek(requestNetwork_in, RequestMsg) { enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) { out_msg.Address := address; out_msg.Type := CoherenceResponseType:ACK; out_msg.Sender := machineID; out_msg.SenderMachine := MachineType:L1Cache; out_msg.Destination.add(in_msg.Requestor); assert(getCacheEntry(address).Tokens >= 1); out_msg.Tokens := getCacheEntry(address).Tokens; out_msg.MessageSize := MessageSizeType:Response_Control; } } } getCacheEntry(address).Tokens := 0; } action(u_writeDataToCache, "u", desc="Write data to cache") { peek(responseNetwork_in, ResponseMsg) { getCacheEntry(address).DataBlk := in_msg.DataBlk; if (getCacheEntry(address).Dirty == false && in_msg.Dirty) { getCacheEntry(address).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.") { if (L1DcacheMemory.isTagPresent(address)) { L1DcacheMemory.deallocate(address); } else { L1IcacheMemory.deallocate(address); } } action(ii_allocateL1DCacheBlock, "\i", desc="Set L1 D-cache tag equal to tag of block B.") { if (L1DcacheMemory.isTagPresent(address) == false) { L1DcacheMemory.allocate(address, new Entry); } } action(pp_allocateL1ICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") { if (L1IcacheMemory.isTagPresent(address) == false) { L1IcacheMemory.allocate(address, new Entry); } } action(uu_profileMiss, "\u", desc="Profile the demand miss") { peek(mandatoryQueue_in, CacheMsg) { // profile_miss(in_msg, id); } } action(w_assertIncomingDataAndCacheDataMatch, "w", desc="Assert that the incoming data and the data in the cache match") { peek(responseNetwork_in, ResponseMsg) { assert(getCacheEntry(address).DataBlk == in_msg.DataBlk); } } action(zz_recycleMandatoryQueue, "\z", desc="Send the head of the mandatory queue to the back of the queue.") { mandatoryQueue_in.recycle(); } //***************************************************** // 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) { zz_recycleMandatoryQueue; } transition({IM, SM, OM, IS, IM_L, IS_L, SM_L}, Store) { zz_recycleMandatoryQueue; } transition({IM, IS, IM_L, IS_L}, {Load, Ifetch}) { zz_recycleMandatoryQueue; } // 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_profileMiss; k_popMandatoryQueue; } transition(NP, Ifetch, IS) { pp_allocateL1ICacheBlock; i_allocateTBE; a_issueReadRequest; uu_profileMiss; k_popMandatoryQueue; } transition(NP, Store, IM) { ii_allocateL1DCacheBlock; i_allocateTBE; b_issueWriteRequest; uu_profileMiss; 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}, I_L) { l_popPersistentQueue; } // Transitions from Idle transition(I, Load, IS) { i_allocateTBE; a_issueReadRequest; uu_profileMiss; k_popMandatoryQueue; } transition(I, Ifetch, IS) { i_allocateTBE; a_issueReadRequest; uu_profileMiss; k_popMandatoryQueue; } transition(I, Store, IM) { i_allocateTBE; b_issueWriteRequest; uu_profileMiss; k_popMandatoryQueue; } transition(I, L1_Replacement) { cc_sharedReplacement; gg_deallocateL1CacheBlock; } 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}, I_L) { e_sendAckWithCollectedTokens; l_popPersistentQueue; } transition(I_L, {Persistent_GETX, Persistent_GETS}) { 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, Ifetch}) { h_load_hit; k_popMandatoryQueue; } transition(S, Store, SM) { i_allocateTBE; b_issueWriteRequest; uu_profileMiss; k_popMandatoryQueue; } transition(S, L1_Replacement, I) { cc_sharedReplacement; // Only needed in some cases gg_deallocateL1CacheBlock; } transition(S, {Transient_GETX, Transient_Local_GETX}, I) { t_sendAckWithCollectedTokens; p_informL2AboutTokenLoss; 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; l_popPersistentQueue; } transition(S, Persistent_GETS, S_L) { f_sendAckWithAllButNorOneTokens; l_popPersistentQueue; } transition(S_L, Persistent_GETS) { 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}, {Load, Ifetch}) { h_load_hit; k_popMandatoryQueue; } transition(O, Store, OM) { i_allocateTBE; b_issueWriteRequest; uu_profileMiss; k_popMandatoryQueue; } transition(O, L1_Replacement, I) { c_ownedReplacement; gg_deallocateL1CacheBlock; } transition(O, {Transient_GETX, Transient_Local_GETX}, I) { dd_sendDataWithAllTokens; p_informL2AboutTokenLoss; m_popRequestQueue; } transition(O, Persistent_GETX, I_L) { ee_sendDataWithAllTokens; p_informL2AboutTokenLoss; l_popPersistentQueue; } transition(O, Persistent_GETS, S_L) { ff_sendDataWithAllButNorOneTokens; 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}, {Load, Ifetch}) { h_load_hit; k_popMandatoryQueue; } transition({MM, MM_W}, Store) { hh_store_hit; k_popMandatoryQueue; } transition(MM, L1_Replacement, I) { c_ownedReplacement; gg_deallocateL1CacheBlock; } transition(MM, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_Local_GETS}, I) { dd_sendDataWithAllTokens; p_informL2AboutTokenLoss; 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; 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; } // Transitions from Dirty Exclusive transition({M, M_W}, {Load, Ifetch}) { h_load_hit; k_popMandatoryQueue; } transition(M, Store, MM) { hh_store_hit; k_popMandatoryQueue; } transition(M_W, Store, MM_W) { hh_store_hit; k_popMandatoryQueue; } transition(M, L1_Replacement, I) { c_ownedReplacement; gg_deallocateL1CacheBlock; } transition(M, {Transient_GETX, Transient_Local_GETX}, I) { dd_sendDataWithAllTokens; p_informL2AboutTokenLoss; 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; 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, M) { s_deallocateTBE; jj_unsetUseTimer; } transition(M_W, Use_TimeoutStarverX, I_L) { s_deallocateTBE; ee_sendDataWithAllTokens; p_informL2AboutTokenLoss; jj_unsetUseTimer; } // migratory transition(MM_W, {Use_TimeoutStarverX, Use_TimeoutStarverS}, I_L) { s_deallocateTBE; ee_sendDataWithAllTokens; 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}, IS_L) { e_sendAckWithCollectedTokens; l_popPersistentQueue; } transition(IS_L, {Persistent_GETX, Persistent_GETS}) { l_popPersistentQueue; } transition(IM, {Persistent_GETX, Persistent_GETS}, 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; l_popPersistentQueue; } transition(SM, Persistent_GETS, SM_L) { f_sendAckWithAllButNorOneTokens; l_popPersistentQueue; } transition(SM_L, Persistent_GETS) { l_popPersistentQueue; } transition(OM, Persistent_GETX, IM_L) { ee_sendDataWithAllTokens; l_popPersistentQueue; } transition(OM, Persistent_GETS, SM_L) { ff_sendDataWithAllButNorOneTokens; 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; } 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; } 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; 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; } 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; } 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; } transition(IS, Data_Owner, O) { u_writeDataToCache; q_updateTokensFromResponse; x_external_load_hit; s_deallocateTBE; j_unsetReissueTimer; n_popResponseQueue; } transition(IS, Data_All_Tokens, M_W) { u_writeDataToCache; q_updateTokensFromResponse; x_external_load_hit; o_scheduleUseTimeout; j_unsetReissueTimer; n_popResponseQueue; } 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_profileMiss; k_popMandatoryQueue; } transition(I_L, Ifetch, IS_L) { pp_allocateL1ICacheBlock; i_allocateTBE; a_issueReadRequest; uu_profileMiss; k_popMandatoryQueue; } transition(I_L, Store, IM_L) { ii_allocateL1DCacheBlock; i_allocateTBE; b_issueWriteRequest; uu_profileMiss; k_popMandatoryQueue; } // Transitions from S_L transition(S_L, Store, SM_L) { i_allocateTBE; b_issueWriteRequest; uu_profileMiss; 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; } 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; } // Own_Lock_or_Unlock transition(I_L, Own_Lock_or_Unlock, I) { l_popPersistentQueue; } transition(S_L, Own_Lock_or_Unlock, S) { l_popPersistentQueue; } transition(IM_L, Own_Lock_or_Unlock, IM) { l_popPersistentQueue; } transition(IS_L, Own_Lock_or_Unlock, IS) { l_popPersistentQueue; } transition(SM_L, Own_Lock_or_Unlock, SM) { l_popPersistentQueue; } }