blob: 4950d63b2c44e4070833458df6f82f3bccd26c4a [file] [log] [blame]
/*
* Copyright (c) 2013 Mark D. Hill and David A. Wood
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
machine(MachineType:L0Cache, "MESI Directory L0 Cache")
: Sequencer * sequencer;
CacheMemory * Icache;
CacheMemory * Dcache;
Cycles request_latency := 2;
Cycles response_latency := 2;
bool send_evictions;
// From this node's L0 cache to the network
MessageBuffer * bufferToL1, network="To";
// To this node's L0 cache FROM the network
MessageBuffer * bufferFromL1, network="From";
// Message queue between this controller and the processor
MessageBuffer * mandatoryQueue;
{
// STATES
state_declaration(State, desc="Cache states", default="L0Cache_State_I") {
// Base states
// The cache entry has not been allocated.
I, AccessPermission:Invalid;
// The cache entry is in shared mode. The processor can read this entry
// but it cannot write to it.
S, AccessPermission:Read_Only;
// The cache entry is in exclusive mode. The processor can read this
// entry. It can write to this entry without informing the directory.
// On writing, the entry moves to M state.
E, AccessPermission:Read_Only;
// The processor has read and write permissions on this entry.
M, AccessPermission:Read_Write;
// Transient States
// The cache controller has requested an instruction. It will be stored
// in the shared state so that the processor can read it.
Inst_IS, AccessPermission:Busy;
// The cache controller has requested that this entry be fetched in
// shared state so that the processor can read it.
IS, AccessPermission:Busy;
// The cache controller has requested that this entry be fetched in
// modify state so that the processor can read/write it.
IM, AccessPermission:Busy;
// The cache controller had read permission over the entry. But now the
// processor needs to write to it. So, the controller has requested for
// write permission.
SM, AccessPermission:Read_Only;
}
// EVENTS
enumeration(Event, desc="Cache events") {
// L0 events
Load, desc="Load request from the home processor";
Ifetch, desc="I-fetch request from the home processor";
Store, desc="Store request from the home processor";
Inv, desc="Invalidate request from L2 bank";
// internal generated request
L0_Replacement, desc="L0 Replacement", format="!r";
// other requests
Fwd_GETX, desc="GETX from other processor";
Fwd_GETS, desc="GETS from other processor";
Fwd_GET_INSTR, desc="GET_INSTR from other processor";
Data, desc="Data for processor";
Data_Exclusive, desc="Data for processor";
Ack, desc="Ack for processor";
Ack_all, desc="Last ack for processor";
WB_Ack, desc="Ack for replacement";
}
// TYPES
// CacheEntry
structure(Entry, desc="...", interface="AbstractCacheEntry" ) {
State CacheState, desc="cache state";
DataBlock DataBlk, desc="data for the block";
bool Dirty, default="false", desc="data is dirty";
}
// TBE fields
structure(TBE, desc="...") {
Addr addr, 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";
int pendingAcks, default="0", desc="number of pending acks";
}
structure(TBETable, external="yes") {
TBE lookup(Addr);
void allocate(Addr);
void deallocate(Addr);
bool isPresent(Addr);
}
TBETable TBEs, template="<L0Cache_TBE>", constructor="m_number_of_TBEs";
Tick clockEdge();
Cycles ticksToCycles(Tick t);
void set_cache_entry(AbstractCacheEntry a);
void unset_cache_entry();
void set_tbe(TBE a);
void unset_tbe();
void wakeUpBuffers(Addr a);
void wakeUpAllBuffers(Addr a);
void profileMsgDelay(int virtualNetworkType, Cycles c);
// inclusive cache returns L0 entries only
Entry getCacheEntry(Addr addr), return_by_pointer="yes" {
Entry Dcache_entry := static_cast(Entry, "pointer", Dcache[addr]);
if(is_valid(Dcache_entry)) {
return Dcache_entry;
}
Entry Icache_entry := static_cast(Entry, "pointer", Icache[addr]);
return Icache_entry;
}
Entry getDCacheEntry(Addr addr), return_by_pointer="yes" {
Entry Dcache_entry := static_cast(Entry, "pointer", Dcache[addr]);
return Dcache_entry;
}
Entry getICacheEntry(Addr addr), return_by_pointer="yes" {
Entry Icache_entry := static_cast(Entry, "pointer", Icache[addr]);
return Icache_entry;
}
State getState(TBE tbe, Entry cache_entry, Addr addr) {
assert((Dcache.isTagPresent(addr) && Icache.isTagPresent(addr)) == false);
if(is_valid(tbe)) {
return tbe.TBEState;
} else if (is_valid(cache_entry)) {
return cache_entry.CacheState;
}
return State:I;
}
void setState(TBE tbe, Entry cache_entry, Addr addr, State state) {
assert((Dcache.isTagPresent(addr) && Icache.isTagPresent(addr)) == false);
// MUST CHANGE
if(is_valid(tbe)) {
tbe.TBEState := state;
}
if (is_valid(cache_entry)) {
cache_entry.CacheState := state;
}
}
AccessPermission getAccessPermission(Addr addr) {
TBE tbe := TBEs[addr];
if(is_valid(tbe)) {
DPRINTF(RubySlicc, "%s\n", L0Cache_State_to_permission(tbe.TBEState));
return L0Cache_State_to_permission(tbe.TBEState);
}
Entry cache_entry := getCacheEntry(addr);
if(is_valid(cache_entry)) {
DPRINTF(RubySlicc, "%s\n", L0Cache_State_to_permission(cache_entry.CacheState));
return L0Cache_State_to_permission(cache_entry.CacheState);
}
DPRINTF(RubySlicc, "%s\n", AccessPermission:NotPresent);
return AccessPermission:NotPresent;
}
void functionalRead(Addr addr, Packet *pkt) {
TBE tbe := TBEs[addr];
if(is_valid(tbe)) {
testAndRead(addr, tbe.DataBlk, pkt);
} else {
testAndRead(addr, getCacheEntry(addr).DataBlk, pkt);
}
}
int functionalWrite(Addr addr, Packet *pkt) {
int num_functional_writes := 0;
TBE tbe := TBEs[addr];
if(is_valid(tbe)) {
num_functional_writes := num_functional_writes +
testAndWrite(addr, tbe.DataBlk, pkt);
return num_functional_writes;
}
num_functional_writes := num_functional_writes +
testAndWrite(addr, getCacheEntry(addr).DataBlk, pkt);
return num_functional_writes;
}
void setAccessPermission(Entry cache_entry, Addr addr, State state) {
if (is_valid(cache_entry)) {
cache_entry.changePermission(L0Cache_State_to_permission(state));
}
}
Event mandatory_request_type_to_event(RubyRequestType type) {
if (type == RubyRequestType:LD) {
return Event:Load;
} else if (type == RubyRequestType:IFETCH) {
return Event:Ifetch;
} else if ((type == RubyRequestType:ST) || (type == RubyRequestType:ATOMIC)) {
return Event:Store;
} else {
error("Invalid RubyRequestType");
}
}
int getPendingAcks(TBE tbe) {
return tbe.pendingAcks;
}
out_port(requestNetwork_out, CoherenceMsg, bufferToL1);
// Messages for this L0 cache from the L1 cache
in_port(messgeBuffer_in, CoherenceMsg, bufferFromL1, rank = 1) {
if (messgeBuffer_in.isReady(clockEdge())) {
peek(messgeBuffer_in, CoherenceMsg, block_on="addr") {
assert(in_msg.Dest == machineID);
Entry cache_entry := getCacheEntry(in_msg.addr);
TBE tbe := TBEs[in_msg.addr];
if(in_msg.Class == CoherenceClass:DATA_EXCLUSIVE) {
trigger(Event:Data_Exclusive, in_msg.addr, cache_entry, tbe);
} else if(in_msg.Class == CoherenceClass:DATA) {
trigger(Event:Data, in_msg.addr, cache_entry, tbe);
} else if (in_msg.Class == CoherenceClass:ACK) {
trigger(Event:Ack, in_msg.addr, cache_entry, tbe);
} else if (in_msg.Class == CoherenceClass:WB_ACK) {
trigger(Event:WB_Ack, in_msg.addr, cache_entry, tbe);
} else if (in_msg.Class == CoherenceClass:INV) {
trigger(Event:Inv, in_msg.addr, cache_entry, tbe);
} else if (in_msg.Class == CoherenceClass:GETX ||
in_msg.Class == CoherenceClass:UPGRADE) {
// upgrade transforms to GETX due to race
trigger(Event:Fwd_GETX, in_msg.addr, cache_entry, tbe);
} else if (in_msg.Class == CoherenceClass:GETS) {
trigger(Event:Fwd_GETS, in_msg.addr, cache_entry, tbe);
} else if (in_msg.Class == CoherenceClass:GET_INSTR) {
trigger(Event:Fwd_GET_INSTR, in_msg.addr, cache_entry, tbe);
} else {
error("Invalid forwarded request type");
}
}
}
}
// Mandatory Queue betweens Node's CPU and it's L0 caches
in_port(mandatoryQueue_in, RubyRequest, mandatoryQueue, desc="...", rank = 0) {
if (mandatoryQueue_in.isReady(clockEdge())) {
peek(mandatoryQueue_in, RubyRequest, block_on="LineAddress") {
// Check for data access to blocks in I-cache and ifetchs to blocks in D-cache
if (in_msg.Type == RubyRequestType:IFETCH) {
// ** INSTRUCTION ACCESS ***
Entry Icache_entry := getICacheEntry(in_msg.LineAddress);
if (is_valid(Icache_entry)) {
// The tag matches for the L0, so the L0 asks the L2 for it.
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress,
Icache_entry, TBEs[in_msg.LineAddress]);
} else {
// Check to see if it is in the OTHER L0
Entry Dcache_entry := getDCacheEntry(in_msg.LineAddress);
if (is_valid(Dcache_entry)) {
// The block is in the wrong L0, put the request on the queue to the shared L2
trigger(Event:L0_Replacement, in_msg.LineAddress,
Dcache_entry, TBEs[in_msg.LineAddress]);
}
if (Icache.cacheAvail(in_msg.LineAddress)) {
// L0 does't have the line, but we have space for it
// in the L0 so let's see if the L2 has it
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress,
Icache_entry, TBEs[in_msg.LineAddress]);
} else {
// No room in the L0, so we need to make room in the L0
trigger(Event:L0_Replacement, Icache.cacheProbe(in_msg.LineAddress),
getICacheEntry(Icache.cacheProbe(in_msg.LineAddress)),
TBEs[Icache.cacheProbe(in_msg.LineAddress)]);
}
}
} else {
// *** DATA ACCESS ***
Entry Dcache_entry := getDCacheEntry(in_msg.LineAddress);
if (is_valid(Dcache_entry)) {
// The tag matches for the L0, so the L0 ask the L1 for it
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress,
Dcache_entry, TBEs[in_msg.LineAddress]);
} else {
// Check to see if it is in the OTHER L0
Entry Icache_entry := getICacheEntry(in_msg.LineAddress);
if (is_valid(Icache_entry)) {
// The block is in the wrong L0, put the request on the queue to the private L1
trigger(Event:L0_Replacement, in_msg.LineAddress,
Icache_entry, TBEs[in_msg.LineAddress]);
}
if (Dcache.cacheAvail(in_msg.LineAddress)) {
// L1 does't have the line, but we have space for it
// in the L0 let's see if the L1 has it
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress,
Dcache_entry, TBEs[in_msg.LineAddress]);
} else {
// No room in the L1, so we need to make room in the L0
trigger(Event:L0_Replacement, Dcache.cacheProbe(in_msg.LineAddress),
getDCacheEntry(Dcache.cacheProbe(in_msg.LineAddress)),
TBEs[Dcache.cacheProbe(in_msg.LineAddress)]);
}
}
}
}
}
}
// ACTIONS
action(a_issueGETS, "a", desc="Issue GETS") {
peek(mandatoryQueue_in, RubyRequest) {
enqueue(requestNetwork_out, CoherenceMsg, request_latency) {
out_msg.addr := address;
out_msg.Class := CoherenceClass:GETS;
out_msg.Sender := machineID;
out_msg.Dest := createMachineID(MachineType:L1Cache, version);
DPRINTF(RubySlicc, "address: %#x, destination: %s\n",
address, out_msg.Dest);
out_msg.MessageSize := MessageSizeType:Control;
out_msg.AccessMode := in_msg.AccessMode;
}
}
}
action(b_issueGETX, "b", desc="Issue GETX") {
peek(mandatoryQueue_in, RubyRequest) {
enqueue(requestNetwork_out, CoherenceMsg, request_latency) {
out_msg.addr := address;
out_msg.Class := CoherenceClass:GETX;
out_msg.Sender := machineID;
DPRINTF(RubySlicc, "%s\n", machineID);
out_msg.Dest := createMachineID(MachineType:L1Cache, version);
DPRINTF(RubySlicc, "address: %#x, destination: %s\n",
address, out_msg.Dest);
out_msg.MessageSize := MessageSizeType:Control;
out_msg.AccessMode := in_msg.AccessMode;
}
}
}
action(c_issueUPGRADE, "c", desc="Issue GETX") {
peek(mandatoryQueue_in, RubyRequest) {
enqueue(requestNetwork_out, CoherenceMsg, request_latency) {
out_msg.addr := address;
out_msg.Class := CoherenceClass:UPGRADE;
out_msg.Sender := machineID;
out_msg.Dest := createMachineID(MachineType:L1Cache, version);
DPRINTF(RubySlicc, "address: %#x, destination: %s\n",
address, out_msg.Dest);
out_msg.MessageSize := MessageSizeType:Control;
out_msg.AccessMode := in_msg.AccessMode;
}
}
}
action(f_sendDataToL1, "f", desc="send data to the L2 cache") {
enqueue(requestNetwork_out, CoherenceMsg, response_latency) {
assert(is_valid(cache_entry));
out_msg.addr := address;
out_msg.Class := CoherenceClass:INV_DATA;
out_msg.DataBlk := cache_entry.DataBlk;
out_msg.Dirty := cache_entry.Dirty;
out_msg.Sender := machineID;
out_msg.Dest := createMachineID(MachineType:L1Cache, version);
out_msg.MessageSize := MessageSizeType:Writeback_Data;
}
cache_entry.Dirty := false;
}
action(fi_sendInvAck, "fi", desc="send data to the L2 cache") {
peek(messgeBuffer_in, CoherenceMsg) {
enqueue(requestNetwork_out, CoherenceMsg, response_latency) {
out_msg.addr := address;
out_msg.Class := CoherenceClass:INV_ACK;
out_msg.Sender := machineID;
out_msg.Dest := createMachineID(MachineType:L1Cache, version);
out_msg.MessageSize := MessageSizeType:Response_Control;
}
}
}
action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") {
if (send_evictions) {
DPRINTF(RubySlicc, "Sending invalidation for %#x to the CPU\n", address);
sequencer.evictionCallback(address);
}
}
action(g_issuePUTX, "g", desc="send data to the L2 cache") {
enqueue(requestNetwork_out, CoherenceMsg, response_latency) {
assert(is_valid(cache_entry));
out_msg.addr := address;
out_msg.Class := CoherenceClass:PUTX;
out_msg.Dirty := cache_entry.Dirty;
out_msg.Sender:= machineID;
out_msg.Dest := createMachineID(MachineType:L1Cache, version);
if (cache_entry.Dirty) {
out_msg.MessageSize := MessageSizeType:Writeback_Data;
out_msg.DataBlk := cache_entry.DataBlk;
} else {
out_msg.MessageSize := MessageSizeType:Writeback_Control;
}
}
}
action(h_load_hit, "hd", desc="If not prefetch, notify sequencer the load completed.") {
assert(is_valid(cache_entry));
DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
Dcache.setMRU(cache_entry);
sequencer.readCallback(address, cache_entry.DataBlk);
}
action(h_ifetch_hit, "hi", desc="If not prefetch, notify sequencer the ifetch completed.") {
assert(is_valid(cache_entry));
DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
Icache.setMRU(cache_entry);
sequencer.readCallback(address, cache_entry.DataBlk);
}
action(hx_load_hit, "hxd", desc="notify sequencer the load completed.") {
assert(is_valid(cache_entry));
DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
Dcache.setMRU(cache_entry);
sequencer.readCallback(address, cache_entry.DataBlk, true);
}
action(hx_ifetch_hit, "hxi", desc="notify sequencer the ifetch completed.") {
assert(is_valid(cache_entry));
DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
Icache.setMRU(cache_entry);
sequencer.readCallback(address, cache_entry.DataBlk, true);
}
action(hh_store_hit, "\h", desc="If not prefetch, notify sequencer that store completed.") {
assert(is_valid(cache_entry));
DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
Dcache.setMRU(cache_entry);
sequencer.writeCallback(address, cache_entry.DataBlk);
cache_entry.Dirty := true;
}
action(hhx_store_hit, "\hx", desc="If not prefetch, notify sequencer that store completed.") {
assert(is_valid(cache_entry));
DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
Dcache.setMRU(cache_entry);
sequencer.writeCallback(address, cache_entry.DataBlk, true);
cache_entry.Dirty := true;
}
action(i_allocateTBE, "i", desc="Allocate TBE (number of invalidates=0)") {
check_allocate(TBEs);
assert(is_valid(cache_entry));
TBEs.allocate(address);
set_tbe(TBEs[address]);
tbe.Dirty := cache_entry.Dirty;
tbe.DataBlk := cache_entry.DataBlk;
}
action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") {
mandatoryQueue_in.dequeue(clockEdge());
}
action(l_popRequestQueue, "l",
desc="Pop incoming request queue and profile the delay within this virtual network") {
Tick delay := messgeBuffer_in.dequeue(clockEdge());
profileMsgDelay(2, ticksToCycles(delay));
}
action(o_popIncomingResponseQueue, "o",
desc="Pop Incoming Response queue and profile the delay within this virtual network") {
Tick delay := messgeBuffer_in.dequeue(clockEdge());
profileMsgDelay(1, ticksToCycles(delay));
}
action(s_deallocateTBE, "s", desc="Deallocate TBE") {
TBEs.deallocate(address);
unset_tbe();
}
action(u_writeDataToCache, "u", desc="Write data to cache") {
peek(messgeBuffer_in, CoherenceMsg) {
assert(is_valid(cache_entry));
cache_entry.DataBlk := in_msg.DataBlk;
}
}
action(u_writeInstToCache, "ui", desc="Write data to cache") {
peek(messgeBuffer_in, CoherenceMsg) {
assert(is_valid(cache_entry));
cache_entry.DataBlk := in_msg.DataBlk;
}
}
action(ff_deallocateCacheBlock, "\f",
desc="Deallocate L1 cache block.") {
if (Dcache.isTagPresent(address)) {
Dcache.deallocate(address);
} else {
Icache.deallocate(address);
}
unset_cache_entry();
}
action(oo_allocateDCacheBlock, "\o", desc="Set L1 D-cache tag equal to tag of block B.") {
if (is_invalid(cache_entry)) {
set_cache_entry(Dcache.allocate(address, new Entry));
}
}
action(pp_allocateICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") {
if (is_invalid(cache_entry)) {
set_cache_entry(Icache.allocate(address, new Entry));
}
}
action(z_stallAndWaitMandatoryQueue, "\z", desc="recycle cpu request queue") {
stall_and_wait(mandatoryQueue_in, address);
}
action(kd_wakeUpDependents, "kd", desc="wake-up dependents") {
wakeUpAllBuffers(address);
}
action(uu_profileInstMiss, "\ui", desc="Profile the demand miss") {
++Icache.demand_misses;
}
action(uu_profileInstHit, "\uih", desc="Profile the demand miss") {
++Icache.demand_hits;
}
action(uu_profileDataMiss, "\ud", desc="Profile the demand miss") {
++Dcache.demand_misses;
}
action(uu_profileDataHit, "\udh", desc="Profile the demand miss") {
++Dcache.demand_hits;
}
//*****************************************************
// TRANSITIONS
//*****************************************************
// Transitions for Load/Store/Replacement/WriteBack from transient states
transition({Inst_IS, IS, IM, SM}, {Load, Ifetch, Store, L0_Replacement}) {
z_stallAndWaitMandatoryQueue;
}
// Transitions from Idle
transition(I, Load, IS) {
oo_allocateDCacheBlock;
i_allocateTBE;
a_issueGETS;
uu_profileDataMiss;
k_popMandatoryQueue;
}
transition(I, Ifetch, Inst_IS) {
pp_allocateICacheBlock;
i_allocateTBE;
a_issueGETS;
uu_profileInstMiss;
k_popMandatoryQueue;
}
transition(I, Store, IM) {
oo_allocateDCacheBlock;
i_allocateTBE;
b_issueGETX;
uu_profileDataMiss;
k_popMandatoryQueue;
}
transition({I, IS, IM, Inst_IS}, Inv) {
fi_sendInvAck;
l_popRequestQueue;
}
transition(SM, Inv, IM) {
fi_sendInvAck;
l_popRequestQueue;
}
// Transitions from Shared
transition({S,E,M}, Load) {
h_load_hit;
uu_profileDataHit;
k_popMandatoryQueue;
}
transition({S,E,M}, Ifetch) {
h_ifetch_hit;
uu_profileInstHit;
k_popMandatoryQueue;
}
transition(S, Store, SM) {
i_allocateTBE;
c_issueUPGRADE;
uu_profileDataMiss;
k_popMandatoryQueue;
}
transition(S, L0_Replacement, I) {
forward_eviction_to_cpu;
ff_deallocateCacheBlock;
}
transition(S, Inv, I) {
forward_eviction_to_cpu;
fi_sendInvAck;
ff_deallocateCacheBlock;
l_popRequestQueue;
}
// Transitions from Exclusive
transition({E,M}, Store, M) {
hh_store_hit;
uu_profileDataHit;
k_popMandatoryQueue;
}
transition(E, L0_Replacement, I) {
forward_eviction_to_cpu;
g_issuePUTX;
ff_deallocateCacheBlock;
}
transition(E, {Inv, Fwd_GETX}, I) {
// don't send data
forward_eviction_to_cpu;
fi_sendInvAck;
ff_deallocateCacheBlock;
l_popRequestQueue;
}
transition(E, {Fwd_GETS, Fwd_GET_INSTR}, S) {
f_sendDataToL1;
l_popRequestQueue;
}
// Transitions from Modified
transition(M, L0_Replacement, I) {
forward_eviction_to_cpu;
g_issuePUTX;
ff_deallocateCacheBlock;
}
transition(M, {Inv, Fwd_GETX}, I) {
forward_eviction_to_cpu;
f_sendDataToL1;
ff_deallocateCacheBlock;
l_popRequestQueue;
}
transition(M, {Fwd_GETS, Fwd_GET_INSTR}, S) {
f_sendDataToL1;
l_popRequestQueue;
}
transition(IS, Data, S) {
u_writeDataToCache;
hx_load_hit;
s_deallocateTBE;
o_popIncomingResponseQueue;
kd_wakeUpDependents;
}
transition(IS, Data_Exclusive, E) {
u_writeDataToCache;
hx_load_hit;
s_deallocateTBE;
o_popIncomingResponseQueue;
kd_wakeUpDependents;
}
transition(Inst_IS, Data, S) {
u_writeInstToCache;
hx_ifetch_hit;
s_deallocateTBE;
o_popIncomingResponseQueue;
kd_wakeUpDependents;
}
transition(Inst_IS, Data_Exclusive, E) {
u_writeInstToCache;
hx_ifetch_hit;
s_deallocateTBE;
o_popIncomingResponseQueue;
kd_wakeUpDependents;
}
transition({IM,SM}, Data_Exclusive, M) {
u_writeDataToCache;
hhx_store_hit;
s_deallocateTBE;
o_popIncomingResponseQueue;
kd_wakeUpDependents;
}
}