gem5/src/mem/protocol/MOESI_hammer-dir.sm
Brad Beckmann ext:(%2C%20Nilay%20Vaish%20%3Cnilay%40cs.wisc.edu%3E) c86f849d5a Ruby: Add support for functional accesses
This patch rpovides functional access support in Ruby. Currently only
the M5Port of RubyPort supports functional accesses. The support for
functional through the PioPort will be added as a separate patch.
2011-06-30 19:49:26 -05:00

1887 lines
63 KiB
Text

/*
* 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.
*
* Authors: Milo Martin
* Brad Beckmann
*/
machine(Directory, "AMD Hammer-like protocol")
: DirectoryMemory * directory,
CacheMemory * probeFilter,
MemoryControl * memBuffer,
int memory_controller_latency = 2,
bool probe_filter_enabled = false,
bool full_bit_dir_enabled = false
{
MessageBuffer forwardFromDir, network="To", virtual_network="3", ordered="false", vnet_type="forward";
MessageBuffer responseFromDir, network="To", virtual_network="4", ordered="false", 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", ordered="true", vnet_type="response";
MessageBuffer unblockToDir, network="From", virtual_network="5", ordered="false", vnet_type="unblock";
MessageBuffer responseToDir, network="From", virtual_network="4", ordered="false", vnet_type="response";
MessageBuffer requestToDir, network="From", virtual_network="2", ordered="false", vnet_type="request", recycle_latency="1";
MessageBuffer dmaRequestToDir, network="From", virtual_network="0", ordered="true", vnet_type="request";
// 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="AbstractEntry") {
State DirectoryState, desc="Directory state";
DataBlock DataBlk, desc="data for the block";
}
// ProbeFilterEntry
structure(PfEntry, desc="...", interface="AbstractCacheEntry") {
State PfState, desc="Directory state";
MachineID Owner, desc="Owner node";
DataBlock DataBlk, desc="data for the block";
Set Sharers, desc="sharing vector for full bit directory";
}
// TBE entries for DMA requests
structure(TBE, desc="TBE entries for outstanding DMA requests") {
Address 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(Address);
void allocate(Address);
void deallocate(Address);
bool isPresent(Address);
}
void set_cache_entry(AbstractCacheEntry b);
void unset_cache_entry();
void set_tbe(TBE a);
void unset_tbe();
void wakeUpBuffers(Address a);
// ** OBJECTS **
Set fwd_set;
TBETable TBEs, template_hack="<Directory_TBE>";
Entry getDirectoryEntry(Address addr), return_by_ref="yes" {
return static_cast(Entry, directory[addr]);
}
DataBlock getDataBlock(Address addr), return_by_ref="yes" {
return getDirectoryEntry(addr).DataBlk;
}
PfEntry getProbeFilterEntry(Address 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, Address 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, Address 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(Address 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, Address addr, State state) {
getDirectoryEntry(addr).changePermission(Directory_State_to_permission(state));
}
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");
}
}
MessageBuffer triggerQueue, ordered="true";
// ** OUT_PORTS **
out_port(requestQueue_out, ResponseMsg, requestToDir); // For recycling requests
out_port(forwardNetwork_out, RequestMsg, forwardFromDir);
out_port(responseNetwork_out, ResponseMsg, responseFromDir);
out_port(dmaResponseNetwork_out, DMAResponseMsg, dmaResponseFromDir);
out_port(triggerQueue_out, TriggerMsg, triggerQueue);
//
// Memory buffer for memory controller to DIMM communication
//
out_port(memQueue_out, MemoryMsg, memBuffer);
// ** IN_PORTS **
// Trigger Queue
in_port(triggerQueue_in, TriggerMsg, triggerQueue, rank=5) {
if (triggerQueue_in.isReady()) {
peek(triggerQueue_in, TriggerMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.Address);
TBE tbe := TBEs[in_msg.Address];
if (in_msg.Type == TriggerType:ALL_ACKS) {
trigger(Event:All_acks_and_owner_data, in_msg.Address,
pf_entry, tbe);
} else if (in_msg.Type == TriggerType:ALL_ACKS_OWNER_EXISTS) {
trigger(Event:All_acks_and_shared_data, in_msg.Address,
pf_entry, tbe);
} else if (in_msg.Type == TriggerType:ALL_ACKS_NO_SHARERS) {
trigger(Event:All_acks_and_data_no_sharers, in_msg.Address,
pf_entry, tbe);
} else if (in_msg.Type == TriggerType:ALL_UNBLOCKS) {
trigger(Event:All_Unblocks, in_msg.Address,
pf_entry, tbe);
} else {
error("Unexpected message");
}
}
}
}
in_port(unblockNetwork_in, ResponseMsg, unblockToDir, rank=4) {
if (unblockNetwork_in.isReady()) {
peek(unblockNetwork_in, ResponseMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.Address);
TBE tbe := TBEs[in_msg.Address];
if (in_msg.Type == CoherenceResponseType:UNBLOCK) {
trigger(Event:Unblock, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:UNBLOCKS) {
trigger(Event:UnblockS, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:UNBLOCKM) {
trigger(Event:UnblockM, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:WB_CLEAN) {
trigger(Event:Writeback_Clean, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:WB_DIRTY) {
trigger(Event:Writeback_Dirty, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:WB_EXCLUSIVE_CLEAN) {
trigger(Event:Writeback_Exclusive_Clean, in_msg.Address,
pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:WB_EXCLUSIVE_DIRTY) {
trigger(Event:Writeback_Exclusive_Dirty, in_msg.Address,
pf_entry, tbe);
} else {
error("Invalid message");
}
}
}
}
// Response Network
in_port(responseToDir_in, ResponseMsg, responseToDir, rank=3) {
if (responseToDir_in.isReady()) {
peek(responseToDir_in, ResponseMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.Address);
TBE tbe := TBEs[in_msg.Address];
if (in_msg.Type == CoherenceResponseType:ACK) {
trigger(Event:Ack, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:ACK_SHARED) {
trigger(Event:Shared_Ack, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:DATA_SHARED) {
trigger(Event:Shared_Data, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:DATA) {
trigger(Event:Data, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE) {
trigger(Event:Exclusive_Data, in_msg.Address, pf_entry, tbe);
} else {
error("Unexpected message");
}
}
}
}
// off-chip memory request/response is done
in_port(memQueue_in, MemoryMsg, memBuffer, rank=2) {
if (memQueue_in.isReady()) {
peek(memQueue_in, MemoryMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.Address);
TBE tbe := TBEs[in_msg.Address];
if (in_msg.Type == MemoryRequestType:MEMORY_READ) {
trigger(Event:Memory_Data, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == MemoryRequestType:MEMORY_WB) {
trigger(Event:Memory_Ack, in_msg.Address, 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()) {
peek(requestQueue_in, RequestMsg) {
PfEntry pf_entry := getProbeFilterEntry(in_msg.Address);
TBE tbe := TBEs[in_msg.Address];
if (in_msg.Type == CoherenceRequestType:PUT) {
trigger(Event:PUT, in_msg.Address, pf_entry, tbe);
} else if (in_msg.Type == CoherenceRequestType:PUTF) {
trigger(Event:PUTF, in_msg.Address, 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.Address,
pf_entry, tbe);
} else {
if (probeFilter.cacheAvail(in_msg.Address)) {
trigger(cache_request_to_event(in_msg.Type), in_msg.Address,
pf_entry, tbe);
} else {
trigger(Event:Pf_Replacement,
probeFilter.cacheProbe(in_msg.Address),
getProbeFilterEntry(probeFilter.cacheProbe(in_msg.Address)),
TBEs[probeFilter.cacheProbe(in_msg.Address)]);
}
}
} else {
trigger(cache_request_to_event(in_msg.Type), in_msg.Address,
pf_entry, tbe);
}
}
}
}
}
in_port(dmaRequestQueue_in, DMARequestMsg, dmaRequestToDir, rank=0) {
if (dmaRequestQueue_in.isReady()) {
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, latency=memory_controller_latency) {
out_msg.Address := 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)) {
enqueue(forwardNetwork_out, RequestMsg, latency=memory_controller_latency) {
out_msg.Address := 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, latency=memory_controller_latency) {
out_msg.Address := 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") {
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") {
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();
}
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.Address := 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.Address := 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.Address := 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, latency="1") {
assert(is_valid(tbe));
out_msg.Address := 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, latency="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, latency="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, latency="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, latency="1") {
out_msg.Address := address;
out_msg.Type := MemoryRequestType:MEMORY_READ;
out_msg.Sender := machineID;
out_msg.OriginalRequestorMachId := in_msg.Requestor;
out_msg.MessageSize := in_msg.MessageSize;
out_msg.DataBlk := getDirectoryEntry(address).DataBlk;
DPRINTF(RubySlicc, "%s\n", out_msg);
}
}
}
action(qd_queueMemoryRequestFromDmaRead, "qd", desc="Queue off-chip fetch request") {
peek(dmaRequestQueue_in, DMARequestMsg) {
enqueue(memQueue_out, MemoryMsg, latency="1") {
out_msg.Address := address;
out_msg.Type := MemoryRequestType:MEMORY_READ;
out_msg.Sender := machineID;
out_msg.OriginalRequestorMachId := in_msg.Requestor;
out_msg.MessageSize := in_msg.MessageSize;
out_msg.DataBlk := getDirectoryEntry(address).DataBlk;
DPRINTF(RubySlicc, "%s\n", out_msg);
}
}
}
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, latency=memory_controller_latency) {
out_msg.Address := 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 := get_time();
assert(tbe.SilentAcks > 0);
out_msg.SilentAcks := tbe.SilentAcks;
}
}
}
} else {
peek(requestQueue_in, RequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, latency=memory_controller_latency) {
out_msg.Address := 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 := get_time();
}
}
}
}
}
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, latency=memory_controller_latency) {
out_msg.Address := 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, latency=memory_controller_latency) {
out_msg.Address := 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, latency=memory_controller_latency) {
assert(is_valid(cache_entry));
out_msg.Address := 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, latency=memory_controller_latency) {
out_msg.Address := 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 := get_time();
out_msg.SilentAcks := machineCount(MachineType:L1Cache) - fwd_set.count();
out_msg.SilentAcks := out_msg.SilentAcks - 1;
}
}
} else {
enqueue(forwardNetwork_out, RequestMsg, latency=memory_controller_latency) {
out_msg.Address := 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 := get_time();
}
}
}
}
}
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, latency=memory_controller_latency) {
assert(is_valid(tbe));
out_msg.Address := 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 := get_time();
}
}
}
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, latency=memory_controller_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.Owner);
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.DirectedProbe := true;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ForwardRequestTime := get_time();
}
}
} else {
peek(requestQueue_in, RequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, latency=memory_controller_latency) {
out_msg.Address := 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 := get_time();
}
}
}
}
action(nofc_forwardRequestConditionalOwner, "nofc", desc="Forward request to one or more nodes if the requestor is not the owner") {
assert(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, latency=memory_controller_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.Owner);
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.DirectedProbe := true;
out_msg.InitialRequestTime := in_msg.InitialRequestTime;
out_msg.ForwardRequestTime := get_time();
}
}
}
} else {
peek(requestQueue_in, RequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, latency=memory_controller_latency) {
out_msg.Address := 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 := get_time();
}
}
}
}
action(f_forwardWriteFromDma, "fw", desc="Forward requests") {
assert(is_valid(tbe));
if (tbe.NumPendingMsgs > 0) {
peek(dmaRequestQueue_in, DMARequestMsg) {
enqueue(forwardNetwork_out, RequestMsg, latency=memory_controller_latency) {
out_msg.Address := 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, latency=memory_controller_latency) {
out_msg.Address := 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();
}
action(j_popIncomingUnblockQueue, "j", desc="Pop incoming unblock queue") {
peek(unblockNetwork_in, ResponseMsg) {
APPEND_TRANSITION_COMMENT(in_msg.Sender);
}
unblockNetwork_in.dequeue();
}
action(k_wakeUpDependents, "k", desc="wake-up dependents") {
wakeUpBuffers(address);
}
action(l_popMemQueue, "q", desc="Pop off-chip request queue") {
memQueue_in.dequeue();
}
action(g_popTriggerQueue, "g", desc="Pop trigger queue") {
triggerQueue_in.dequeue();
}
action(p_popDmaRequestQueue, "pd", desc="pop dma request queue") {
dmaRequestQueue_in.dequeue();
}
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(wr_writeResponseDataToMemory, "wr", desc="Write response data to memory") {
peek(responseToDir_in, ResponseMsg) {
getDirectoryEntry(address).DataBlk := in_msg.DataBlk;
DPRINTF(RubySlicc, "Address: %s, Data Block: %s\n",
in_msg.Address, in_msg.DataBlk);
}
}
action(l_writeDataToMemory, "l", desc="Write PUTX/PUTO data to memory") {
peek(unblockNetwork_in, ResponseMsg) {
assert(in_msg.Dirty);
assert(in_msg.MessageSize == MessageSizeType:Writeback_Data);
getDirectoryEntry(address).DataBlk := in_msg.DataBlk;
DPRINTF(RubySlicc, "Address: %s, Data Block: %s\n",
in_msg.Address, in_msg.DataBlk);
}
}
action(dwt_writeDmaDataFromTBE, "dwt", desc="DMA Write data to memory from TBE") {
DPRINTF(RubySlicc, "%s\n", getDirectoryEntry(address).DataBlk);
assert(is_valid(tbe));
getDirectoryEntry(address).DataBlk := tbe.DataBlk;
DPRINTF(RubySlicc, "%s\n", getDirectoryEntry(address).DataBlk);
getDirectoryEntry(address).DataBlk.copyPartial(tbe.DmaDataBlk, addressOffset(tbe.PhysicalAddress), tbe.Len);
DPRINTF(RubySlicc, "%s\n", getDirectoryEntry(address).DataBlk);
}
action(wdt_writeDataFromTBE, "wdt", desc="DMA Write data to memory from TBE") {
assert(is_valid(tbe));
DPRINTF(RubySlicc, "%s\n", getDirectoryEntry(address).DataBlk);
getDirectoryEntry(address).DataBlk := tbe.DataBlk;
DPRINTF(RubySlicc, "%s\n", getDirectoryEntry(address).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, latency="1") {
out_msg.Address := address;
out_msg.Type := MemoryRequestType:MEMORY_WB;
DPRINTF(RubySlicc, "%s\n", out_msg);
}
}
}
action(ld_queueMemoryDmaWrite, "ld", desc="Write DMA data to memory") {
enqueue(memQueue_out, MemoryMsg, latency="1") {
assert(is_valid(tbe));
out_msg.Address := address;
out_msg.Type := MemoryRequestType:MEMORY_WB;
// first, initialize the data blk to the current version of system memory
out_msg.DataBlk := tbe.DataBlk;
// then add the dma write data
out_msg.DataBlk.copyPartial(tbe.DmaDataBlk, addressOffset(tbe.PhysicalAddress), tbe.Len);
DPRINTF(RubySlicc, "%s\n", out_msg);
}
}
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);
// NOTE: The following check would not be valid in a real
// implementation. We include the data in the "dataless"
// message so we can assert the clean data matches the datablock
// in memory
assert(getDirectoryEntry(address).DataBlk == 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) {
wr_writeResponseDataToMemory;
m_decrementNumberOfMessages;
o_checkForCompletion;
n_popResponseQueue;
}
transition(NO_R, {Data, Exclusive_Data}) {
wr_writeResponseDataToMemory;
m_decrementNumberOfMessages;
o_checkForCompletion;
n_popResponseQueue;
}
transition({O_R, S_R, NO_R}, All_acks_and_data_no_sharers, E) {
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, O) {
//
// 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;
wdt_writeDataFromTBE;
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;
wdt_writeDataFromTBE;
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_DR_B_D, All_acks_and_owner_data, O) {
//
// 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;
wdt_writeDataFromTBE;
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;
wdt_writeDataFromTBE;
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(O_DR_B, All_acks_and_owner_data, O) {
wdt_writeDataFromTBE;
w_deallocateTBE;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(O_DR_B, All_acks_and_data_no_sharers, E) {
wdt_writeDataFromTBE;
w_deallocateTBE;
pfd_probeFilterDeallocate;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_DR_B, All_acks_and_data_no_sharers, E) {
//
// 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;
wdt_writeDataFromTBE;
w_deallocateTBE;
ppfd_possibleProbeFilterDeallocate;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_DR_B_D, All_acks_and_data_no_sharers, E) {
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;
wdt_writeDataFromTBE;
w_deallocateTBE;
ppfd_possibleProbeFilterDeallocate;
k_wakeUpDependents;
g_popTriggerQueue;
}
transition(NO_DW_B_W, All_acks_and_data_no_sharers, NO_DW_W) {
dwt_writeDmaDataFromTBE;
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) {
l_writeDataToMemory;
rs_removeSharer;
l_queueMemoryWBRequest;
j_popIncomingUnblockQueue;
}
transition(WB, Writeback_Exclusive_Dirty, WB_E_W) {
l_writeDataToMemory;
rs_removeSharer;
l_queueMemoryWBRequest;
j_popIncomingUnblockQueue;
}
transition(WB_E_W, Memory_Ack, E) {
pfd_probeFilterDeallocate;
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;
}
}