gem5/src/mem/protocol/MOESI_CMP_token-L1cache.sm
Brad Beckmann 45f6f31d7a ruby: fixed token bugs associated with owner token counts
This patch fixes several bugs related to previous inconsistent assumptions on
how many tokens the Owner had.  Mike Marty should have fixes these bugs years
ago.  :)
2010-08-20 11:46:13 -07:00

2196 lines
72 KiB
Text

/*
* 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")
: Sequencer * sequencer,
CacheMemory * L1IcacheMemory,
CacheMemory * L1DcacheMemory,
int l2_select_num_bits,
int N_tokens,
int l1_request_latency = 2,
int l1_response_latency = 2,
int retry_threshold = 1,
int fixed_timeout_latency = 100,
bool dynamic_timeout_enabled = true
{
// From this node's L1 cache TO the network
// a local L1 -> this L2 bank
MessageBuffer responseFromL1Cache, network="To", virtual_network="4", ordered="false";
MessageBuffer persistentFromL1Cache, network="To", virtual_network="3", ordered="true";
// a local L1 -> this L2 bank, currently ordered with directory forwarded requests
MessageBuffer requestFromL1Cache, network="To", virtual_network="1", ordered="false";
// To this node's L1 cache FROM the network
// a L2 bank -> this L1
MessageBuffer responseToL1Cache, network="From", virtual_network="4", ordered="false";
MessageBuffer persistentToL1Cache, network="From", virtual_network="3", ordered="true";
// a L2 bank -> this L1
MessageBuffer requestToL1Cache, network="From", virtual_network="1", 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";
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";
}
// 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(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="<L1Cache_TBE>";
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";
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)) {
assert(L1IcacheMemory.isTagPresent(addr) == false);
return static_cast(Entry, L1DcacheMemory[addr]);
} else {
return static_cast(Entry, L1IcacheMemory[addr]);
}
}
int getTokens(Address addr) {
if (L1DcacheMemory.isTagPresent(addr)) {
assert(L1IcacheMemory.isTagPresent(addr) == false);
return static_cast(Entry, L1DcacheMemory[addr]).Tokens;
} else if (L1IcacheMemory.isTagPresent(addr)) {
return static_cast(Entry, 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());
assert(getCacheEntry(addr).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
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);
assert(getCacheEntry(addr).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(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 > (max_tokens() / 2));
}
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) {
//
// NOTE direct local hits should not call this
//
return GenericMachineType:L1Cache_wCC;
} 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, block_on="Address") {
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) {
if (getTokens(in_msg.Address) == 1 ||
getTokens(in_msg.Address) == (max_tokens() / 2) + 1) {
trigger(Event:Persistent_GETS_Last_Token, in_msg.Address);
} else {
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, block_on="Address") {
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 (getTokens(in_msg.Address) == 1 ||
getTokens(in_msg.Address) == (max_tokens() / 2) + 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, block_on="Address") {
assert(in_msg.Destination.isElement(machineID));
// 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.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 ( machineIDToMachineType(in_msg.Sender) == MachineType:Directory ) {
if (L1_TBEs.isPresent(in_msg.Address)) {
L1_TBEs[in_msg.Address].ExternalResponse := true;
// profile_memory_response( in_msg.Address);
}
} else if ( machineIDToMachineType(in_msg.Sender) == 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) {
assert(in_msg.Tokens < (max_tokens() / 2));
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) {
assert(in_msg.Tokens < (max_tokens() / 2));
trigger(Event:Data_Shared, in_msg.Address);
} 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.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, block_on="LineAddress") {
// 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:Broadcast_Control;
} else {
out_msg.MessageSize := MessageSizeType:Broadcast_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.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.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:Broadcast_Control;
} else {
out_msg.MessageSize := MessageSizeType:Broadcast_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.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.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 shared writeback") {
// don't send writeback if replacing block with no tokens
assert (getCacheEntry(address).Tokens > 0);
enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) {
out_msg.Address := address;
out_msg.Sender := machineID;
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;
out_msg.MessageSize := MessageSizeType:Writeback_Data;
out_msg.Type := CoherenceResponseType:WB_SHARED_DATA;
}
getCacheEntry(address).Tokens := 0;
}
action(tr_tokenReplacement, "tr", desc="Issue token writeback") {
if (getCacheEntry(address).Tokens > 0) {
enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) {
out_msg.Address := address;
out_msg.Sender := machineID;
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?
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.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 + (max_tokens() / 2))) {
enqueue(responseNetwork_out, ResponseMsg, latency = l1_response_latency) {
out_msg.Address := 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 := 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.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.Destination.add(in_msg.Requestor);
assert(getCacheEntry(address).Tokens > (max_tokens() / 2));
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;
if (getCacheEntry(address).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(getCacheEntry(address).Tokens >= 1);
out_msg.Tokens := getCacheEntry(address).Tokens;
out_msg.DataBlk := getCacheEntry(address).DataBlk;
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.Destination.add(persistentTable.findSmallest(address));
assert(getCacheEntry(address).Tokens > (max_tokens() / 2));
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;
if (getCacheEntry(address).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(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.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 > ((max_tokens() / 2) + 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.Destination.add(persistentTable.findSmallest(address));
if (getCacheEntry(address).Tokens > (N_tokens + (max_tokens() / 2))) {
out_msg.Tokens := getCacheEntry(address).Tokens - N_tokens;
} else {
out_msg.Tokens := getCacheEntry(address).Tokens - 1;
}
assert(out_msg.Tokens > (max_tokens() / 2));
out_msg.DataBlk := getCacheEntry(address).DataBlk;
out_msg.Dirty := getCacheEntry(address).Dirty;
out_msg.MessageSize := MessageSizeType:Response_Data;
}
if (getCacheEntry(address).Tokens > (N_tokens + (max_tokens() / 2))) {
getCacheEntry(address).Tokens := N_tokens;
} else {
getCacheEntry(address).Tokens := 1;
}
}
action(fo_sendDataWithOwnerToken, "fo", desc="Send data and owner tokens") {
assert(getCacheEntry(address).Tokens == ((max_tokens() / 2) + 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.Destination.add(persistentTable.findSmallest(address));
out_msg.Tokens := getCacheEntry(address).Tokens;
assert(out_msg.Tokens > (max_tokens() / 2));
out_msg.DataBlk := getCacheEntry(address).DataBlk;
out_msg.Dirty := getCacheEntry(address).Dirty;
out_msg.MessageSize := MessageSizeType:Response_Data;
}
getCacheEntry(address).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, latency="1") {
out_msg.Address := 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.") {
DEBUG_EXPR(address);
DEBUG_EXPR(getCacheEntry(address).DataBlk);
sequencer.readCallback(address,
GenericMachineType:L1Cache,
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,
getNondirectHitMachType(address, in_msg.Sender),
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,
GenericMachineType:L1Cache,
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,
getNondirectHitMachType(address, in_msg.Sender),
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.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.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;
if (getCacheEntry(address).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(getCacheEntry(address).Tokens >= 1);
out_msg.Tokens := getCacheEntry(address).Tokens;
out_msg.DataBlk := getCacheEntry(address).DataBlk;
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.") {
assert(getTokens(address) == 0);
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) {
if (L1DcacheMemory.isTagPresent(address)) {
L1DcacheMemory.profileMiss(in_msg);
} else {
L1IcacheMemory.profileMiss(in_msg);
}
}
}
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, Persistent_GETS_Last_Token}, 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) {
tr_tokenReplacement;
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, 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, 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, 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}, {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, Persistent_GETS_Last_Token, I_L) {
fo_sendDataWithOwnerToken;
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, 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;
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;
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;
}
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;
}
}