gem5/src/mem/protocol/MESI_CMP_directory-L2cache.sm
Tushar Krishna 3ed048e4f5 slicc: added vnet_type field to identify response vnets from others
Identifying response vnets versus other vnets will allow garnet to
determine which vnets will carry data packets, and which will carry
ctrl packets, and use appropriate buffer sizes (since data packets are larger
than ctrl packets). This in turn allows the orion power model to accurately
estimate buffer power.
2011-05-18 03:06:07 -04:00

1095 lines
37 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: MSI_MOSI_CMP_directory-L2cache.sm 1.12 05/01/19 15:55:40-06:00 beckmann@s0-28.cs.wisc.edu $
*
*/
machine(L2Cache, "MESI Directory L2 Cache CMP")
: CacheMemory * L2cacheMemory,
int l2_request_latency = 2,
int l2_response_latency = 2,
int to_l1_latency = 1
{
// L2 BANK QUEUES
// From local bank of L2 cache TO the network
MessageBuffer DirRequestFromL2Cache, network="To", virtual_network="0", ordered="false", vnet_type="request"; // this L2 bank -> Memory
MessageBuffer L1RequestFromL2Cache, network="To", virtual_network="0", ordered="false", vnet_type="request"; // this L2 bank -> a local L1
MessageBuffer responseFromL2Cache, network="To", virtual_network="1", ordered="false", vnet_type="response"; // this L2 bank -> a local L1 || Memory
// FROM the network to this local bank of L2 cache
MessageBuffer unblockToL2Cache, network="From", virtual_network="2", ordered="false", vnet_type="unblock"; // a local L1 || Memory -> this L2 bank
MessageBuffer L1RequestToL2Cache, network="From", virtual_network="0", ordered="false", vnet_type="request"; // a local L1 -> this L2 bank
MessageBuffer responseToL2Cache, network="From", virtual_network="1", ordered="false", vnet_type="response"; // a local L1 || Memory -> this L2 bank
// MessageBuffer unblockToL2Cache, network="From", virtual_network="4", ordered="false", vnet_type="unblock"; // a local L1 || Memory -> this L2 bank
// STATES
state_declaration(State, desc="L2 Cache states", default="L2Cache_State_NP") {
// Base states
NP, AccessPermission:Invalid, desc="Not present in either cache";
SS, AccessPermission:Read_Only, desc="L2 cache entry Shared, also present in one or more L1s";
M, AccessPermission:Read_Write, desc="L2 cache entry Modified, not present in any L1s", format="!b";
MT, AccessPermission:Invalid, desc="L2 cache entry Modified in a local L1, assume L2 copy stale", format="!b";
// L2 replacement
M_I, AccessPermission:Busy, desc="L2 cache replacing, have all acks, sent dirty data to memory, waiting for ACK from memory";
MT_I, AccessPermission:Busy, desc="L2 cache replacing, getting data from exclusive";
MCT_I, AccessPermission:Busy, desc="L2 cache replacing, clean in L2, getting data or ack from exclusive";
I_I, AccessPermission:Busy, desc="L2 replacing clean data, need to inv sharers and then drop data";
S_I, AccessPermission:Busy, desc="L2 replacing dirty data, collecting acks from L1s";
// Transient States for fetching data from memory
ISS, AccessPermission:Busy, desc="L2 idle, got single L1_GETS, issued memory fetch, have not seen response yet";
IS, AccessPermission:Busy, desc="L2 idle, got L1_GET_INSTR or multiple L1_GETS, issued memory fetch, have not seen response yet";
IM, AccessPermission:Busy, desc="L2 idle, got L1_GETX, issued memory fetch, have not seen response(s) yet";
// Blocking states
SS_MB, AccessPermission:Busy, desc="Blocked for L1_GETX from SS";
MT_MB, AccessPermission:Busy, desc="Blocked for L1_GETX from MT";
M_MB, AccessPermission:Busy, desc="Blocked for L1_GETX from M";
MT_IIB, AccessPermission:Busy, desc="Blocked for L1_GETS from MT, waiting for unblock and data";
MT_IB, AccessPermission:Busy, desc="Blocked for L1_GETS from MT, got unblock, waiting for data";
MT_SB, AccessPermission:Busy, desc="Blocked for L1_GETS from MT, got data, waiting for unblock";
}
// EVENTS
enumeration(Event, desc="L2 Cache events") {
// L2 events
// events initiated by the local L1s
L1_GET_INSTR, desc="a L1I GET INSTR request for a block maped to us";
L1_GETS, desc="a L1D GETS request for a block maped to us";
L1_GETX, desc="a L1D GETX request for a block maped to us";
L1_UPGRADE, desc="a L1D GETX request for a block maped to us";
L1_PUTX, desc="L1 replacing data";
L1_PUTX_old, desc="L1 replacing data, but no longer sharer";
Fwd_L1_GETX, desc="L1 did not have data, so we supply";
Fwd_L1_GETS, desc="L1 did not have data, so we supply";
Fwd_L1_GET_INSTR, desc="L1 did not have data, so we supply";
// events initiated by this L2
L2_Replacement, desc="L2 Replacement", format="!r";
L2_Replacement_clean, desc="L2 Replacement, but data is clean", format="!r";
// events from memory controller
Mem_Data, desc="data from memory", format="!r";
Mem_Ack, desc="ack from memory", format="!r";
// M->S data writeback
WB_Data, desc="data from L1";
WB_Data_clean, desc="clean data from L1";
Ack, desc="writeback ack";
Ack_all, desc="writeback ack";
Unblock, desc="Unblock from L1 requestor";
Unblock_Cancel, desc="Unblock from L1 requestor (FOR XACT MEMORY)";
Exclusive_Unblock, desc="Unblock from L1 requestor";
MEM_Inv, desc="Invalidation from directory";
}
// TYPES
// CacheEntry
structure(Entry, desc="...", interface="AbstractCacheEntry") {
State CacheState, desc="cache state";
NetDest Sharers, desc="tracks the L1 shares on-chip";
MachineID Exclusive, desc="Exclusive holder of block";
DataBlock DataBlk, desc="data for the block";
bool Dirty, default="false", desc="data is dirty";
}
// TBE fields
structure(TBE, desc="...") {
Address Address, desc="Physical address for this TBE";
State TBEState, desc="Transient state";
DataBlock DataBlk, desc="Buffer for the data block";
bool Dirty, default="false", desc="Data is Dirty";
NetDest L1_GetS_IDs, desc="Set of the internal processors that want the block in shared state";
MachineID L1_GetX_ID, desc="ID of the L1 cache to forward the block to once we get a response";
bool isPrefetch, desc="Set if this was caused by a prefetch";
int pendingAcks, desc="number of pending acks for invalidates during writeback";
}
structure(TBETable, external="yes") {
TBE lookup(Address);
void allocate(Address);
void deallocate(Address);
bool isPresent(Address);
}
TBETable L2_TBEs, template_hack="<L2Cache_TBE>";
void set_cache_entry(AbstractCacheEntry a);
void unset_cache_entry();
void set_tbe(TBE a);
void unset_tbe();
// inclusive cache, returns L2 entries only
Entry getCacheEntry(Address addr), return_by_pointer="yes" {
return static_cast(Entry, "pointer", L2cacheMemory[addr]);
}
std::string getCoherenceRequestTypeStr(CoherenceRequestType type) {
return CoherenceRequestType_to_string(type);
}
bool isOneSharerLeft(Address addr, MachineID requestor, Entry cache_entry) {
assert(is_valid(cache_entry));
assert(cache_entry.Sharers.isElement(requestor));
return (cache_entry.Sharers.count() == 1);
}
bool isSharer(Address addr, MachineID requestor, Entry cache_entry) {
if (is_valid(cache_entry)) {
return cache_entry.Sharers.isElement(requestor);
} else {
return false;
}
}
void addSharer(Address addr, MachineID requestor, Entry cache_entry) {
assert(is_valid(cache_entry));
DPRINTF(RubySlicc, "machineID: %s, requestor: %s, address: %s\n",
machineID, requestor, addr);
cache_entry.Sharers.add(requestor);
}
State getState(TBE tbe, Entry cache_entry, Address addr) {
if(is_valid(tbe)) {
return tbe.TBEState;
} else if (is_valid(cache_entry)) {
return cache_entry.CacheState;
}
return State:NP;
}
std::string getStateStr(TBE tbe, Entry cache_entry, Address addr) {
return L2Cache_State_to_string(getState(tbe, cache_entry, addr));
}
// when is this called
void setState(TBE tbe, Entry cache_entry, Address addr, State state) {
// MUST CHANGE
if (is_valid(tbe)) {
tbe.TBEState := state;
}
if (is_valid(cache_entry)) {
cache_entry.CacheState := state;
}
}
Event L1Cache_request_type_to_event(CoherenceRequestType type, Address addr,
MachineID requestor, Entry cache_entry) {
if(type == CoherenceRequestType:GETS) {
return Event:L1_GETS;
} else if(type == CoherenceRequestType:GET_INSTR) {
return Event:L1_GET_INSTR;
} else if (type == CoherenceRequestType:GETX) {
return Event:L1_GETX;
} else if (type == CoherenceRequestType:UPGRADE) {
if ( is_valid(cache_entry) && cache_entry.Sharers.isElement(requestor) ) {
return Event:L1_UPGRADE;
} else {
return Event:L1_GETX;
}
} else if (type == CoherenceRequestType:PUTX) {
if (isSharer(addr, requestor, cache_entry)) {
return Event:L1_PUTX;
} else {
return Event:L1_PUTX_old;
}
} else {
DPRINTF(RubySlicc, "address: %s, Request Type: %s\n", addr, type);
error("Invalid L1 forwarded request type");
}
}
int getPendingAcks(TBE tbe) {
return tbe.pendingAcks;
}
bool isDirty(Entry cache_entry) {
assert(is_valid(cache_entry));
return cache_entry.Dirty;
}
// ** OUT_PORTS **
out_port(L1RequestIntraChipL2Network_out, RequestMsg, L1RequestFromL2Cache);
out_port(DirRequestIntraChipL2Network_out, RequestMsg, DirRequestFromL2Cache);
out_port(responseIntraChipL2Network_out, ResponseMsg, responseFromL2Cache);
in_port(L1unblockNetwork_in, ResponseMsg, unblockToL2Cache) {
if(L1unblockNetwork_in.isReady()) {
peek(L1unblockNetwork_in, ResponseMsg) {
Entry cache_entry := getCacheEntry(in_msg.Address);
TBE tbe := L2_TBEs[in_msg.Address];
DPRINTF(RubySlicc, "Addr: %s State: %s Sender: %s Type: %s Dest: %s\n",
in_msg.Address, getState(tbe, cache_entry, in_msg.Address),
in_msg.Sender, in_msg.Type, in_msg.Destination);
assert(in_msg.Destination.isElement(machineID));
if (in_msg.Type == CoherenceResponseType:EXCLUSIVE_UNBLOCK) {
trigger(Event:Exclusive_Unblock, in_msg.Address, cache_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:UNBLOCK) {
trigger(Event:Unblock, in_msg.Address, cache_entry, tbe);
} else {
error("unknown unblock message");
}
}
}
}
// Response IntraChip L2 Network - response msg to this particular L2 bank
in_port(responseIntraChipL2Network_in, ResponseMsg, responseToL2Cache) {
if (responseIntraChipL2Network_in.isReady()) {
peek(responseIntraChipL2Network_in, ResponseMsg) {
// test wether it's from a local L1 or an off chip source
assert(in_msg.Destination.isElement(machineID));
Entry cache_entry := getCacheEntry(in_msg.Address);
TBE tbe := L2_TBEs[in_msg.Address];
if(machineIDToMachineType(in_msg.Sender) == MachineType:L1Cache) {
if(in_msg.Type == CoherenceResponseType:DATA) {
if (in_msg.Dirty) {
trigger(Event:WB_Data, in_msg.Address, cache_entry, tbe);
} else {
trigger(Event:WB_Data_clean, in_msg.Address, cache_entry, tbe);
}
} else if (in_msg.Type == CoherenceResponseType:ACK) {
if ((getPendingAcks(tbe) - in_msg.AckCount) == 0) {
trigger(Event:Ack_all, in_msg.Address, cache_entry, tbe);
} else {
trigger(Event:Ack, in_msg.Address, cache_entry, tbe);
}
} else {
error("unknown message type");
}
} else { // external message
if(in_msg.Type == CoherenceResponseType:MEMORY_DATA) {
// L2 now has data and all off-chip acks
trigger(Event:Mem_Data, in_msg.Address, cache_entry, tbe);
} else if(in_msg.Type == CoherenceResponseType:MEMORY_ACK) {
// L2 now has data and all off-chip acks
trigger(Event:Mem_Ack, in_msg.Address, cache_entry, tbe);
} else if(in_msg.Type == CoherenceResponseType:INV) {
// L2 now has data and all off-chip acks
trigger(Event:MEM_Inv, in_msg.Address, cache_entry, tbe);
} else {
error("unknown message type");
}
}
}
} // if not ready, do nothing
}
// L1 Request
in_port(L1RequestIntraChipL2Network_in, RequestMsg, L1RequestToL2Cache) {
if(L1RequestIntraChipL2Network_in.isReady()) {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
Entry cache_entry := getCacheEntry(in_msg.Address);
TBE tbe := L2_TBEs[in_msg.Address];
DPRINTF(RubySlicc, "Addr: %s State: %s Req: %s Type: %s Dest: %s\n",
in_msg.Address, getState(tbe, cache_entry, in_msg.Address),
in_msg.Requestor, in_msg.Type, in_msg.Destination);
assert(machineIDToMachineType(in_msg.Requestor) == MachineType:L1Cache);
assert(in_msg.Destination.isElement(machineID));
if (is_valid(cache_entry)) {
// The L2 contains the block, so proceeded with handling the request
trigger(L1Cache_request_type_to_event(in_msg.Type, in_msg.Address,
in_msg.Requestor, cache_entry),
in_msg.Address, cache_entry, tbe);
} else {
if (L2cacheMemory.cacheAvail(in_msg.Address)) {
// L2 does't have the line, but we have space for it in the L2
trigger(L1Cache_request_type_to_event(in_msg.Type, in_msg.Address,
in_msg.Requestor, cache_entry),
in_msg.Address, cache_entry, tbe);
} else {
// No room in the L2, so we need to make room before handling the request
Entry L2cache_entry := getCacheEntry(L2cacheMemory.cacheProbe(in_msg.Address));
if (isDirty(L2cache_entry)) {
trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address),
L2cache_entry, L2_TBEs[L2cacheMemory.cacheProbe(in_msg.Address)]);
} else {
trigger(Event:L2_Replacement_clean, L2cacheMemory.cacheProbe(in_msg.Address),
L2cache_entry, L2_TBEs[L2cacheMemory.cacheProbe(in_msg.Address)]);
}
}
}
}
}
}
// ACTIONS
action(a_issueFetchToMemory, "a", desc="fetch data from memory") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
enqueue(DirRequestIntraChipL2Network_out, RequestMsg, latency=l2_request_latency) {
out_msg.Address := address;
out_msg.Type := CoherenceRequestType:GETS;
out_msg.Requestor := machineID;
out_msg.Destination.add(map_Address_to_Directory(address));
out_msg.MessageSize := MessageSizeType:Control;
}
}
}
action(b_forwardRequestToExclusive, "b", desc="Forward request to the exclusive L1") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency=to_l1_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := in_msg.Type;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.add(cache_entry.Exclusive);
out_msg.MessageSize := MessageSizeType:Request_Control;
}
}
}
action(c_exclusiveReplacement, "c", desc="Send data to memory") {
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=l2_response_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:MEMORY_DATA;
out_msg.Sender := machineID;
out_msg.Destination.add(map_Address_to_Directory(address));
out_msg.DataBlk := cache_entry.DataBlk;
out_msg.Dirty := cache_entry.Dirty;
out_msg.MessageSize := MessageSizeType:Response_Data;
}
}
action(c_exclusiveCleanReplacement, "cc", desc="Send ack to memory for clean replacement") {
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=l2_response_latency) {
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:ACK;
out_msg.Sender := machineID;
out_msg.Destination.add(map_Address_to_Directory(address));
out_msg.MessageSize := MessageSizeType:Response_Control;
}
}
action(ct_exclusiveReplacementFromTBE, "ct", desc="Send data to memory") {
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=l2_response_latency) {
assert(is_valid(tbe));
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:MEMORY_DATA;
out_msg.Sender := machineID;
out_msg.Destination.add(map_Address_to_Directory(address));
out_msg.DataBlk := tbe.DataBlk;
out_msg.Dirty := tbe.Dirty;
out_msg.MessageSize := MessageSizeType:Response_Data;
}
}
action(d_sendDataToRequestor, "d", desc="Send data from cache to reqeustor") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=l2_response_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:DATA;
out_msg.Sender := machineID;
out_msg.Destination.add(in_msg.Requestor);
out_msg.DataBlk := cache_entry.DataBlk;
out_msg.Dirty := cache_entry.Dirty;
out_msg.MessageSize := MessageSizeType:Response_Data;
out_msg.AckCount := 0 - cache_entry.Sharers.count();
if (cache_entry.Sharers.isElement(in_msg.Requestor)) {
out_msg.AckCount := out_msg.AckCount + 1;
}
}
}
}
action(dd_sendExclusiveDataToRequestor, "dd", desc="Send data from cache to reqeustor") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=l2_response_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE;
out_msg.Sender := machineID;
out_msg.Destination.add(in_msg.Requestor);
out_msg.DataBlk := cache_entry.DataBlk;
out_msg.Dirty := cache_entry.Dirty;
out_msg.MessageSize := MessageSizeType:Response_Data;
out_msg.AckCount := 0 - cache_entry.Sharers.count();
if (cache_entry.Sharers.isElement(in_msg.Requestor)) {
out_msg.AckCount := out_msg.AckCount + 1;
}
}
}
}
action(ds_sendSharedDataToRequestor, "ds", desc="Send data from cache to reqeustor") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=l2_response_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:DATA;
out_msg.Sender := machineID;
out_msg.Destination.add(in_msg.Requestor);
out_msg.DataBlk := cache_entry.DataBlk;
out_msg.Dirty := cache_entry.Dirty;
out_msg.MessageSize := MessageSizeType:Response_Data;
out_msg.AckCount := 0;
}
}
}
action(e_sendDataToGetSRequestors, "e", desc="Send data from cache to all GetS IDs") {
assert(is_valid(tbe));
assert(tbe.L1_GetS_IDs.count() > 0);
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=to_l1_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:DATA;
out_msg.Sender := machineID;
out_msg.Destination := tbe.L1_GetS_IDs; // internal nodes
out_msg.DataBlk := cache_entry.DataBlk;
out_msg.Dirty := cache_entry.Dirty;
out_msg.MessageSize := MessageSizeType:Response_Data;
}
}
action(ex_sendExclusiveDataToGetSRequestors, "ex", desc="Send data from cache to all GetS IDs") {
assert(is_valid(tbe));
assert(tbe.L1_GetS_IDs.count() == 1);
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=to_l1_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE;
out_msg.Sender := machineID;
out_msg.Destination := tbe.L1_GetS_IDs; // internal nodes
out_msg.DataBlk := cache_entry.DataBlk;
out_msg.Dirty := cache_entry.Dirty;
out_msg.MessageSize := MessageSizeType:Response_Data;
}
}
action(ee_sendDataToGetXRequestor, "ee", desc="Send data from cache to GetX ID") {
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=to_l1_latency) {
assert(is_valid(tbe));
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:DATA;
out_msg.Sender := machineID;
out_msg.Destination.add(tbe.L1_GetX_ID);
DPRINTF(RubySlicc, "%s\n", out_msg.Destination);
out_msg.DataBlk := cache_entry.DataBlk;
out_msg.Dirty := cache_entry.Dirty;
DPRINTF(RubySlicc, "Address: %s, Destination: %s, DataBlock: %s\n",
out_msg.Address, out_msg.Destination, out_msg.DataBlk);
out_msg.MessageSize := MessageSizeType:Response_Data;
}
}
action(f_sendInvToSharers, "f", desc="invalidate sharers for L2 replacement") {
enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency=to_l1_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceRequestType:INV;
out_msg.Requestor := machineID;
out_msg.Destination := cache_entry.Sharers;
out_msg.MessageSize := MessageSizeType:Request_Control;
}
}
action(fw_sendFwdInvToSharers, "fw", desc="invalidate sharers for request") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency=to_l1_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceRequestType:INV;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination := cache_entry.Sharers;
out_msg.MessageSize := MessageSizeType:Request_Control;
}
}
}
action(fwm_sendFwdInvToSharersMinusRequestor, "fwm", desc="invalidate sharers for request, requestor is sharer") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency=to_l1_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceRequestType:INV;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination := cache_entry.Sharers;
out_msg.Destination.remove(in_msg.Requestor);
out_msg.MessageSize := MessageSizeType:Request_Control;
}
}
}
// OTHER ACTIONS
action(i_allocateTBE, "i", desc="Allocate TBE for internal/external request(isPrefetch=0, number of invalidates=0)") {
check_allocate(L2_TBEs);
assert(is_valid(cache_entry));
L2_TBEs.allocate(address);
set_tbe(L2_TBEs[address]);
tbe.L1_GetS_IDs.clear();
tbe.DataBlk := cache_entry.DataBlk;
tbe.Dirty := cache_entry.Dirty;
tbe.pendingAcks := cache_entry.Sharers.count();
}
action(s_deallocateTBE, "s", desc="Deallocate external TBE") {
L2_TBEs.deallocate(address);
unset_tbe();
}
action(jj_popL1RequestQueue, "\j", desc="Pop incoming L1 request queue") {
profileMsgDelay(0, L1RequestIntraChipL2Network_in.dequeue_getDelayCycles());
}
action(k_popUnblockQueue, "k", desc="Pop incoming unblock queue") {
profileMsgDelay(0, L1unblockNetwork_in.dequeue_getDelayCycles());
}
action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue") {
profileMsgDelay(3, responseIntraChipL2Network_in.dequeue_getDelayCycles());
}
action(m_writeDataToCache, "m", desc="Write data from response queue to cache") {
peek(responseIntraChipL2Network_in, ResponseMsg) {
assert(is_valid(cache_entry));
cache_entry.DataBlk := in_msg.DataBlk;
cache_entry.Dirty := in_msg.Dirty;
}
}
action(mr_writeDataToCacheFromRequest, "mr", desc="Write data from response queue to cache") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
assert(is_valid(cache_entry));
cache_entry.DataBlk := in_msg.DataBlk;
cache_entry.Dirty := in_msg.Dirty;
}
}
action(q_updateAck, "q", desc="update pending ack count") {
peek(responseIntraChipL2Network_in, ResponseMsg) {
assert(is_valid(tbe));
tbe.pendingAcks := tbe.pendingAcks - in_msg.AckCount;
APPEND_TRANSITION_COMMENT(in_msg.AckCount);
APPEND_TRANSITION_COMMENT(" p: ");
APPEND_TRANSITION_COMMENT(tbe.pendingAcks);
}
}
action(qq_writeDataToTBE, "\qq", desc="Write data from response queue to TBE") {
peek(responseIntraChipL2Network_in, ResponseMsg) {
assert(is_valid(tbe));
tbe.DataBlk := in_msg.DataBlk;
tbe.Dirty := in_msg.Dirty;
}
}
action(z_stall, "z", desc="Stall") {
}
action(ss_recordGetSL1ID, "\s", desc="Record L1 GetS for load response") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
assert(is_valid(tbe));
tbe.L1_GetS_IDs.add(in_msg.Requestor);
}
}
action(xx_recordGetXL1ID, "\x", desc="Record L1 GetX for store response") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
assert(is_valid(tbe));
tbe.L1_GetX_ID := in_msg.Requestor;
}
}
action(set_setMRU, "\set", desc="set the MRU entry") {
L2cacheMemory.setMRU(address);
}
action(qq_allocateL2CacheBlock, "\q", desc="Set L2 cache tag equal to tag of block B.") {
if (is_invalid(cache_entry)) {
set_cache_entry(L2cacheMemory.allocate(address, new Entry));
}
}
action(rr_deallocateL2CacheBlock, "\r", desc="Deallocate L2 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") {
L2cacheMemory.deallocate(address);
unset_cache_entry();
}
action(t_sendWBAck, "t", desc="Send writeback ACK") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=to_l1_latency) {
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:WB_ACK;
out_msg.Sender := machineID;
out_msg.Destination.add(in_msg.Requestor);
out_msg.MessageSize := MessageSizeType:Response_Control;
}
}
}
action(ts_sendInvAckToUpgrader, "ts", desc="Send ACK to upgrader") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
enqueue(responseIntraChipL2Network_out, ResponseMsg, latency=to_l1_latency) {
assert(is_valid(cache_entry));
out_msg.Address := address;
out_msg.Type := CoherenceResponseType:ACK;
out_msg.Sender := machineID;
out_msg.Destination.add(in_msg.Requestor);
out_msg.MessageSize := MessageSizeType:Response_Control;
// upgrader doesn't get ack from itself, hence the + 1
out_msg.AckCount := 0 - cache_entry.Sharers.count() + 1;
}
}
}
action(uu_profileMiss, "\u", desc="Profile the demand miss") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
//profile_L2Cache_miss(convertToGenericType(in_msg.Type), in_msg.AccessMode, MessageSizeTypeToInt(in_msg.MessageSize), in_msg.Prefetch, L1CacheMachIDToProcessorNum(in_msg.Requestor));
}
}
action(ww_profileMissNoDir, "\w", desc="Profile this transition at the L2 because Dir won't see the request") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
// profile_request(in_msg.L1CacheStateStr, getStateStr(address), "NA", getCoherenceRequestTypeStr(in_msg.Type));
}
}
action(nn_addSharer, "\n", desc="Add L1 sharer to list") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
assert(is_valid(cache_entry));
addSharer(address, in_msg.Requestor, cache_entry);
APPEND_TRANSITION_COMMENT( cache_entry.Sharers );
}
}
action(nnu_addSharerFromUnblock, "\nu", desc="Add L1 sharer to list") {
peek(L1unblockNetwork_in, ResponseMsg) {
assert(is_valid(cache_entry));
addSharer(address, in_msg.Sender, cache_entry);
}
}
action(kk_removeRequestSharer, "\k", desc="Remove L1 Request sharer from list") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
assert(is_valid(cache_entry));
cache_entry.Sharers.remove(in_msg.Requestor);
}
}
action(ll_clearSharers, "\l", desc="Remove all L1 sharers from list") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
assert(is_valid(cache_entry));
cache_entry.Sharers.clear();
}
}
action(mm_markExclusive, "\m", desc="set the exclusive owner") {
peek(L1RequestIntraChipL2Network_in, RequestMsg) {
assert(is_valid(cache_entry));
cache_entry.Sharers.clear();
cache_entry.Exclusive := in_msg.Requestor;
addSharer(address, in_msg.Requestor, cache_entry);
}
}
action(mmu_markExclusiveFromUnblock, "\mu", desc="set the exclusive owner") {
peek(L1unblockNetwork_in, ResponseMsg) {
assert(is_valid(cache_entry));
cache_entry.Sharers.clear();
cache_entry.Exclusive := in_msg.Sender;
addSharer(address, in_msg.Sender, cache_entry);
}
}
action(zz_recycleL1RequestQueue, "zz", desc="recycle L1 request queue") {
L1RequestIntraChipL2Network_in.recycle();
}
action(zn_recycleResponseNetwork, "zn", desc="recycle memory request") {
responseIntraChipL2Network_in.recycle();
}
//*****************************************************
// TRANSITIONS
//*****************************************************
//===============================================
// BASE STATE - I
// Transitions from I (Idle)
transition({NP, IS, ISS, IM, SS, M, M_I, I_I, S_I, M_MB, MT_IB, MT_SB}, L1_PUTX) {
t_sendWBAck;
jj_popL1RequestQueue;
}
transition({NP, SS, M, MT, M_I, I_I, S_I, IS, ISS, IM, M_MB, MT_IB, MT_SB}, L1_PUTX_old) {
t_sendWBAck;
jj_popL1RequestQueue;
}
transition({IM, IS, ISS, SS_MB, M_MB, MT_MB, MT_IIB, MT_IB, MT_SB}, {L2_Replacement, L2_Replacement_clean}) {
zz_recycleL1RequestQueue;
}
transition({IM, IS, ISS, SS_MB, M_MB, MT_MB, MT_IIB, MT_IB, MT_SB}, MEM_Inv) {
zn_recycleResponseNetwork;
}
transition({S_I, M_I, MT_I}, MEM_Inv) {
o_popIncomingResponseQueue;
}
transition({SS_MB, M_MB, MT_MB, MT_IIB, MT_IB, MT_SB}, {L1_GETS, L1_GET_INSTR, L1_GETX, L1_UPGRADE}) {
zz_recycleL1RequestQueue;
}
transition(NP, L1_GETS, ISS) {
qq_allocateL2CacheBlock;
ll_clearSharers;
nn_addSharer;
i_allocateTBE;
ss_recordGetSL1ID;
a_issueFetchToMemory;
uu_profileMiss;
jj_popL1RequestQueue;
}
transition(NP, L1_GET_INSTR, IS) {
qq_allocateL2CacheBlock;
ll_clearSharers;
nn_addSharer;
i_allocateTBE;
ss_recordGetSL1ID;
a_issueFetchToMemory;
uu_profileMiss;
jj_popL1RequestQueue;
}
transition(NP, L1_GETX, IM) {
qq_allocateL2CacheBlock;
ll_clearSharers;
// nn_addSharer;
i_allocateTBE;
xx_recordGetXL1ID;
a_issueFetchToMemory;
uu_profileMiss;
jj_popL1RequestQueue;
}
// transitions from IS/IM
transition(ISS, Mem_Data, MT_MB) {
m_writeDataToCache;
ex_sendExclusiveDataToGetSRequestors;
s_deallocateTBE;
o_popIncomingResponseQueue;
}
transition(IS, Mem_Data, SS) {
m_writeDataToCache;
e_sendDataToGetSRequestors;
s_deallocateTBE;
o_popIncomingResponseQueue;
}
transition(IM, Mem_Data, MT_MB) {
m_writeDataToCache;
ee_sendDataToGetXRequestor;
s_deallocateTBE;
o_popIncomingResponseQueue;
}
transition({IS, ISS}, {L1_GETS, L1_GET_INSTR}, IS) {
nn_addSharer;
ss_recordGetSL1ID;
uu_profileMiss;
jj_popL1RequestQueue;
}
transition({IS, ISS}, L1_GETX) {
zz_recycleL1RequestQueue;
}
transition(IM, {L1_GETX, L1_GETS, L1_GET_INSTR}) {
zz_recycleL1RequestQueue;
}
// transitions from SS
transition(SS, {L1_GETS, L1_GET_INSTR}) {
ds_sendSharedDataToRequestor;
nn_addSharer;
uu_profileMiss;
set_setMRU;
jj_popL1RequestQueue;
}
transition(SS, L1_GETX, SS_MB) {
d_sendDataToRequestor;
// fw_sendFwdInvToSharers;
fwm_sendFwdInvToSharersMinusRequestor;
uu_profileMiss;
set_setMRU;
jj_popL1RequestQueue;
}
transition(SS, L1_UPGRADE, SS_MB) {
fwm_sendFwdInvToSharersMinusRequestor;
ts_sendInvAckToUpgrader;
uu_profileMiss;
set_setMRU;
jj_popL1RequestQueue;
}
transition(SS, L2_Replacement_clean, I_I) {
i_allocateTBE;
f_sendInvToSharers;
rr_deallocateL2CacheBlock;
}
transition(SS, {L2_Replacement, MEM_Inv}, S_I) {
i_allocateTBE;
f_sendInvToSharers;
rr_deallocateL2CacheBlock;
}
transition(M, L1_GETX, MT_MB) {
d_sendDataToRequestor;
uu_profileMiss;
set_setMRU;
jj_popL1RequestQueue;
}
transition(M, L1_GET_INSTR, SS) {
d_sendDataToRequestor;
nn_addSharer;
uu_profileMiss;
set_setMRU;
jj_popL1RequestQueue;
}
transition(M, L1_GETS, MT_MB) {
dd_sendExclusiveDataToRequestor;
uu_profileMiss;
set_setMRU;
jj_popL1RequestQueue;
}
transition(M, {L2_Replacement, MEM_Inv}, M_I) {
i_allocateTBE;
c_exclusiveReplacement;
rr_deallocateL2CacheBlock;
}
transition(M, L2_Replacement_clean, M_I) {
i_allocateTBE;
c_exclusiveCleanReplacement;
rr_deallocateL2CacheBlock;
}
// transitions from MT
transition(MT, L1_GETX, MT_MB) {
b_forwardRequestToExclusive;
uu_profileMiss;
set_setMRU;
jj_popL1RequestQueue;
}
transition(MT, {L1_GETS, L1_GET_INSTR}, MT_IIB) {
b_forwardRequestToExclusive;
uu_profileMiss;
set_setMRU;
jj_popL1RequestQueue;
}
transition(MT, {L2_Replacement, MEM_Inv}, MT_I) {
i_allocateTBE;
f_sendInvToSharers;
rr_deallocateL2CacheBlock;
}
transition(MT, L2_Replacement_clean, MCT_I) {
i_allocateTBE;
f_sendInvToSharers;
rr_deallocateL2CacheBlock;
}
transition(MT, L1_PUTX, M) {
ll_clearSharers;
mr_writeDataToCacheFromRequest;
t_sendWBAck;
jj_popL1RequestQueue;
}
// transitions from blocking states
transition(SS_MB, Unblock_Cancel, SS) {
k_popUnblockQueue;
}
transition(MT_MB, Unblock_Cancel, MT) {
k_popUnblockQueue;
}
transition(MT_IB, Unblock_Cancel, MT) {
k_popUnblockQueue;
}
transition(SS_MB, Exclusive_Unblock, MT) {
// update actual directory
mmu_markExclusiveFromUnblock;
k_popUnblockQueue;
}
transition({M_MB, MT_MB}, Exclusive_Unblock, MT) {
// update actual directory
mmu_markExclusiveFromUnblock;
k_popUnblockQueue;
}
transition(MT_IIB, {L1_PUTX, L1_PUTX_old}){
zz_recycleL1RequestQueue;
}
transition(MT_IIB, Unblock, MT_IB) {
nnu_addSharerFromUnblock;
k_popUnblockQueue;
}
transition(MT_IIB, {WB_Data, WB_Data_clean}, MT_SB) {
m_writeDataToCache;
o_popIncomingResponseQueue;
}
transition(MT_IB, {WB_Data, WB_Data_clean}, SS) {
m_writeDataToCache;
o_popIncomingResponseQueue;
}
transition(MT_SB, Unblock, SS) {
nnu_addSharerFromUnblock;
k_popUnblockQueue;
}
// writeback states
transition({I_I, S_I, MT_I, MCT_I, M_I}, {L1_GETX, L1_UPGRADE, L1_GETS, L1_GET_INSTR}) {
zz_recycleL1RequestQueue;
}
transition(I_I, Ack) {
q_updateAck;
o_popIncomingResponseQueue;
}
transition(I_I, Ack_all, M_I) {
c_exclusiveCleanReplacement;
o_popIncomingResponseQueue;
}
transition({MT_I, MCT_I}, WB_Data, M_I) {
qq_writeDataToTBE;
ct_exclusiveReplacementFromTBE;
o_popIncomingResponseQueue;
}
transition(MCT_I, {WB_Data_clean, Ack_all}, M_I) {
c_exclusiveCleanReplacement;
o_popIncomingResponseQueue;
}
transition(MCT_I, {L1_PUTX, L1_PUTX_old}){
zz_recycleL1RequestQueue;
}
// L1 never changed Dirty data
transition(MT_I, Ack_all, M_I) {
ct_exclusiveReplacementFromTBE;
o_popIncomingResponseQueue;
}
transition(MT_I, {L1_PUTX, L1_PUTX_old}){
zz_recycleL1RequestQueue;
}
// possible race between unblock and immediate replacement
transition({MT_MB,SS_MB}, {L1_PUTX, L1_PUTX_old}) {
zz_recycleL1RequestQueue;
}
transition(MT_I, WB_Data_clean, NP) {
s_deallocateTBE;
o_popIncomingResponseQueue;
}
transition(S_I, Ack) {
q_updateAck;
o_popIncomingResponseQueue;
}
transition(S_I, Ack_all, M_I) {
ct_exclusiveReplacementFromTBE;
o_popIncomingResponseQueue;
}
transition(M_I, Mem_Ack, NP) {
s_deallocateTBE;
o_popIncomingResponseQueue;
}
}