Files
gem5/src/mem/ruby/protocol/MOESI_hammer-dir.sm
Matt Poremba 53b6e21c63 mem-ruby: Replace SLICC queueMemory calls with enqueue
Calls to queueMemoryRead and queueMemoryWrite do not consider the size
of the queue between ruby directories and DRAMCtrl which causes infinite
buffering in the queued port between the two. This adds a MessageBuffer
in between which uses enqueues in SLICC and is therefore size checked
before any SLICC transaction pushing to the buffer can occur, removing
the infinite buffering between the two.

Change-Id: Iedb9070844e4f6c8532a9c914d126105ec98d0bc
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/27427
Tested-by: Gem5 Cloud Project GCB service account <345032938727@cloudbuild.gserviceaccount.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Bradford Beckmann <brad.beckmann@amd.com>
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
Reviewed-by: Matt Sinclair <mattdsinclair@gmail.com>
Maintainer: Bradford Beckmann <brad.beckmann@amd.com>
2020-04-08 23:40:57 +00:00

1904 lines
63 KiB
Plaintext

/*
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
* Copyright (c) 2009 Advanced Micro Devices, Inc.
* 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.
*
* AMD's contributions to the MOESI hammer protocol do not constitute an
* endorsement of its similarity to any AMD products.
*/
machine(MachineType:Directory, "AMD Hammer-like protocol")
: DirectoryMemory * directory;
CacheMemory * probeFilter;
Cycles from_memory_controller_latency := 2;
Cycles to_memory_controller_latency := 1;
bool probe_filter_enabled := "False";
bool full_bit_dir_enabled := "False";
MessageBuffer * forwardFromDir, network="To", virtual_network="3",
vnet_type="forward";
MessageBuffer * responseFromDir, network="To", virtual_network="4",
vnet_type="response";
// For a finite buffered network, note that the DMA response network only
// works at this relatively lower numbered (lower priority) virtual network
// because the trigger queue decouples cache responses from DMA responses.
MessageBuffer * dmaResponseFromDir, network="To", virtual_network="1",
vnet_type="response";
MessageBuffer * unblockToDir, network="From", virtual_network="5",
vnet_type="unblock";
MessageBuffer * responseToDir, network="From", virtual_network="4",
vnet_type="response";
MessageBuffer * requestToDir, network="From", virtual_network="2",
vnet_type="request";
MessageBuffer * dmaRequestToDir, network="From", virtual_network="0",
vnet_type="request";
MessageBuffer * triggerQueue;
MessageBuffer * requestToMemory;
MessageBuffer * responseFromMemory;
{
// STATES
state_declaration(State, desc="Directory states", default="Directory_State_E") {
// Base states
NX, AccessPermission:Maybe_Stale, desc="Not Owner, probe filter entry exists, block in O at Owner";
NO, AccessPermission:Maybe_Stale, desc="Not Owner, probe filter entry exists, block in E/M at Owner";
S, AccessPermission:Read_Only, desc="Data clean, probe filter entry exists pointing to the current owner";
O, AccessPermission:Read_Only, desc="Data clean, probe filter entry exists";
E, AccessPermission:Read_Write, desc="Exclusive Owner, no probe filter entry";
O_R, AccessPermission:Read_Only, desc="Was data Owner, replacing probe filter entry";
S_R, AccessPermission:Read_Only, desc="Was Not Owner or Sharer, replacing probe filter entry";
NO_R, AccessPermission:Busy, desc="Was Not Owner or Sharer, replacing probe filter entry";
NO_B, AccessPermission:Busy, "NO^B", desc="Not Owner, Blocked";
NO_B_X, AccessPermission:Busy, "NO^B", desc="Not Owner, Blocked, next queued request GETX";
NO_B_S, AccessPermission:Busy, "NO^B", desc="Not Owner, Blocked, next queued request GETS";
NO_B_S_W, AccessPermission:Busy, "NO^B", desc="Not Owner, Blocked, forwarded merged GETS, waiting for responses";
O_B, AccessPermission:Busy, "O^B", desc="Owner, Blocked";
NO_B_W, AccessPermission:Busy, desc="Not Owner, Blocked, waiting for Dram";
O_B_W, AccessPermission:Busy, desc="Owner, Blocked, waiting for Dram";
NO_W, AccessPermission:Busy, desc="Not Owner, waiting for Dram";
O_W, AccessPermission:Busy, desc="Owner, waiting for Dram";
NO_DW_B_W, AccessPermission:Busy, desc="Not Owner, Dma Write waiting for Dram and cache responses";
NO_DR_B_W, AccessPermission:Busy, desc="Not Owner, Dma Read waiting for Dram and cache responses";
NO_DR_B_D, AccessPermission:Busy, desc="Not Owner, Dma Read waiting for cache responses including dirty data";
NO_DR_B, AccessPermission:Busy, desc="Not Owner, Dma Read waiting for cache responses";
NO_DW_W, AccessPermission:Busy, desc="Not Owner, Dma Write waiting for Dram";
O_DR_B_W, AccessPermission:Busy, desc="Owner, Dma Read waiting for Dram and cache responses";
O_DR_B, AccessPermission:Busy, desc="Owner, Dma Read waiting for cache responses";
WB, AccessPermission:Busy, desc="Blocked on a writeback";
WB_O_W, AccessPermission:Busy, desc="Blocked on memory write, will go to O";
WB_E_W, AccessPermission:Busy, desc="Blocked on memory write, will go to E";
NO_F, AccessPermission:Busy, desc="Blocked on a flush";
NO_F_W, AccessPermission:Busy, desc="Not Owner, Blocked, waiting for Dram";
}
// Events
enumeration(Event, desc="Directory events") {
GETX, desc="A GETX arrives";
GETS, desc="A GETS arrives";
PUT, desc="A PUT arrives";
Unblock, desc="An unblock message arrives";
UnblockS, desc="An unblock message arrives";
UnblockM, desc="An unblock message arrives";
Writeback_Clean, desc="The final part of a PutX (no data)";
Writeback_Dirty, desc="The final part of a PutX (data)";
Writeback_Exclusive_Clean, desc="The final part of a PutX (no data, exclusive)";
Writeback_Exclusive_Dirty, desc="The final part of a PutX (data, exclusive)";
// Probe filter
Pf_Replacement, desc="probe filter replacement";
// DMA requests
DMA_READ, desc="A DMA Read memory request";
DMA_WRITE, desc="A DMA Write memory request";
// Memory Controller
Memory_Data, desc="Fetched data from memory arrives";
Memory_Ack, desc="Writeback Ack from memory arrives";
// Cache responses required to handle DMA
Ack, desc="Received an ack message";
Shared_Ack, desc="Received an ack message, responder has a shared copy";
Shared_Data, desc="Received a data message, responder has a shared copy";
Data, desc="Received a data message, responder had a owner or exclusive copy, they gave it to us";
Exclusive_Data, desc="Received a data message, responder had an exclusive copy, they gave it to us";
// Triggers
All_acks_and_shared_data, desc="Received shared data and message acks";
All_acks_and_owner_data, desc="Received shared data and message acks";
All_acks_and_data_no_sharers, desc="Received all acks and no other processor has a shared copy";
All_Unblocks, desc="Received all unblocks for a merged gets request";
GETF, desc="A GETF arrives";
PUTF, desc="A PUTF arrives";
}
// TYPES
// DirectoryEntry
structure(Entry, desc="...", interface="AbstractCacheEntry", main="false") {
State DirectoryState, desc="Directory state";
}
// ProbeFilterEntry
structure(PfEntry, desc="...", interface="AbstractCacheEntry") {
State PfState, desc="Directory state";
MachineID Owner, desc="Owner node";
Set Sharers, desc="sharing vector for full bit directory";
}
// TBE entries for DMA requests
structure(TBE, desc="TBE entries for outstanding DMA requests") {
Addr PhysicalAddress, desc="physical address";
State TBEState, desc="Transient State";
CoherenceResponseType ResponseType, desc="The type for the subsequent response message";
int Acks, default="0", desc="The number of acks that the waiting response represents";
int SilentAcks, default="0", desc="The number of silent acks associated with this transaction";
DataBlock DmaDataBlk, desc="DMA Data to be written. Partial blocks need to merged with system memory";
DataBlock DataBlk, desc="The current view of system memory";
int Len, desc="...";
MachineID DmaRequestor, desc="DMA requestor";
NetDest GetSRequestors, desc="GETS merged requestors";
int NumPendingMsgs, desc="Number of pending acks/messages";
bool CacheDirty, default="false", desc="Indicates whether a cache has responded with dirty data";
bool Sharers, default="false", desc="Indicates whether a cache has indicated it is currently a sharer";
bool Owned, default="false", desc="Indicates whether a cache has indicated it is currently a sharer";
}
structure(TBETable, external="yes") {
TBE lookup(Addr);
void allocate(Addr);
void deallocate(Addr);
bool isPresent(Addr);
}
Tick clockEdge();
void set_cache_entry(AbstractCacheEntry b);
void unset_cache_entry();
void set_tbe(TBE a);
void unset_tbe();
void wakeUpBuffers(Addr a);
Cycles curCycle();
// ** OBJECTS **
Set fwd_set;
TBETable TBEs, template="<Directory_TBE>", constructor="m_number_of_TBEs";
Entry getDirectoryEntry(Addr addr), return_by_pointer="yes" {
Entry dir_entry := static_cast(Entry, "pointer", directory[addr]);
if (is_valid(dir_entry)) {
return dir_entry;
}
dir_entry := static_cast(Entry, "pointer",
directory.allocate(addr, new Entry));
return dir_entry;
}
PfEntry getProbeFilterEntry(Addr addr), return_by_pointer="yes" {
if (probe_filter_enabled || full_bit_dir_enabled) {
PfEntry pfEntry := static_cast(PfEntry, "pointer", probeFilter.lookup(addr));
return pfEntry;
}
return OOD;
}
State getState(TBE tbe, PfEntry pf_entry, Addr addr) {
if (is_valid(tbe)) {
return tbe.TBEState;
} else {
if (probe_filter_enabled || full_bit_dir_enabled) {
if (is_valid(pf_entry)) {
assert(pf_entry.PfState == getDirectoryEntry(addr).DirectoryState);
}
}
return getDirectoryEntry(addr).DirectoryState;
}
}
void setState(TBE tbe, PfEntry pf_entry, Addr addr, State state) {
if (is_valid(tbe)) {
tbe.TBEState := state;
}
if (probe_filter_enabled || full_bit_dir_enabled) {
if (is_valid(pf_entry)) {
pf_entry.PfState := state;
}
if (state == State:NX || state == State:NO || state == State:S || state == State:O) {
assert(is_valid(pf_entry));
}
if (state == State:E) {
assert(is_valid(pf_entry) == false);
}
}
if (state == State:E || state == State:NX || state == State:NO || state == State:S ||
state == State:O) {
assert(is_valid(tbe) == false);
}
getDirectoryEntry(addr).DirectoryState := state;
}
AccessPermission getAccessPermission(Addr addr) {
TBE tbe := TBEs[addr];
if(is_valid(tbe)) {
return Directory_State_to_permission(tbe.TBEState);
}
if(directory.isPresent(addr)) {
return Directory_State_to_permission(getDirectoryEntry(addr).DirectoryState);
}
return AccessPermission:NotPresent;
}
void setAccessPermission(PfEntry pf_entry, Addr addr, State state) {
getDirectoryEntry(addr).changePermission(Directory_State_to_permission(state));
}
void functionalRead(Addr addr, Packet *pkt) {
TBE tbe := TBEs[addr];
if(is_valid(tbe)) {
testAndRead(addr, tbe.DataBlk, pkt);
} else {
functionalMemoryRead(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);
}
num_functional_writes := num_functional_writes + functionalMemoryWrite(pkt);
return num_functional_writes;
}
Event cache_request_to_event(CoherenceRequestType type) {
if (type == CoherenceRequestType:GETS) {
return Event:GETS;
} else if (type == CoherenceRequestType:GETX) {
return Event:GETX;
} else if (type == CoherenceRequestType:GETF) {
return Event:GETF;
} else {
error("Invalid CoherenceRequestType");
}
}
// ** OUT_PORTS **
out_port(requestQueue_out, ResponseMsg, requestToDir); // For recycling requests
out_port(forwardNetwork_out, RequestMsg, forwardFromDir);
out_port(memQueue_out, MemoryMsg, requestToMemory);
out_port(responseNetwork_out, ResponseMsg, responseFromDir);
out_port(dmaResponseNetwork_out, DMAResponseMsg, dmaResponseFromDir);
out_port(triggerQueue_out, TriggerMsg, triggerQueue);
// ** IN_PORTS **
// Trigger Queue
in_port(triggerQueue_in, TriggerMsg, triggerQueue, rank=5) {
if (triggerQueue_in.isReady(clockEdge())) {
peek(triggerQueue_in, TriggerMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.addr);
TBE tbe := TBEs[in_msg.addr];
if (in_msg.Type == TriggerType:ALL_ACKS) {
trigger(Event:All_acks_and_owner_data, in_msg.addr,
pf_entry, tbe);
} else if (in_msg.Type == TriggerType:ALL_ACKS_OWNER_EXISTS) {
trigger(Event:All_acks_and_shared_data, in_msg.addr,
pf_entry, tbe);
} else if (in_msg.Type == TriggerType:ALL_ACKS_NO_SHARERS) {
trigger(Event:All_acks_and_data_no_sharers, in_msg.addr,
pf_entry, tbe);
} else if (in_msg.Type == TriggerType:ALL_UNBLOCKS) {
trigger(Event:All_Unblocks, in_msg.addr,
pf_entry, tbe);
} else {
error("Unexpected message");
}
}
}
}
in_port(unblockNetwork_in, ResponseMsg, unblockToDir, rank=4) {
if (unblockNetwork_in.isReady(clockEdge())) {
peek(unblockNetwork_in, ResponseMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.addr);
TBE tbe := TBEs[in_msg.addr];
if (in_msg.Type == CoherenceResponseType:UNBLOCK) {
trigger(Event:Unblock, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:UNBLOCKS) {
trigger(Event:UnblockS, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:UNBLOCKM) {
trigger(Event:UnblockM, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:WB_CLEAN) {
trigger(Event:Writeback_Clean, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:WB_DIRTY) {
trigger(Event:Writeback_Dirty, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:WB_EXCLUSIVE_CLEAN) {
trigger(Event:Writeback_Exclusive_Clean, in_msg.addr,
pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:WB_EXCLUSIVE_DIRTY) {
trigger(Event:Writeback_Exclusive_Dirty, in_msg.addr,
pf_entry, tbe);
} else {
error("Invalid message");
}
}
}
}
// Response Network
in_port(responseToDir_in, ResponseMsg, responseToDir, rank=3) {
if (responseToDir_in.isReady(clockEdge())) {
peek(responseToDir_in, ResponseMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.addr);
TBE tbe := TBEs[in_msg.addr];
if (in_msg.Type == CoherenceResponseType:ACK) {
trigger(Event:Ack, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:ACK_SHARED) {
trigger(Event:Shared_Ack, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:DATA_SHARED) {
trigger(Event:Shared_Data, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:DATA) {
trigger(Event:Data, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE) {
trigger(Event:Exclusive_Data, in_msg.addr, pf_entry, tbe);
} else {
error("Unexpected message");
}
}
}
}
// off-chip memory request/response is done
in_port(memQueue_in, MemoryMsg, responseFromMemory, rank=2) {
if (memQueue_in.isReady(clockEdge())) {
peek(memQueue_in, MemoryMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.addr);
TBE tbe := TBEs[in_msg.addr];
if (in_msg.Type == MemoryRequestType:MEMORY_READ) {
trigger(Event:Memory_Data, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == MemoryRequestType:MEMORY_WB) {
trigger(Event:Memory_Ack, in_msg.addr, pf_entry, tbe);
} else {
DPRINTF(RubySlicc, "%d\n", in_msg.Type);
error("Invalid message");
}
}
}
}
in_port(requestQueue_in, RequestMsg, requestToDir, rank=1) {
if (requestQueue_in.isReady(clockEdge())) {
peek(requestQueue_in, RequestMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.addr);
TBE tbe := TBEs[in_msg.addr];
if (in_msg.Type == CoherenceRequestType:PUT) {
trigger(Event:PUT, in_msg.addr, pf_entry, tbe);
} else if (in_msg.Type == CoherenceRequestType:PUTF) {
trigger(Event:PUTF, in_msg.addr, pf_entry, tbe);
} else {
if (probe_filter_enabled || full_bit_dir_enabled) {
if (is_valid(pf_entry)) {
trigger(cache_request_to_event(in_msg.Type), in_msg.addr,
pf_entry, tbe);
} else {
if (probeFilter.cacheAvail(in_msg.addr)) {
trigger(cache_request_to_event(in_msg.Type), in_msg.addr,
pf_entry, tbe);
} else {
Addr victim := probeFilter.cacheProbe(in_msg.addr);
trigger(Event:Pf_Replacement,
victim, getProbeFilterEntry(victim), TBEs[victim]);
}
}
} else {
trigger(cache_request_to_event(in_msg.Type), in_msg.addr,
pf_entry, tbe);
}
}
}
}
}
in_port(dmaRequestQueue_in, DMARequestMsg, dmaRequestToDir, rank=0) {
if (dmaRequestQueue_in.isReady(clockEdge())) {
peek(dmaRequestQueue_in, DMARequestMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.LineAddress);
TBE tbe := TBEs[in_msg.LineAddress];
if (in_msg.Type == DMARequestType:READ) {
trigger(Event:DMA_READ, in_msg.LineAddress, pf_entry, tbe);
} else if (in_msg.Type == DMARequestType:WRITE) {
trigger(Event:DMA_WRITE, in_msg.LineAddress, pf_entry, tbe);
} else {
error("Invalid message");
}
}
}
}
// Actions
action(r_setMRU, "\rr", desc="manually set the MRU bit for pf entry" ) {
if (probe_filter_enabled || full_bit_dir_enabled) {
assert(is_valid(cache_entry));
probeFilter.setMRU(address);
}
}
action(auno_assertUnblockerNotOwner, "auno", desc="assert unblocker not owner") {
if (probe_filter_enabled || full_bit_dir_enabled) {
assert(is_valid(cache_entry));
peek(unblockNetwork_in, ResponseMsg) {
assert(cache_entry.Owner != in_msg.Sender);
if (full_bit_dir_enabled) {
assert(cache_entry.Sharers.isElement(machineIDToNodeID(in_msg.Sender)) == false);
}
}
}
}
action(uo_updateOwnerIfPf, "uo", desc="update owner") {
if (probe_filter_enabled || full_bit_dir_enabled) {
assert(is_valid(cache_entry));
peek(unblockNetwork_in, ResponseMsg) {
cache_entry.Owner := in_msg.Sender;
if (full_bit_dir_enabled) {
cache_entry.Sharers.clear();
cache_entry.Sharers.add(machineIDToNodeID(in_msg.Sender));
APPEND_TRANSITION_COMMENT(cache_entry.Sharers);
DPRINTF(RubySlicc, "Sharers = %d\n", cache_entry.Sharers);
}
}
}
}
action(us_updateSharerIfFBD, "us", desc="update sharer if full-bit directory") {
if (full_bit_dir_enabled) {
assert(probeFilter.isTagPresent(address));
peek(unblockNetwork_in, ResponseMsg) {
cache_entry.Sharers.add(machineIDToNodeID(in_msg.Sender));
}
}
}
action(a_sendWriteBackAck, "a", desc="Send writeback ack to requestor") {
peek(requestQueue_in, RequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := CoherenceRequestType:WB_ACK;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.add(in_msg.Requestor);
out_msg.MessageSize := MessageSizeType:Writeback_Control;
}
}
}
action(oc_sendBlockAck, "oc", desc="Send block ack to the owner") {
peek(requestQueue_in, RequestMsg) {
if (((probe_filter_enabled || full_bit_dir_enabled) && (in_msg.Requestor == cache_entry.Owner)) || machineCount(MachineType:L1Cache) == 1) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := CoherenceRequestType:BLOCK_ACK;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.add(in_msg.Requestor);
out_msg.MessageSize := MessageSizeType:Writeback_Control;
}
}
}
}
action(b_sendWriteBackNack, "b", desc="Send writeback nack to requestor") {
peek(requestQueue_in, RequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := CoherenceRequestType:WB_NACK;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.add(in_msg.Requestor);
out_msg.MessageSize := MessageSizeType:Writeback_Control;
}
}
}
action(pfa_probeFilterAllocate, "pfa", desc="Allocate ProbeFilterEntry") {
if (probe_filter_enabled || full_bit_dir_enabled) {
peek(requestQueue_in, RequestMsg) {
set_cache_entry(probeFilter.allocate(address, new PfEntry));
cache_entry.Owner := in_msg.Requestor;
cache_entry.Sharers.setSize(machineCount(MachineType:L1Cache));
}
}
}
action(pfd_probeFilterDeallocate, "pfd", desc="Deallocate ProbeFilterEntry") {
if (probe_filter_enabled || full_bit_dir_enabled) {
probeFilter.deallocate(address);
unset_cache_entry();
}
}
action(ppfd_possibleProbeFilterDeallocate, "ppfd", desc="Deallocate ProbeFilterEntry") {
if ((probe_filter_enabled || full_bit_dir_enabled) && is_valid(cache_entry)) {
probeFilter.deallocate(address);
unset_cache_entry();
}
}
action(v_allocateTBE, "v", desc="Allocate TBE") {
check_allocate(TBEs);
peek(requestQueue_in, RequestMsg) {
TBEs.allocate(address);
set_tbe(TBEs[address]);
tbe.PhysicalAddress := address;
tbe.ResponseType := CoherenceResponseType:NULL;
}
}
action(vd_allocateDmaRequestInTBE, "vd", desc="Record Data in TBE") {
check_allocate(TBEs);
peek(dmaRequestQueue_in, DMARequestMsg) {
TBEs.allocate(address);
set_tbe(TBEs[address]);
tbe.DmaDataBlk := in_msg.DataBlk;
tbe.PhysicalAddress := in_msg.PhysicalAddress;
tbe.Len := in_msg.Len;
tbe.DmaRequestor := in_msg.Requestor;
tbe.ResponseType := CoherenceResponseType:DATA_EXCLUSIVE;
//
// One ack for each last-level cache
//
tbe.NumPendingMsgs := machineCount(MachineType:L1Cache);
//
// Assume initially that the caches store a clean copy and that memory
// will provide the data
//
tbe.CacheDirty := false;
}
}
action(pa_setPendingMsgsToAll, "pa", desc="set pending msgs to all") {
assert(is_valid(tbe));
if (full_bit_dir_enabled) {
assert(is_valid(cache_entry));
tbe.NumPendingMsgs := cache_entry.Sharers.count();
} else {
tbe.NumPendingMsgs := machineCount(MachineType:L1Cache);
}
}
action(po_setPendingMsgsToOne, "po", desc="set pending msgs to one") {
assert(is_valid(tbe));
tbe.NumPendingMsgs := 1;
}
action(w_deallocateTBE, "w", desc="Deallocate TBE") {
TBEs.deallocate(address);
unset_tbe();
}
action(sa_setAcksToOne, "sa", desc="Forwarded request, set the ack amount to one") {
assert(is_valid(tbe));
peek(requestQueue_in, RequestMsg) {
if (full_bit_dir_enabled) {
assert(is_valid(cache_entry));
//
// If we are using the full-bit directory and no sharers exists beyond
// the requestor, then we must set the ack number to all, not one
//
fwd_set := cache_entry.Sharers;
fwd_set.remove(machineIDToNodeID(in_msg.Requestor));
if (fwd_set.count() > 0) {
tbe.Acks := 1;
tbe.SilentAcks := machineCount(MachineType:L1Cache) - fwd_set.count();
tbe.SilentAcks := tbe.SilentAcks - 1;
} else {
tbe.Acks := machineCount(MachineType:L1Cache);
tbe.SilentAcks := 0;
}
} else {
tbe.Acks := 1;
}
}
}
action(saa_setAcksToAllIfPF, "saa", desc="Non-forwarded request, set the ack amount to all") {
assert(is_valid(tbe));
if (probe_filter_enabled || full_bit_dir_enabled) {
tbe.Acks := machineCount(MachineType:L1Cache);
tbe.SilentAcks := 0;
} else {
tbe.Acks := 1;
}
}
action(m_decrementNumberOfMessages, "m", desc="Decrement the number of messages for which we're waiting") {
peek(responseToDir_in, ResponseMsg) {
assert(is_valid(tbe));
assert(in_msg.Acks > 0);
DPRINTF(RubySlicc, "%d\n", tbe.NumPendingMsgs);
//
// Note that cache data responses will have an ack count of 2. However,
// directory DMA requests must wait for acks from all LLC caches, so
// only decrement by 1.
//
if ((in_msg.Type == CoherenceResponseType:DATA_SHARED) ||
(in_msg.Type == CoherenceResponseType:DATA) ||
(in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE)) {
tbe.NumPendingMsgs := tbe.NumPendingMsgs - 1;
} else {
tbe.NumPendingMsgs := tbe.NumPendingMsgs - in_msg.Acks;
}
DPRINTF(RubySlicc, "%d\n", tbe.NumPendingMsgs);
}
}
action(mu_decrementNumberOfUnblocks, "mu", desc="Decrement the number of messages for which we're waiting") {
peek(unblockNetwork_in, ResponseMsg) {
assert(is_valid(tbe));
assert(in_msg.Type == CoherenceResponseType:UNBLOCKS);
DPRINTF(RubySlicc, "%d\n", tbe.NumPendingMsgs);
tbe.NumPendingMsgs := tbe.NumPendingMsgs - 1;
DPRINTF(RubySlicc, "%d\n", tbe.NumPendingMsgs);
}
}
action(n_popResponseQueue, "n", desc="Pop response queue") {
responseToDir_in.dequeue(clockEdge());
}
action(o_checkForCompletion, "o", desc="Check if we have received all the messages required for completion") {
assert(is_valid(tbe));
if (tbe.NumPendingMsgs == 0) {
enqueue(triggerQueue_out, TriggerMsg) {
out_msg.addr := address;
if (tbe.Sharers) {
if (tbe.Owned) {
out_msg.Type := TriggerType:ALL_ACKS_OWNER_EXISTS;
} else {
out_msg.Type := TriggerType:ALL_ACKS;
}
} else {
out_msg.Type := TriggerType:ALL_ACKS_NO_SHARERS;
}
}
}
}
action(os_checkForMergedGetSCompletion, "os", desc="Check for merged GETS completion") {
assert(is_valid(tbe));
if (tbe.NumPendingMsgs == 0) {
enqueue(triggerQueue_out, TriggerMsg) {
out_msg.addr := address;
out_msg.Type := TriggerType:ALL_UNBLOCKS;
}
}
}
action(sp_setPendingMsgsToMergedSharers, "sp", desc="Set pending messages to waiting sharers") {
assert(is_valid(tbe));
tbe.NumPendingMsgs := tbe.GetSRequestors.count();
}
action(spa_setPendingAcksToZeroIfPF, "spa", desc="if probe filter, no need to wait for acks") {
if (probe_filter_enabled || full_bit_dir_enabled) {
assert(is_valid(tbe));
tbe.NumPendingMsgs := 0;
}
}
action(sc_signalCompletionIfPF, "sc", desc="indicate that we should skip waiting for cpu acks") {
assert(is_valid(tbe));
if (tbe.NumPendingMsgs == 0) {
assert(probe_filter_enabled || full_bit_dir_enabled);
enqueue(triggerQueue_out, TriggerMsg) {
out_msg.addr := address;
out_msg.Type := TriggerType:ALL_ACKS_NO_SHARERS;
}
}
}
action(d_sendData, "d", desc="Send data to requestor") {
peek(memQueue_in, MemoryMsg) {
enqueue(responseNetwork_out, ResponseMsg, 1) {
assert(is_valid(tbe));
out_msg.addr := address;
out_msg.Type := tbe.ResponseType;
out_msg.Sender := machineID;
out_msg.Destination.add(in_msg.OriginalRequestorMachId);
out_msg.DataBlk := in_msg.DataBlk;
DPRINTF(RubySlicc, "%s\n", out_msg.DataBlk);
out_msg.Dirty := false; // By definition, the block is now clean
out_msg.Acks := tbe.Acks;
out_msg.SilentAcks := tbe.SilentAcks;
DPRINTF(RubySlicc, "%d\n", out_msg.Acks);
assert(out_msg.Acks > 0);
out_msg.MessageSize := MessageSizeType:Response_Data;
}
}
}
action(dr_sendDmaData, "dr", desc="Send Data to DMA controller from memory") {
peek(memQueue_in, MemoryMsg) {
enqueue(dmaResponseNetwork_out, DMAResponseMsg, 1) {
assert(is_valid(tbe));
out_msg.PhysicalAddress := address;
out_msg.LineAddress := address;
out_msg.Type := DMAResponseType:DATA;
//
// we send the entire data block and rely on the dma controller to
// split it up if need be
//
out_msg.DataBlk := in_msg.DataBlk;
out_msg.Destination.add(tbe.DmaRequestor);
out_msg.MessageSize := MessageSizeType:Response_Data;
}
}
}
action(dt_sendDmaDataFromTbe, "dt", desc="Send Data to DMA controller from tbe") {
peek(triggerQueue_in, TriggerMsg) {
enqueue(dmaResponseNetwork_out, DMAResponseMsg, 1) {
assert(is_valid(tbe));
out_msg.PhysicalAddress := address;
out_msg.LineAddress := address;
out_msg.Type := DMAResponseType:DATA;
//
// we send the entire data block and rely on the dma controller to
// split it up if need be
//
out_msg.DataBlk := tbe.DataBlk;
out_msg.Destination.add(tbe.DmaRequestor);
out_msg.MessageSize := MessageSizeType:Response_Data;
}
}
}
action(da_sendDmaAck, "da", desc="Send Ack to DMA controller") {
enqueue(dmaResponseNetwork_out, DMAResponseMsg, 1) {
assert(is_valid(tbe));
out_msg.PhysicalAddress := address;
out_msg.LineAddress := address;
out_msg.Type := DMAResponseType:ACK;
out_msg.Destination.add(tbe.DmaRequestor);
out_msg.MessageSize := MessageSizeType:Writeback_Control;
}
}
action(rx_recordExclusiveInTBE, "rx", desc="Record Exclusive in TBE") {
peek(requestQueue_in, RequestMsg) {
assert(is_valid(tbe));
tbe.ResponseType := CoherenceResponseType:DATA_EXCLUSIVE;
}
}
action(r_recordDataInTBE, "rt", desc="Record Data in TBE") {
peek(requestQueue_in, RequestMsg) {
assert(is_valid(tbe));
if (full_bit_dir_enabled) {
fwd_set := cache_entry.Sharers;
fwd_set.remove(machineIDToNodeID(in_msg.Requestor));
if (fwd_set.count() > 0) {
tbe.ResponseType := CoherenceResponseType:DATA;
} else {
tbe.ResponseType := CoherenceResponseType:DATA_EXCLUSIVE;
}
} else {
tbe.ResponseType := CoherenceResponseType:DATA;
}
}
}
action(rs_recordGetSRequestor, "rs", desc="Record GETS requestor in TBE") {
peek(requestQueue_in, RequestMsg) {
assert(is_valid(tbe));
tbe.GetSRequestors.add(in_msg.Requestor);
}
}
action(r_setSharerBit, "r", desc="We saw other sharers") {
assert(is_valid(tbe));
tbe.Sharers := true;
}
action(so_setOwnerBit, "so", desc="We saw other sharers") {
assert(is_valid(tbe));
tbe.Sharers := true;
tbe.Owned := true;
}
action(qf_queueMemoryFetchRequest, "qf", desc="Queue off-chip fetch request") {
peek(requestQueue_in, RequestMsg) {
enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := MemoryRequestType:MEMORY_READ;
out_msg.Sender := in_msg.Requestor;
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.Len := 0;
}
}
}
action(qd_queueMemoryRequestFromDmaRead, "qd", desc="Queue off-chip fetch request") {
peek(dmaRequestQueue_in, DMARequestMsg) {
enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := MemoryRequestType:MEMORY_READ;
out_msg.Sender := in_msg.Requestor;
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.Len := 0;
}
}
}
action(fn_forwardRequestIfNecessary, "fn", desc="Forward requests if necessary") {
assert(is_valid(tbe));
if ((machineCount(MachineType:L1Cache) > 1) && (tbe.Acks <= 1)) {
if (full_bit_dir_enabled) {
assert(is_valid(cache_entry));
peek(requestQueue_in, RequestMsg) {
fwd_set := cache_entry.Sharers;
fwd_set.remove(machineIDToNodeID(in_msg.Requestor));
if (fwd_set.count() > 0) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := in_msg.Type;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.setNetDest(MachineType:L1Cache, fwd_set);
out_msg.MessageSize := MessageSizeType:Multicast_Control;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ForwardRequestTime := curCycle();
assert(tbe.SilentAcks > 0);
out_msg.SilentAcks := tbe.SilentAcks;
}
}
}
} else {
peek(requestQueue_in, RequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := in_msg.Type;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.broadcast(MachineType:L1Cache); // Send to all L1 caches
out_msg.Destination.remove(in_msg.Requestor); // Don't include the original requestor
out_msg.MessageSize := MessageSizeType:Broadcast_Control;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ForwardRequestTime := curCycle();
}
}
}
}
}
action(ia_invalidateAllRequest, "ia", desc="invalidate all copies") {
if (machineCount(MachineType:L1Cache) > 1) {
if (full_bit_dir_enabled) {
assert(cache_entry.Sharers.count() > 0);
peek(requestQueue_in, RequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := CoherenceRequestType:INV;
out_msg.Requestor := machineID;
out_msg.Destination.setNetDest(MachineType:L1Cache, cache_entry.Sharers);
out_msg.MessageSize := MessageSizeType:Multicast_Control;
}
}
} else {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := CoherenceRequestType:INV;
out_msg.Requestor := machineID;
out_msg.Destination.broadcast(MachineType:L1Cache); // Send to all L1 caches
out_msg.MessageSize := MessageSizeType:Broadcast_Control;
}
}
}
}
action(io_invalidateOwnerRequest, "io", desc="invalidate all copies") {
if (machineCount(MachineType:L1Cache) > 1) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
assert(is_valid(cache_entry));
out_msg.addr := address;
out_msg.Type := CoherenceRequestType:INV;
out_msg.Requestor := machineID;
out_msg.Destination.add(cache_entry.Owner);
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.DirectedProbe := true;
}
}
}
action(fb_forwardRequestBcast, "fb", desc="Forward requests to all nodes") {
if (machineCount(MachineType:L1Cache) > 1) {
peek(requestQueue_in, RequestMsg) {
if (full_bit_dir_enabled) {
fwd_set := cache_entry.Sharers;
fwd_set.remove(machineIDToNodeID(in_msg.Requestor));
if (fwd_set.count() > 0) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := in_msg.Type;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.setNetDest(MachineType:L1Cache, fwd_set);
out_msg.MessageSize := MessageSizeType:Multicast_Control;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ForwardRequestTime := curCycle();
out_msg.SilentAcks := machineCount(MachineType:L1Cache) - fwd_set.count();
out_msg.SilentAcks := out_msg.SilentAcks - 1;
}
}
} else {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := in_msg.Type;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.broadcast(MachineType:L1Cache); // Send to all L1 caches
out_msg.Destination.remove(in_msg.Requestor); // Don't include the original requestor
out_msg.MessageSize := MessageSizeType:Broadcast_Control;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ForwardRequestTime := curCycle();
}
}
}
} else {
peek(requestQueue_in, RequestMsg) {
enqueue(responseNetwork_out, ResponseMsg, 1) {
out_msg.addr := address;
out_msg.Type := CoherenceResponseType:ACK;
out_msg.Sender := machineID;
out_msg.Destination.add(in_msg.Requestor);
out_msg.Dirty := false; // By definition, the block is now clean
out_msg.Acks := 0;
out_msg.SilentAcks := 0;
DPRINTF(RubySlicc, "%d\n", out_msg.Acks);
out_msg.MessageSize := MessageSizeType:Response_Control;
}
}
}
}
action(fr_forwardMergeReadRequestsToOwner, "frr", desc="Forward coalesced read request to owner") {
assert(machineCount(MachineType:L1Cache) > 1);
//
// Fixme! The unblock network should not stall on the forward network. Add a trigger queue to
// decouple the two.
//
peek(unblockNetwork_in, ResponseMsg) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
assert(is_valid(tbe));
out_msg.addr := address;
out_msg.Type := CoherenceRequestType:MERGED_GETS;
out_msg.MergedRequestors := tbe.GetSRequestors;
if (in_msg.Type == CoherenceResponseType:UNBLOCKS) {
out_msg.Destination.add(in_msg.CurOwner);
} else {
out_msg.Destination.add(in_msg.Sender);
}
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.InitialRequestTime := zero_time();
out_msg.ForwardRequestTime := curCycle();
}
}
}
action(fc_forwardRequestConditionalOwner, "fc", desc="Forward request to one or more nodes") {
assert(machineCount(MachineType:L1Cache) > 1);
if (probe_filter_enabled || full_bit_dir_enabled) {
peek(requestQueue_in, RequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
assert(is_valid(cache_entry));
out_msg.addr := address;
out_msg.Type := in_msg.Type;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.add(cache_entry.Owner);
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.DirectedProbe := true;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ForwardRequestTime := curCycle();
}
}
} else {
peek(requestQueue_in, RequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := in_msg.Type;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.broadcast(MachineType:L1Cache); // Send to all L1 caches
out_msg.Destination.remove(in_msg.Requestor); // Don't include the original requestor
out_msg.MessageSize := MessageSizeType:Broadcast_Control;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ForwardRequestTime := curCycle();
}
}
}
}
action(nofc_forwardRequestConditionalOwner, "nofc", desc="Forward request to one or more nodes if the requestor is not the owner") {
if (machineCount(MachineType:L1Cache) > 1) {
if (probe_filter_enabled || full_bit_dir_enabled) {
peek(requestQueue_in, RequestMsg) {
if (in_msg.Requestor != cache_entry.Owner) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
assert(is_valid(cache_entry));
out_msg.addr := address;
out_msg.Type := in_msg.Type;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.add(cache_entry.Owner);
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.DirectedProbe := true;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ForwardRequestTime := curCycle();
}
}
}
} else {
peek(requestQueue_in, RequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := in_msg.Type;
out_msg.Requestor := in_msg.Requestor;
out_msg.Destination.broadcast(MachineType:L1Cache); // Send to all L1 caches
out_msg.Destination.remove(in_msg.Requestor); // Don't include the original requestor
out_msg.MessageSize := MessageSizeType:Broadcast_Control;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ForwardRequestTime := curCycle();
}
}
}
}
}
action(f_forwardWriteFromDma, "fw", desc="Forward requests") {
assert(is_valid(tbe));
if (tbe.NumPendingMsgs > 0) {
peek(dmaRequestQueue_in, DMARequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := CoherenceRequestType:GETX;
//
// Send to all L1 caches, since the requestor is the memory controller
// itself
//
out_msg.Requestor := machineID;
out_msg.Destination.broadcast(MachineType:L1Cache);
out_msg.MessageSize := MessageSizeType:Broadcast_Control;
}
}
}
}
action(f_forwardReadFromDma, "fr", desc="Forward requests") {
assert(is_valid(tbe));
if (tbe.NumPendingMsgs > 0) {
peek(dmaRequestQueue_in, DMARequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, from_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := CoherenceRequestType:GETS;
//
// Send to all L1 caches, since the requestor is the memory controller
// itself
//
out_msg.Requestor := machineID;
out_msg.Destination.broadcast(MachineType:L1Cache);
out_msg.MessageSize := MessageSizeType:Broadcast_Control;
}
}
}
}
action(i_popIncomingRequestQueue, "i", desc="Pop incoming request queue") {
requestQueue_in.dequeue(clockEdge());
}
action(j_popIncomingUnblockQueue, "j", desc="Pop incoming unblock queue") {
peek(unblockNetwork_in, ResponseMsg) {
APPEND_TRANSITION_COMMENT(in_msg.Sender);
}
unblockNetwork_in.dequeue(clockEdge());
}
action(k_wakeUpDependents, "k", desc="wake-up dependents") {
wakeUpBuffers(address);
}
action(l_popMemQueue, "q", desc="Pop off-chip request queue") {
memQueue_in.dequeue(clockEdge());
}
action(g_popTriggerQueue, "g", desc="Pop trigger queue") {
triggerQueue_in.dequeue(clockEdge());
}
action(p_popDmaRequestQueue, "pd", desc="pop dma request queue") {
dmaRequestQueue_in.dequeue(clockEdge());
}
action(zd_stallAndWaitDMARequest, "zd", desc="Stall and wait the dma request queue") {
peek(dmaRequestQueue_in, DMARequestMsg) {
APPEND_TRANSITION_COMMENT(in_msg.Requestor);
}
stall_and_wait(dmaRequestQueue_in, address);
}
action(r_recordMemoryData, "rd", desc="record data from memory to TBE") {
peek(memQueue_in, MemoryMsg) {
assert(is_valid(tbe));
if (tbe.CacheDirty == false) {
tbe.DataBlk := in_msg.DataBlk;
}
}
}
action(r_recordCacheData, "rc", desc="record data from cache response to TBE") {
peek(responseToDir_in, ResponseMsg) {
assert(is_valid(tbe));
tbe.CacheDirty := true;
tbe.DataBlk := in_msg.DataBlk;
}
}
action(a_assertCacheData, "ac", desc="Assert that a cache provided the data") {
assert(is_valid(tbe));
assert(tbe.CacheDirty);
}
action(ano_assertNotOwner, "ano", desc="Assert that request is not current owner") {
if (probe_filter_enabled || full_bit_dir_enabled) {
peek(requestQueue_in, RequestMsg) {
assert(is_valid(cache_entry));
assert(cache_entry.Owner != in_msg.Requestor);
}
}
}
action(ans_assertNotSharer, "ans", desc="Assert that request is not a current sharer") {
if (full_bit_dir_enabled) {
peek(requestQueue_in, RequestMsg) {
assert(cache_entry.Sharers.isElement(machineIDToNodeID(in_msg.Requestor)) == false);
}
}
}
action(rs_removeSharer, "s", desc="remove current sharer") {
if (full_bit_dir_enabled) {
peek(unblockNetwork_in, ResponseMsg) {
assert(cache_entry.Sharers.isElement(machineIDToNodeID(in_msg.Sender)));
cache_entry.Sharers.remove(machineIDToNodeID(in_msg.Sender));
}
}
}
action(cs_clearSharers, "cs", desc="clear current sharers") {
if (full_bit_dir_enabled) {
peek(requestQueue_in, RequestMsg) {
cache_entry.Sharers.clear();
cache_entry.Sharers.add(machineIDToNodeID(in_msg.Requestor));
}
}
}
action(l_queueMemoryWBRequest, "lq", desc="Write PUTX data to memory") {
peek(unblockNetwork_in, ResponseMsg) {
enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := MemoryRequestType:MEMORY_WB;
out_msg.Sender := in_msg.Sender;
out_msg.MessageSize := MessageSizeType:Writeback_Data;
out_msg.DataBlk := in_msg.DataBlk;
out_msg.Len := 0;
}
}
}
action(ld_queueMemoryDmaWrite, "ld", desc="Write DMA data to memory") {
assert(is_valid(tbe));
enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) {
out_msg.addr := tbe.PhysicalAddress;
out_msg.Type := MemoryRequestType:MEMORY_WB;
out_msg.Sender := tbe.DmaRequestor;
out_msg.MessageSize := MessageSizeType:Writeback_Data;
out_msg.DataBlk := tbe.DmaDataBlk;
out_msg.Len := tbe.Len;
}
}
action(ly_queueMemoryWriteFromTBE, "ly", desc="Write data to memory from TBE") {
enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := MemoryRequestType:MEMORY_WB;
out_msg.Sender := tbe.DmaRequestor;
out_msg.MessageSize := MessageSizeType:Writeback_Data;
out_msg.DataBlk := tbe.DataBlk;
out_msg.Len := 0;
}
}
action(ll_checkIncomingWriteback, "\l", desc="Check PUTX/PUTO response message") {
peek(unblockNetwork_in, ResponseMsg) {
assert(in_msg.Dirty == false);
assert(in_msg.MessageSize == MessageSizeType:Writeback_Control);
DPRINTF(RubySlicc, "%s\n", in_msg.DataBlk);
}
}
action(z_stallAndWaitRequest, "z", desc="Recycle the request queue") {
peek(requestQueue_in, RequestMsg) {
APPEND_TRANSITION_COMMENT(in_msg.Requestor);
}
stall_and_wait(requestQueue_in, address);
}
// TRANSITIONS
// Transitions out of E state
transition(E, GETX, NO_B_W) {
pfa_probeFilterAllocate;
v_allocateTBE;
rx_recordExclusiveInTBE;
saa_setAcksToAllIfPF;
qf_queueMemoryFetchRequest;
fn_forwardRequestIfNecessary;
i_popIncomingRequestQueue;
}
transition(E, GETF, NO_F_W) {
pfa_probeFilterAllocate;
v_allocateTBE;
rx_recordExclusiveInTBE;
saa_setAcksToAllIfPF;
qf_queueMemoryFetchRequest;
fn_forwardRequestIfNecessary;
i_popIncomingRequestQueue;
}
transition(E, GETS, NO_B_W) {
pfa_probeFilterAllocate;
v_allocateTBE;
rx_recordExclusiveInTBE;
saa_setAcksToAllIfPF;
qf_queueMemoryFetchRequest;
fn_forwardRequestIfNecessary;
i_popIncomingRequestQueue;
}
transition(E, DMA_READ, NO_DR_B_W) {
vd_allocateDmaRequestInTBE;
qd_queueMemoryRequestFromDmaRead;
spa_setPendingAcksToZeroIfPF;
f_forwardReadFromDma;
p_popDmaRequestQueue;
}
transition(E, DMA_WRITE, NO_DW_B_W) {
vd_allocateDmaRequestInTBE;
spa_setPendingAcksToZeroIfPF;
sc_signalCompletionIfPF;
f_forwardWriteFromDma;
p_popDmaRequestQueue;
}
// Transitions out of O state
transition(O, GETX, NO_B_W) {
r_setMRU;
v_allocateTBE;
r_recordDataInTBE;
sa_setAcksToOne;
qf_queueMemoryFetchRequest;
fb_forwardRequestBcast;
cs_clearSharers;
i_popIncomingRequestQueue;
}
transition(O, GETF, NO_F_W) {
r_setMRU;
v_allocateTBE;
r_recordDataInTBE;
sa_setAcksToOne;
qf_queueMemoryFetchRequest;
fb_forwardRequestBcast;
cs_clearSharers;
i_popIncomingRequestQueue;
}
// This transition is dumb, if a shared copy exists on-chip, then that should
// provide data, not slow off-chip dram. The problem is that the current
// caches don't provide data in S state
transition(O, GETS, O_B_W) {
r_setMRU;
v_allocateTBE;
r_recordDataInTBE;
saa_setAcksToAllIfPF;
qf_queueMemoryFetchRequest;
fn_forwardRequestIfNecessary;
i_popIncomingRequestQueue;
}
transition(O, DMA_READ, O_DR_B_W) {
vd_allocateDmaRequestInTBE;
spa_setPendingAcksToZeroIfPF;
qd_queueMemoryRequestFromDmaRead;
f_forwardReadFromDma;
p_popDmaRequestQueue;
}
transition(O, Pf_Replacement, O_R) {
v_allocateTBE;
pa_setPendingMsgsToAll;
ia_invalidateAllRequest;
pfd_probeFilterDeallocate;
}
transition(S, Pf_Replacement, S_R) {
v_allocateTBE;
pa_setPendingMsgsToAll;
ia_invalidateAllRequest;
pfd_probeFilterDeallocate;
}
transition(NO, Pf_Replacement, NO_R) {
v_allocateTBE;
po_setPendingMsgsToOne;
io_invalidateOwnerRequest;
pfd_probeFilterDeallocate;
}
transition(NX, Pf_Replacement, NO_R) {
v_allocateTBE;
pa_setPendingMsgsToAll;
ia_invalidateAllRequest;
pfd_probeFilterDeallocate;
}
transition({O, S, NO, NX}, DMA_WRITE, NO_DW_B_W) {
vd_allocateDmaRequestInTBE;
f_forwardWriteFromDma;
p_popDmaRequestQueue;
}
// Transitions out of NO state
transition(NX, GETX, NO_B) {
r_setMRU;
fb_forwardRequestBcast;
cs_clearSharers;
i_popIncomingRequestQueue;
}
transition(NX, GETF, NO_F) {
r_setMRU;
fb_forwardRequestBcast;
cs_clearSharers;
i_popIncomingRequestQueue;
}
// Transitions out of NO state
transition(NO, GETX, NO_B) {
r_setMRU;
ano_assertNotOwner;
fc_forwardRequestConditionalOwner;
cs_clearSharers;
i_popIncomingRequestQueue;
}
transition(NO, GETF, NO_F) {
r_setMRU;
//ano_assertNotOwner;
nofc_forwardRequestConditionalOwner; //forward request if the requester is not the owner
cs_clearSharers;
oc_sendBlockAck; // send ack if the owner
i_popIncomingRequestQueue;
}
transition(S, GETX, NO_B) {
r_setMRU;
fb_forwardRequestBcast;
cs_clearSharers;
i_popIncomingRequestQueue;
}
transition(S, GETF, NO_F) {
r_setMRU;
fb_forwardRequestBcast;
cs_clearSharers;
i_popIncomingRequestQueue;
}
transition(S, GETS, NO_B) {
r_setMRU;
ano_assertNotOwner;
fb_forwardRequestBcast;
i_popIncomingRequestQueue;
}
transition(NO, GETS, NO_B) {
r_setMRU;
ano_assertNotOwner;
ans_assertNotSharer;
fc_forwardRequestConditionalOwner;
i_popIncomingRequestQueue;
}
transition(NX, GETS, NO_B) {
r_setMRU;
ano_assertNotOwner;
fc_forwardRequestConditionalOwner;
i_popIncomingRequestQueue;
}
transition({NO, NX, S}, PUT, WB) {
//
// note that the PUT requestor may not be the current owner if an invalidate
// raced with PUT
//
a_sendWriteBackAck;
i_popIncomingRequestQueue;
}
transition({NO, NX, S}, DMA_READ, NO_DR_B_D) {
vd_allocateDmaRequestInTBE;
f_forwardReadFromDma;
p_popDmaRequestQueue;
}
// Nack PUT requests when races cause us to believe we own the data
transition({O, E}, PUT) {
b_sendWriteBackNack;
i_popIncomingRequestQueue;
}
// Blocked transient states
transition({NO_B_X, O_B, NO_DR_B_W, NO_DW_B_W, NO_B_W, NO_DR_B_D,
NO_DR_B, O_DR_B, O_B_W, O_DR_B_W, NO_DW_W, NO_B_S_W,
NO_W, O_W, WB, WB_E_W, WB_O_W, O_R, S_R, NO_R, NO_F_W},
{GETS, GETX, GETF, PUT, Pf_Replacement}) {
z_stallAndWaitRequest;
}
transition(NO_F, {GETS, GETX, GETF, PUT, Pf_Replacement}){
z_stallAndWaitRequest;
}
transition(NO_B, {GETX, GETF}, NO_B_X) {
z_stallAndWaitRequest;
}
transition(NO_B, {PUT, Pf_Replacement}) {
z_stallAndWaitRequest;
}
transition(NO_B_S, {GETX, GETF, PUT, Pf_Replacement}) {
z_stallAndWaitRequest;
}
transition({NO_B_X, NO_B, NO_B_S, O_B, NO_DR_B_W, NO_DW_B_W, NO_B_W, NO_DR_B_D,
NO_DR_B, O_DR_B, O_B_W, O_DR_B_W, NO_DW_W, NO_B_S_W,
NO_W, O_W, WB, WB_E_W, WB_O_W, O_R, S_R, NO_R, NO_F_W},
{DMA_READ, DMA_WRITE}) {
zd_stallAndWaitDMARequest;
}
// merge GETS into one response
transition(NO_B, GETS, NO_B_S) {
v_allocateTBE;
rs_recordGetSRequestor;
i_popIncomingRequestQueue;
}
transition(NO_B_S, GETS) {
rs_recordGetSRequestor;
i_popIncomingRequestQueue;
}
// unblock responses
transition({NO_B, NO_B_X}, UnblockS, NX) {
us_updateSharerIfFBD;
k_wakeUpDependents;
j_popIncomingUnblockQueue;
}
transition({NO_B, NO_B_X}, UnblockM, NO) {
uo_updateOwnerIfPf;
us_updateSharerIfFBD;
k_wakeUpDependents;
j_popIncomingUnblockQueue;
}
transition(NO_B_S, UnblockS, NO_B_S_W) {
us_updateSharerIfFBD;
fr_forwardMergeReadRequestsToOwner;
sp_setPendingMsgsToMergedSharers;
j_popIncomingUnblockQueue;
}
transition(NO_B_S, UnblockM, NO_B_S_W) {
uo_updateOwnerIfPf;
fr_forwardMergeReadRequestsToOwner;
sp_setPendingMsgsToMergedSharers;
j_popIncomingUnblockQueue;
}
transition(NO_B_S_W, UnblockS) {
us_updateSharerIfFBD;
mu_decrementNumberOfUnblocks;
os_checkForMergedGetSCompletion;
j_popIncomingUnblockQueue;
}
transition(NO_B_S_W, All_Unblocks, NX) {
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(O_B, UnblockS, O) {
us_updateSharerIfFBD;
k_wakeUpDependents;
j_popIncomingUnblockQueue;
}
transition(O_B, UnblockM, NO) {
us_updateSharerIfFBD;
uo_updateOwnerIfPf;
k_wakeUpDependents;
j_popIncomingUnblockQueue;
}
transition(NO_B_W, Memory_Data, NO_B) {
d_sendData;
w_deallocateTBE;
l_popMemQueue;
}
transition(NO_F_W, Memory_Data, NO_F) {
d_sendData;
w_deallocateTBE;
l_popMemQueue;
}
transition(NO_DR_B_W, Memory_Data, NO_DR_B) {
r_recordMemoryData;
o_checkForCompletion;
l_popMemQueue;
}
transition(O_DR_B_W, Memory_Data, O_DR_B) {
r_recordMemoryData;
dr_sendDmaData;
o_checkForCompletion;
l_popMemQueue;
}
transition({NO_DR_B, O_DR_B, NO_DR_B_D, NO_DW_B_W}, Ack) {
m_decrementNumberOfMessages;
o_checkForCompletion;
n_popResponseQueue;
}
transition({O_R, S_R, NO_R}, Ack) {
m_decrementNumberOfMessages;
o_checkForCompletion;
n_popResponseQueue;
}
transition(S_R, Data) {
m_decrementNumberOfMessages;
o_checkForCompletion;
n_popResponseQueue;
}
transition(NO_R, {Data, Exclusive_Data}) {
r_recordCacheData;
m_decrementNumberOfMessages;
o_checkForCompletion;
n_popResponseQueue;
}
transition({O_R, S_R}, All_acks_and_data_no_sharers, E) {
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_R, All_acks_and_data_no_sharers, WB_E_W) {
ly_queueMemoryWriteFromTBE;
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition({NO_DR_B_W, O_DR_B_W}, Ack) {
m_decrementNumberOfMessages;
n_popResponseQueue;
}
transition(NO_DR_B_W, Shared_Ack) {
m_decrementNumberOfMessages;
r_setSharerBit;
n_popResponseQueue;
}
transition(O_DR_B, Shared_Ack) {
m_decrementNumberOfMessages;
r_setSharerBit;
o_checkForCompletion;
n_popResponseQueue;
}
transition(O_DR_B_W, Shared_Ack) {
m_decrementNumberOfMessages;
r_setSharerBit;
n_popResponseQueue;
}
transition({NO_DR_B, NO_DR_B_D}, Shared_Ack) {
m_decrementNumberOfMessages;
r_setSharerBit;
o_checkForCompletion;
n_popResponseQueue;
}
transition(NO_DR_B_W, Shared_Data) {
r_recordCacheData;
m_decrementNumberOfMessages;
so_setOwnerBit;
o_checkForCompletion;
n_popResponseQueue;
}
transition({NO_DR_B, NO_DR_B_D}, Shared_Data) {
r_recordCacheData;
m_decrementNumberOfMessages;
so_setOwnerBit;
o_checkForCompletion;
n_popResponseQueue;
}
transition(NO_DR_B_W, {Exclusive_Data, Data}) {
r_recordCacheData;
m_decrementNumberOfMessages;
n_popResponseQueue;
}
transition({NO_DR_B, NO_DR_B_D, NO_DW_B_W}, {Exclusive_Data, Data}) {
r_recordCacheData;
m_decrementNumberOfMessages;
o_checkForCompletion;
n_popResponseQueue;
}
transition(NO_DR_B, All_acks_and_owner_data, WB_O_W) {
//
// Note that the DMA consistency model allows us to send the DMA device
// a response as soon as we receive valid data and prior to receiving
// all acks. However, to simplify the protocol we wait for all acks.
//
dt_sendDmaDataFromTbe;
ly_queueMemoryWriteFromTBE;
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_DR_B, All_acks_and_shared_data, S) {
//
// Note that the DMA consistency model allows us to send the DMA device
// a response as soon as we receive valid data and prior to receiving
// all acks. However, to simplify the protocol we wait for all acks.
//
dt_sendDmaDataFromTbe;
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_DR_B_D, All_acks_and_owner_data, WB_O_W) {
//
// Note that the DMA consistency model allows us to send the DMA device
// a response as soon as we receive valid data and prior to receiving
// all acks. However, to simplify the protocol we wait for all acks.
//
dt_sendDmaDataFromTbe;
ly_queueMemoryWriteFromTBE;
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_DR_B_D, All_acks_and_shared_data, S) {
//
// Note that the DMA consistency model allows us to send the DMA device
// a response as soon as we receive valid data and prior to receiving
// all acks. However, to simplify the protocol we wait for all acks.
//
dt_sendDmaDataFromTbe;
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(O_DR_B, All_acks_and_owner_data, WB_O_W) {
ly_queueMemoryWriteFromTBE;
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(O_DR_B, All_acks_and_data_no_sharers, WB_E_W) {
ly_queueMemoryWriteFromTBE;
w_deallocateTBE;
pfd_probeFilterDeallocate;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_DR_B, All_acks_and_data_no_sharers, WB_E_W) {
//
// Note that the DMA consistency model allows us to send the DMA device
// a response as soon as we receive valid data and prior to receiving
// all acks. However, to simplify the protocol we wait for all acks.
//
dt_sendDmaDataFromTbe;
ly_queueMemoryWriteFromTBE;
w_deallocateTBE;
ppfd_possibleProbeFilterDeallocate;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_DR_B_D, All_acks_and_data_no_sharers, WB_E_W) {
a_assertCacheData;
//
// Note that the DMA consistency model allows us to send the DMA device
// a response as soon as we receive valid data and prior to receiving
// all acks. However, to simplify the protocol we wait for all acks.
//
dt_sendDmaDataFromTbe;
ly_queueMemoryWriteFromTBE;
w_deallocateTBE;
ppfd_possibleProbeFilterDeallocate;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_DW_B_W, All_acks_and_data_no_sharers, NO_DW_W) {
ld_queueMemoryDmaWrite;
g_popTriggerQueue;
}
transition(NO_DW_W, Memory_Ack, E) {
da_sendDmaAck;
w_deallocateTBE;
ppfd_possibleProbeFilterDeallocate;
k_wakeUpDependents;
l_popMemQueue;
}
transition(O_B_W, Memory_Data, O_B) {
d_sendData;
w_deallocateTBE;
l_popMemQueue;
}
transition(NO_B_W, UnblockM, NO_W) {
uo_updateOwnerIfPf;
j_popIncomingUnblockQueue;
}
transition(NO_B_W, UnblockS, NO_W) {
us_updateSharerIfFBD;
j_popIncomingUnblockQueue;
}
transition(O_B_W, UnblockS, O_W) {
us_updateSharerIfFBD;
j_popIncomingUnblockQueue;
}
transition(NO_W, Memory_Data, NO) {
w_deallocateTBE;
k_wakeUpDependents;
l_popMemQueue;
}
transition(O_W, Memory_Data, O) {
w_deallocateTBE;
k_wakeUpDependents;
l_popMemQueue;
}
// WB State Transistions
transition(WB, Writeback_Dirty, WB_O_W) {
rs_removeSharer;
l_queueMemoryWBRequest;
j_popIncomingUnblockQueue;
}
transition(WB, Writeback_Exclusive_Dirty, WB_E_W) {
rs_removeSharer;
l_queueMemoryWBRequest;
pfd_probeFilterDeallocate;
j_popIncomingUnblockQueue;
}
transition(WB_E_W, Memory_Ack, E) {
k_wakeUpDependents;
l_popMemQueue;
}
transition(WB_O_W, Memory_Ack, O) {
k_wakeUpDependents;
l_popMemQueue;
}
transition(WB, Writeback_Clean, O) {
ll_checkIncomingWriteback;
rs_removeSharer;
k_wakeUpDependents;
j_popIncomingUnblockQueue;
}
transition(WB, Writeback_Exclusive_Clean, E) {
ll_checkIncomingWriteback;
rs_removeSharer;
pfd_probeFilterDeallocate;
k_wakeUpDependents;
j_popIncomingUnblockQueue;
}
transition(WB, Unblock, NX) {
auno_assertUnblockerNotOwner;
k_wakeUpDependents;
j_popIncomingUnblockQueue;
}
transition(NO_F, PUTF, WB) {
a_sendWriteBackAck;
i_popIncomingRequestQueue;
}
//possible race between GETF and UnblockM -- not sure needed any more?
transition(NO_F, UnblockM) {
us_updateSharerIfFBD;
uo_updateOwnerIfPf;
j_popIncomingUnblockQueue;
}
}