| /* |
| * 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; |
| } |
| } |