| /* |
| * Copyright (c) 2013-2015 Advanced Micro Devices, Inc. |
| * All rights reserved. |
| * |
| * For use for simulation and test purposes only |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. 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. |
| * |
| * 3. Neither the name of the copyright holder 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 HOLDER 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. |
| * |
| * Authors: Lisa Hsu, |
| * Sooraj Puthoor |
| */ |
| |
| /* |
| * This file is based on MOESI_AMD_Base.sm |
| * Differences with AMD base protocol |
| * -- Uses a probe filter memory to track sharers. |
| * -- The probe filter can be inclusive or non-inclusive |
| * -- Only two sharers tracked. Sharers are a) GPU or/and b) CPU |
| * -- If sharer information available, the sharer is probed |
| * -- If sharer information not available, probes are broadcasted |
| */ |
| |
| machine(MachineType:Directory, "AMD Baseline protocol") |
| : DirectoryMemory * directory; |
| CacheMemory * L3CacheMemory; |
| CacheMemory * ProbeFilterMemory; |
| Cycles response_latency := 5; |
| Cycles l3_hit_latency := 50; |
| bool noTCCdir := "False"; |
| bool CAB_TCC := "False"; |
| int TCC_select_num_bits:=1; |
| bool useL3OnWT := "False"; |
| bool inclusiveDir := "True"; |
| Cycles to_memory_controller_latency := 1; |
| |
| // From the Cores |
| MessageBuffer * requestFromCores, network="From", virtual_network="0", ordered="false", vnet_type="request"; |
| MessageBuffer * responseFromCores, network="From", virtual_network="2", ordered="false", vnet_type="response"; |
| MessageBuffer * unblockFromCores, network="From", virtual_network="4", ordered="false", vnet_type="unblock"; |
| |
| MessageBuffer * probeToCore, network="To", virtual_network="0", ordered="false", vnet_type="request"; |
| MessageBuffer * responseToCore, network="To", virtual_network="2", ordered="false", vnet_type="response"; |
| |
| MessageBuffer * triggerQueue, ordered="true"; |
| MessageBuffer * L3triggerQueue, ordered="true"; |
| MessageBuffer * responseFromMemory; |
| { |
| // STATES |
| state_declaration(State, desc="Directory states", default="Directory_State_U") { |
| U, AccessPermission:Backing_Store, desc="unblocked"; |
| BL, AccessPermission:Busy, desc="got L3 WB request"; |
| // BL is Busy because it is busy waiting for the data |
| // which is possibly in the network. The cache which evicted the data |
| // might have moved to some other state after doing the eviction |
| // BS==> Received a read request; has not requested ownership |
| // B==> Received a read request; has requested ownership |
| // BM==> Received a modification request |
| B_P, AccessPermission:Backing_Store, desc="Back invalidation, waiting for probes"; |
| BS_M, AccessPermission:Backing_Store, desc="blocked waiting for memory"; |
| BM_M, AccessPermission:Backing_Store, desc="blocked waiting for memory"; |
| B_M, AccessPermission:Backing_Store, desc="blocked waiting for memory"; |
| BP, AccessPermission:Backing_Store, desc="blocked waiting for probes, no need for memory"; |
| BS_PM, AccessPermission:Backing_Store, desc="blocked waiting for probes and Memory"; |
| BM_PM, AccessPermission:Backing_Store, desc="blocked waiting for probes and Memory"; |
| B_PM, AccessPermission:Backing_Store, desc="blocked waiting for probes and Memory"; |
| BS_Pm, AccessPermission:Backing_Store, desc="blocked waiting for probes, already got memory"; |
| BM_Pm, AccessPermission:Backing_Store, desc="blocked waiting for probes, already got memory"; |
| B_Pm, AccessPermission:Backing_Store, desc="blocked waiting for probes, already got memory"; |
| B, AccessPermission:Backing_Store, desc="sent response, Blocked til ack"; |
| } |
| |
| // Events |
| enumeration(Event, desc="Directory events") { |
| // CPU requests |
| RdBlkS, desc="..."; |
| RdBlkM, desc="..."; |
| RdBlk, desc="..."; |
| CtoD, desc="..."; |
| WriteThrough, desc="WriteThrough Message"; |
| Atomic, desc="Atomic Message"; |
| |
| // writebacks |
| VicDirty, desc="..."; |
| VicClean, desc="..."; |
| CPUData, desc="WB data from CPU"; |
| StaleWB, desc="Notification that WB has been superceded by a probe"; |
| |
| // probe responses |
| CPUPrbResp, desc="Probe Response Msg"; |
| |
| ProbeAcksComplete, desc="Probe Acks Complete"; |
| |
| L3Hit, desc="Hit in L3 return data to core"; |
| |
| // Replacement |
| PF_Repl, desc="Replace address from probe filter"; |
| |
| // Memory Controller |
| MemData, desc="Fetched data from memory arrives"; |
| WBAck, desc="Writeback Ack from memory arrives"; |
| |
| CoreUnblock, desc="Core received data, unblock"; |
| UnblockWriteThrough, desc="Unblock because of writethrough request finishing"; |
| |
| StaleVicDirty, desc="Core invalidated before VicDirty processed"; |
| } |
| |
| enumeration(RequestType, desc="To communicate stats from transitions to recordStats") { |
| L3DataArrayRead, desc="Read the data array"; |
| L3DataArrayWrite, desc="Write the data array"; |
| L3TagArrayRead, desc="Read the data array"; |
| L3TagArrayWrite, desc="Write the data array"; |
| |
| PFTagArrayRead, desc="Read the data array"; |
| PFTagArrayWrite, desc="Write the data array"; |
| } |
| |
| // TYPES |
| |
| enumeration(ProbeFilterState, desc="") { |
| T, desc="Tracked"; |
| NT, desc="Not tracked"; |
| B, desc="Blocked, This entry is being replaced"; |
| } |
| |
| // DirectoryEntry |
| structure(Entry, desc="...", interface="AbstractEntry") { |
| State DirectoryState, desc="Directory state"; |
| DataBlock DataBlk, desc="data for the block"; |
| NetDest VicDirtyIgnore, desc="VicDirty coming from whom to ignore"; |
| } |
| |
| structure(CacheEntry, desc="...", interface="AbstractCacheEntry") { |
| DataBlock DataBlk, desc="data for the block"; |
| MachineID LastSender, desc="Mach which this block came from"; |
| ProbeFilterState pfState, desc="ProbeFilter state",default="Directory_ProbeFilterState_NT"; |
| bool isOnCPU, desc="Block valid in the CPU complex",default="false"; |
| bool isOnGPU, desc="Block valid in the GPU complex",default="false"; |
| } |
| |
| structure(TBE, desc="...") { |
| State TBEState, desc="Transient state"; |
| DataBlock DataBlk, desc="data for the block"; |
| bool Dirty, desc="Is the data dirty?"; |
| int NumPendingAcks, desc="num acks expected"; |
| MachineID OriginalRequestor, desc="Original Requestor"; |
| MachineID WTRequestor, desc="WT Requestor"; |
| bool Cached, desc="data hit in Cache"; |
| bool MemData, desc="Got MemData?",default="false"; |
| bool wtData, desc="Got write through data?",default="false"; |
| bool atomicData, desc="Got Atomic op?",default="false"; |
| Cycles InitialRequestTime, desc="..."; |
| Cycles ForwardRequestTime, desc="..."; |
| Cycles ProbeRequestStartTime, desc="..."; |
| MachineID LastSender, desc="Mach which this block came from"; |
| bool L3Hit, default="false", desc="Was this an L3 hit?"; |
| uint64_t probe_id, desc="probe id for lifetime profiling"; |
| WriteMask writeMask, desc="outstanding write through mask"; |
| Addr demandAddress, desc="Address of demand request which caused probe filter eviction"; |
| } |
| |
| structure(TBETable, external="yes") { |
| TBE lookup(Addr); |
| void allocate(Addr); |
| void deallocate(Addr); |
| bool isPresent(Addr); |
| } |
| |
| TBETable TBEs, template="<Directory_TBE>", constructor="m_number_of_TBEs"; |
| |
| int TCC_select_low_bit, default="RubySystem::getBlockSizeBits()"; |
| |
| Tick clockEdge(); |
| Tick cyclesToTicks(Cycles c); |
| |
| void set_tbe(TBE a); |
| void unset_tbe(); |
| void wakeUpAllBuffers(); |
| void wakeUpBuffers(Addr a); |
| Cycles curCycle(); |
| MachineID mapAddressToMachine(Addr addr, MachineType mtype); |
| |
| Entry getDirectoryEntry(Addr addr), return_by_pointer="yes" { |
| Entry dir_entry := static_cast(Entry, "pointer", directory.lookup(addr)); |
| |
| if (is_valid(dir_entry)) { |
| //DPRINTF(RubySlicc, "Getting entry %s: %s\n", addr, dir_entry.DataBlk); |
| return dir_entry; |
| } |
| |
| dir_entry := static_cast(Entry, "pointer", |
| directory.allocate(addr, new Entry)); |
| return dir_entry; |
| } |
| |
| DataBlock getDataBlock(Addr addr), return_by_ref="yes" { |
| TBE tbe := TBEs.lookup(addr); |
| if (is_valid(tbe) && tbe.MemData) { |
| DPRINTF(RubySlicc, "Returning DataBlk from TBE %s:%s\n", addr, tbe); |
| return tbe.DataBlk; |
| } |
| DPRINTF(RubySlicc, "Returning DataBlk from Dir %s:%s\n", addr, getDirectoryEntry(addr)); |
| return getDirectoryEntry(addr).DataBlk; |
| } |
| |
| State getState(TBE tbe, CacheEntry entry, Addr addr) { |
| CacheEntry probeFilterEntry := static_cast(CacheEntry, "pointer", ProbeFilterMemory.lookup(addr)); |
| if (inclusiveDir) { |
| if (is_valid(probeFilterEntry) && probeFilterEntry.pfState == ProbeFilterState:B) { |
| return State:B_P; |
| } |
| } |
| return getDirectoryEntry(addr).DirectoryState; |
| } |
| |
| void setState(TBE tbe, CacheEntry entry, Addr addr, State state) { |
| getDirectoryEntry(addr).DirectoryState := state; |
| } |
| |
| void functionalRead(Addr addr, Packet *pkt) { |
| TBE tbe := TBEs.lookup(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.lookup(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; |
| } |
| |
| AccessPermission getAccessPermission(Addr addr) { |
| // For this Directory, all permissions are just tracked in Directory, since |
| // it's not possible to have something in TBE but not Dir, just keep track |
| // of state all in one place. |
| if (directory.isPresent(addr)) { |
| return Directory_State_to_permission(getDirectoryEntry(addr).DirectoryState); |
| } |
| |
| return AccessPermission:NotPresent; |
| } |
| |
| void setAccessPermission(CacheEntry entry, Addr addr, State state) { |
| getDirectoryEntry(addr).changePermission(Directory_State_to_permission(state)); |
| } |
| |
| void recordRequestType(RequestType request_type, Addr addr) { |
| if (request_type == RequestType:L3DataArrayRead) { |
| L3CacheMemory.recordRequestType(CacheRequestType:DataArrayRead, addr); |
| } else if (request_type == RequestType:L3DataArrayWrite) { |
| L3CacheMemory.recordRequestType(CacheRequestType:DataArrayWrite, addr); |
| } else if (request_type == RequestType:L3TagArrayRead) { |
| L3CacheMemory.recordRequestType(CacheRequestType:TagArrayRead, addr); |
| } else if (request_type == RequestType:L3TagArrayWrite) { |
| L3CacheMemory.recordRequestType(CacheRequestType:TagArrayWrite, addr); |
| } else if (request_type == RequestType:PFTagArrayRead) { |
| ProbeFilterMemory.recordRequestType(CacheRequestType:TagArrayRead, addr); |
| } else if (request_type == RequestType:PFTagArrayWrite) { |
| ProbeFilterMemory.recordRequestType(CacheRequestType:TagArrayWrite, addr); |
| } |
| } |
| |
| bool checkResourceAvailable(RequestType request_type, Addr addr) { |
| if (request_type == RequestType:L3DataArrayRead) { |
| return L3CacheMemory.checkResourceAvailable(CacheResourceType:DataArray, addr); |
| } else if (request_type == RequestType:L3DataArrayWrite) { |
| return L3CacheMemory.checkResourceAvailable(CacheResourceType:DataArray, addr); |
| } else if (request_type == RequestType:L3TagArrayRead) { |
| return L3CacheMemory.checkResourceAvailable(CacheResourceType:TagArray, addr); |
| } else if (request_type == RequestType:L3TagArrayWrite) { |
| return L3CacheMemory.checkResourceAvailable(CacheResourceType:TagArray, addr); |
| } else if (request_type == RequestType:PFTagArrayRead) { |
| return ProbeFilterMemory.checkResourceAvailable(CacheResourceType:TagArray, addr); |
| } else if (request_type == RequestType:PFTagArrayWrite) { |
| return ProbeFilterMemory.checkResourceAvailable(CacheResourceType:TagArray, addr); |
| } else { |
| error("Invalid RequestType type in checkResourceAvailable"); |
| return true; |
| } |
| } |
| |
| bool isNotPresentProbeFilter(Addr address) { |
| if (ProbeFilterMemory.isTagPresent(address) || |
| ProbeFilterMemory.cacheAvail(address)) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool isGPUSharer(Addr address) { |
| assert(ProbeFilterMemory.isTagPresent(address)); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", ProbeFilterMemory.lookup(address)); |
| if (entry.pfState == ProbeFilterState:NT) { |
| return true; |
| } else if (entry.isOnGPU){ |
| return true; |
| } |
| return false; |
| } |
| |
| bool isCPUSharer(Addr address) { |
| assert(ProbeFilterMemory.isTagPresent(address)); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", ProbeFilterMemory.lookup(address)); |
| if (entry.pfState == ProbeFilterState:NT) { |
| return true; |
| } else if (entry.isOnCPU){ |
| return true; |
| } |
| return false; |
| } |
| |
| |
| // ** OUT_PORTS ** |
| out_port(probeNetwork_out, NBProbeRequestMsg, probeToCore); |
| out_port(responseNetwork_out, ResponseMsg, responseToCore); |
| |
| out_port(triggerQueue_out, TriggerMsg, triggerQueue); |
| out_port(L3TriggerQueue_out, TriggerMsg, L3triggerQueue); |
| |
| // ** IN_PORTS ** |
| |
| // Trigger Queue |
| in_port(triggerQueue_in, TriggerMsg, triggerQueue, rank=5) { |
| if (triggerQueue_in.isReady(clockEdge())) { |
| peek(triggerQueue_in, TriggerMsg) { |
| TBE tbe := TBEs.lookup(in_msg.addr); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(in_msg.addr)); |
| if (in_msg.Type == TriggerType:AcksComplete) { |
| trigger(Event:ProbeAcksComplete, in_msg.addr, entry, tbe); |
| }else if (in_msg.Type == TriggerType:UnblockWriteThrough) { |
| trigger(Event:UnblockWriteThrough, in_msg.addr, entry, tbe); |
| } else { |
| error("Unknown trigger msg"); |
| } |
| } |
| } |
| } |
| |
| in_port(L3TriggerQueue_in, TriggerMsg, L3triggerQueue, rank=4) { |
| if (L3TriggerQueue_in.isReady(clockEdge())) { |
| peek(L3TriggerQueue_in, TriggerMsg) { |
| TBE tbe := TBEs.lookup(in_msg.addr); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(in_msg.addr)); |
| if (in_msg.Type == TriggerType:L3Hit) { |
| trigger(Event:L3Hit, in_msg.addr, entry, tbe); |
| } else { |
| error("Unknown trigger msg"); |
| } |
| } |
| } |
| } |
| |
| // Unblock Network |
| in_port(unblockNetwork_in, UnblockMsg, unblockFromCores, rank=3) { |
| if (unblockNetwork_in.isReady(clockEdge())) { |
| peek(unblockNetwork_in, UnblockMsg) { |
| TBE tbe := TBEs.lookup(in_msg.addr); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(in_msg.addr)); |
| trigger(Event:CoreUnblock, in_msg.addr, entry, tbe); |
| } |
| } |
| } |
| |
| // Core response network |
| in_port(responseNetwork_in, ResponseMsg, responseFromCores, rank=2) { |
| if (responseNetwork_in.isReady(clockEdge())) { |
| peek(responseNetwork_in, ResponseMsg) { |
| TBE tbe := TBEs.lookup(in_msg.addr); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(in_msg.addr)); |
| if (in_msg.Type == CoherenceResponseType:CPUPrbResp) { |
| trigger(Event:CPUPrbResp, in_msg.addr, entry, tbe); |
| } else if (in_msg.Type == CoherenceResponseType:CPUData) { |
| trigger(Event:CPUData, in_msg.addr, entry, tbe); |
| } else if (in_msg.Type == CoherenceResponseType:StaleNotif) { |
| trigger(Event:StaleWB, in_msg.addr, entry, tbe); |
| } else { |
| error("Unexpected response type"); |
| } |
| } |
| } |
| } |
| |
| // off-chip memory request/response is done |
| in_port(memQueue_in, MemoryMsg, responseFromMemory, rank=1) { |
| if (memQueue_in.isReady(clockEdge())) { |
| peek(memQueue_in, MemoryMsg) { |
| TBE tbe := TBEs.lookup(in_msg.addr); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(in_msg.addr)); |
| if (in_msg.Type == MemoryRequestType:MEMORY_READ) { |
| trigger(Event:MemData, in_msg.addr, entry, tbe); |
| DPRINTF(RubySlicc, "%s\n", in_msg); |
| } else if (in_msg.Type == MemoryRequestType:MEMORY_WB) { |
| trigger(Event:WBAck, in_msg.addr, entry, tbe); // ignore WBAcks, don't care about them. |
| } else { |
| DPRINTF(RubySlicc, "%s\n", in_msg.Type); |
| error("Invalid message"); |
| } |
| } |
| } |
| } |
| |
| in_port(requestNetwork_in, CPURequestMsg, requestFromCores, rank=0) { |
| if (requestNetwork_in.isReady(clockEdge())) { |
| peek(requestNetwork_in, CPURequestMsg) { |
| TBE tbe := TBEs.lookup(in_msg.addr); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(in_msg.addr)); |
| if (inclusiveDir && isNotPresentProbeFilter(in_msg.addr)) { |
| Addr victim := ProbeFilterMemory.cacheProbe(in_msg.addr); |
| tbe := TBEs.lookup(victim); |
| entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(victim)); |
| trigger(Event:PF_Repl, victim, entry, tbe); |
| } else if (in_msg.Type == CoherenceRequestType:RdBlk) { |
| trigger(Event:RdBlk, in_msg.addr, entry, tbe); |
| } else if (in_msg.Type == CoherenceRequestType:RdBlkS) { |
| trigger(Event:RdBlkS, in_msg.addr, entry, tbe); |
| } else if (in_msg.Type == CoherenceRequestType:RdBlkM) { |
| trigger(Event:RdBlkM, in_msg.addr, entry, tbe); |
| } else if (in_msg.Type == CoherenceRequestType:WriteThrough) { |
| trigger(Event:WriteThrough, in_msg.addr, entry, tbe); |
| } else if (in_msg.Type == CoherenceRequestType:Atomic) { |
| trigger(Event:Atomic, in_msg.addr, entry, tbe); |
| } else if (in_msg.Type == CoherenceRequestType:VicDirty) { |
| if (getDirectoryEntry(in_msg.addr).VicDirtyIgnore.isElement(in_msg.Requestor)) { |
| DPRINTF(RubySlicc, "Dropping VicDirty for address %s\n", in_msg.addr); |
| trigger(Event:StaleVicDirty, in_msg.addr, entry, tbe); |
| } else { |
| DPRINTF(RubySlicc, "Got VicDirty from %s on %s\n", in_msg.Requestor, in_msg.addr); |
| trigger(Event:VicDirty, in_msg.addr, entry, tbe); |
| } |
| } else if (in_msg.Type == CoherenceRequestType:VicClean) { |
| if (getDirectoryEntry(in_msg.addr).VicDirtyIgnore.isElement(in_msg.Requestor)) { |
| DPRINTF(RubySlicc, "Dropping VicClean for address %s\n", in_msg.addr); |
| trigger(Event:StaleVicDirty, in_msg.addr, entry, tbe); |
| } else { |
| DPRINTF(RubySlicc, "Got VicClean from %s on %s\n", in_msg.Requestor, in_msg.addr); |
| trigger(Event:VicClean, in_msg.addr, entry, tbe); |
| } |
| } else { |
| error("Bad request message type"); |
| } |
| } |
| } |
| } |
| |
| // Actions |
| action(s_sendResponseS, "s", desc="send Shared response") { |
| enqueue(responseNetwork_out, ResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.Type := CoherenceResponseType:NBSysResp; |
| if (tbe.L3Hit) { |
| out_msg.Sender := createMachineID(MachineType:L3Cache, intToID(0)); |
| } else { |
| out_msg.Sender := machineID; |
| } |
| out_msg.Destination.add(tbe.OriginalRequestor); |
| out_msg.DataBlk := tbe.DataBlk; |
| out_msg.MessageSize := MessageSizeType:Response_Data; |
| out_msg.Dirty := false; |
| out_msg.State := CoherenceState:Shared; |
| out_msg.InitialRequestTime := tbe.InitialRequestTime; |
| out_msg.ForwardRequestTime := tbe.ForwardRequestTime; |
| out_msg.ProbeRequestStartTime := tbe.ProbeRequestStartTime; |
| out_msg.OriginalResponder := tbe.LastSender; |
| out_msg.L3Hit := tbe.L3Hit; |
| DPRINTF(RubySlicc, "%s\n", out_msg); |
| } |
| } |
| |
| action(es_sendResponseES, "es", desc="send Exclusive or Shared response") { |
| enqueue(responseNetwork_out, ResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.Type := CoherenceResponseType:NBSysResp; |
| if (tbe.L3Hit) { |
| out_msg.Sender := createMachineID(MachineType:L3Cache, intToID(0)); |
| } else { |
| out_msg.Sender := machineID; |
| } |
| out_msg.Destination.add(tbe.OriginalRequestor); |
| out_msg.DataBlk := tbe.DataBlk; |
| out_msg.MessageSize := MessageSizeType:Response_Data; |
| out_msg.Dirty := tbe.Dirty; |
| if (tbe.Cached) { |
| out_msg.State := CoherenceState:Shared; |
| } else { |
| out_msg.State := CoherenceState:Exclusive; |
| } |
| out_msg.InitialRequestTime := tbe.InitialRequestTime; |
| out_msg.ForwardRequestTime := tbe.ForwardRequestTime; |
| out_msg.ProbeRequestStartTime := tbe.ProbeRequestStartTime; |
| out_msg.OriginalResponder := tbe.LastSender; |
| out_msg.L3Hit := tbe.L3Hit; |
| DPRINTF(RubySlicc, "%s\n", out_msg); |
| } |
| } |
| |
| // write-through and atomics do not send an unblock ack back to the |
| // directory. Hence, directory has to generate a self unblocking |
| // message. Additionally, write through's does not require data |
| // in its response. Hence, write through is treated seperately from |
| // write-back and atomics |
| action(m_sendResponseM, "m", desc="send Modified response") { |
| if (tbe.wtData) { |
| enqueue(triggerQueue_out, TriggerMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := TriggerType:UnblockWriteThrough; |
| } |
| }else{ |
| enqueue(responseNetwork_out, ResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.Type := CoherenceResponseType:NBSysResp; |
| if (tbe.L3Hit) { |
| out_msg.Sender := createMachineID(MachineType:L3Cache, intToID(0)); |
| } else { |
| out_msg.Sender := machineID; |
| } |
| out_msg.Destination.add(tbe.OriginalRequestor); |
| out_msg.DataBlk := tbe.DataBlk; |
| out_msg.MessageSize := MessageSizeType:Response_Data; |
| out_msg.Dirty := tbe.Dirty; |
| out_msg.State := CoherenceState:Modified; |
| out_msg.CtoD := false; |
| out_msg.InitialRequestTime := tbe.InitialRequestTime; |
| out_msg.ForwardRequestTime := tbe.ForwardRequestTime; |
| out_msg.ProbeRequestStartTime := tbe.ProbeRequestStartTime; |
| out_msg.OriginalResponder := tbe.LastSender; |
| if(tbe.atomicData){ |
| out_msg.WTRequestor := tbe.WTRequestor; |
| } |
| out_msg.L3Hit := tbe.L3Hit; |
| DPRINTF(RubySlicc, "%s\n", out_msg); |
| } |
| if (tbe.atomicData) { |
| enqueue(triggerQueue_out, TriggerMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := TriggerType:UnblockWriteThrough; |
| } |
| } |
| } |
| } |
| |
| action(c_sendResponseCtoD, "c", desc="send CtoD Ack") { |
| enqueue(responseNetwork_out, ResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.Type := CoherenceResponseType:NBSysResp; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(tbe.OriginalRequestor); |
| out_msg.MessageSize := MessageSizeType:Response_Control; |
| out_msg.Dirty := false; |
| out_msg.State := CoherenceState:Modified; |
| out_msg.CtoD := true; |
| out_msg.InitialRequestTime := tbe.InitialRequestTime; |
| out_msg.ForwardRequestTime := curCycle(); |
| out_msg.ProbeRequestStartTime := tbe.ProbeRequestStartTime; |
| DPRINTF(RubySlicc, "%s\n", out_msg); |
| } |
| } |
| |
| action(w_sendResponseWBAck, "w", desc="send WB Ack") { |
| peek(requestNetwork_in, CPURequestMsg) { |
| enqueue(responseNetwork_out, ResponseMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := CoherenceResponseType:NBSysWBAck; |
| out_msg.Destination.add(in_msg.Requestor); |
| out_msg.WTRequestor := in_msg.WTRequestor; |
| out_msg.Sender := machineID; |
| out_msg.MessageSize := MessageSizeType:Writeback_Control; |
| out_msg.InitialRequestTime := in_msg.InitialRequestTime; |
| out_msg.ForwardRequestTime := curCycle(); |
| out_msg.ProbeRequestStartTime := curCycle(); |
| } |
| } |
| } |
| |
| action(l_queueMemWBReq, "lq", desc="Write WB data to memory") { |
| peek(responseNetwork_in, ResponseMsg) { |
| queueMemoryWrite(machineID, address, to_memory_controller_latency, |
| in_msg.DataBlk); |
| } |
| } |
| |
| action(l_queueMemRdReq, "lr", desc="Read data from memory") { |
| peek(requestNetwork_in, CPURequestMsg) { |
| if (L3CacheMemory.isTagPresent(address)) { |
| enqueue(L3TriggerQueue_out, TriggerMsg, l3_hit_latency) { |
| out_msg.addr := address; |
| out_msg.Type := TriggerType:L3Hit; |
| DPRINTF(RubySlicc, "%s\n", out_msg); |
| } |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(address)); |
| tbe.DataBlk := entry.DataBlk; |
| tbe.LastSender := entry.LastSender; |
| tbe.L3Hit := true; |
| tbe.MemData := true; |
| L3CacheMemory.deallocate(address); |
| } else { |
| queueMemoryRead(machineID, address, to_memory_controller_latency); |
| } |
| } |
| } |
| |
| action(dc_probeInvCoreData, "dc", desc="probe inv cores, return data") { |
| peek(requestNetwork_in, CPURequestMsg) { |
| enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.Type := ProbeRequestType:PrbInv; |
| out_msg.ReturnData := true; |
| out_msg.MessageSize := MessageSizeType:Control; |
| if(isCPUSharer(address)) { |
| out_msg.Destination.broadcast(MachineType:CorePair); // won't be realistic for multisocket |
| } |
| |
| // add relevant TCC node to list. This replaces all TCPs and SQCs |
| if(isGPUSharer(address)) { |
| if ((in_msg.Type == CoherenceRequestType:WriteThrough || |
| in_msg.Type == CoherenceRequestType:Atomic) && |
| in_msg.NoWriteConflict) { |
| // Don't Include TCCs unless there was write-CAB conflict in the TCC |
| } else if(noTCCdir) { |
| out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC, |
| TCC_select_low_bit, TCC_select_num_bits)); |
| } else { |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:TCCdir)); |
| } |
| } |
| out_msg.Destination.remove(in_msg.Requestor); |
| tbe.NumPendingAcks := out_msg.Destination.count(); |
| if (tbe.NumPendingAcks == 0) { |
| enqueue(triggerQueue_out, TriggerMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := TriggerType:AcksComplete; |
| } |
| } |
| DPRINTF(RubySlicc, "%s\n", out_msg); |
| APPEND_TRANSITION_COMMENT(" dc: Acks remaining: "); |
| APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks); |
| tbe.ProbeRequestStartTime := curCycle(); |
| } |
| } |
| } |
| |
| action(bp_backProbe, "bp", desc="back probe") { |
| enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.Type := ProbeRequestType:PrbInv; |
| out_msg.ReturnData := true; |
| out_msg.MessageSize := MessageSizeType:Control; |
| if(isCPUSharer(address)) { |
| // won't be realistic for multisocket |
| out_msg.Destination.broadcast(MachineType:CorePair); |
| } |
| // add relevant TCC node to the list. This replaces all TCPs and SQCs |
| if(isGPUSharer(address)) { |
| if (noTCCdir) { |
| //Don't need to notify TCC about reads |
| } else { |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:TCCdir)); |
| tbe.NumPendingAcks := tbe.NumPendingAcks + 1; |
| } |
| if (noTCCdir && CAB_TCC) { |
| out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC, |
| TCC_select_low_bit, TCC_select_num_bits)); |
| } |
| } |
| tbe.NumPendingAcks := out_msg.Destination.count(); |
| if (tbe.NumPendingAcks == 0) { |
| enqueue(triggerQueue_out, TriggerMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := TriggerType:AcksComplete; |
| } |
| } |
| DPRINTF(RubySlicc, "%s\n", (out_msg)); |
| APPEND_TRANSITION_COMMENT(" sc: Acks remaining: "); |
| APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks); |
| APPEND_TRANSITION_COMMENT(" - back probe"); |
| tbe.ProbeRequestStartTime := curCycle(); |
| } |
| } |
| |
| action(sc_probeShrCoreData, "sc", desc="probe shared cores, return data") { |
| peek(requestNetwork_in, CPURequestMsg) { // not the right network? |
| enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.Type := ProbeRequestType:PrbDowngrade; |
| out_msg.ReturnData := true; |
| out_msg.MessageSize := MessageSizeType:Control; |
| if(isCPUSharer(address)) { |
| out_msg.Destination.broadcast(MachineType:CorePair); // won't be realistic for multisocket |
| } |
| // add relevant TCC node to the list. This replaces all TCPs and SQCs |
| if(isGPUSharer(address)) { |
| if (noTCCdir) { |
| //Don't need to notify TCC about reads |
| } else { |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:TCCdir)); |
| tbe.NumPendingAcks := tbe.NumPendingAcks + 1; |
| } |
| if (noTCCdir && CAB_TCC) { |
| out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC, |
| TCC_select_low_bit, TCC_select_num_bits)); |
| } |
| } |
| out_msg.Destination.remove(in_msg.Requestor); |
| tbe.NumPendingAcks := out_msg.Destination.count(); |
| if (tbe.NumPendingAcks == 0) { |
| enqueue(triggerQueue_out, TriggerMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := TriggerType:AcksComplete; |
| } |
| } |
| DPRINTF(RubySlicc, "%s\n", (out_msg)); |
| APPEND_TRANSITION_COMMENT(" sc: Acks remaining: "); |
| APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks); |
| tbe.ProbeRequestStartTime := curCycle(); |
| } |
| } |
| } |
| |
| action(ic_probeInvCore, "ic", desc="probe invalidate core, no return data needed") { |
| peek(requestNetwork_in, CPURequestMsg) { // not the right network? |
| enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.Type := ProbeRequestType:PrbInv; |
| out_msg.ReturnData := false; |
| out_msg.MessageSize := MessageSizeType:Control; |
| if(isCPUSharer(address)) { |
| out_msg.Destination.broadcast(MachineType:CorePair); // won't be realistic for multisocket |
| } |
| |
| // add relevant TCC node to the list. This replaces all TCPs and SQCs |
| if(isGPUSharer(address)) { |
| if (noTCCdir) { |
| out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC, |
| TCC_select_low_bit, TCC_select_num_bits)); |
| } else { |
| out_msg.Destination.add(mapAddressToMachine(address, MachineType:TCCdir)); |
| } |
| } |
| out_msg.Destination.remove(in_msg.Requestor); |
| tbe.NumPendingAcks := out_msg.Destination.count(); |
| if (tbe.NumPendingAcks == 0) { |
| enqueue(triggerQueue_out, TriggerMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := TriggerType:AcksComplete; |
| } |
| } |
| APPEND_TRANSITION_COMMENT(" ic: Acks remaining: "); |
| APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks); |
| DPRINTF(RubySlicc, "%s\n", out_msg); |
| tbe.ProbeRequestStartTime := curCycle(); |
| } |
| } |
| } |
| |
| action(sm_setMRU, "sm", desc="set probe filter entry as MRU") { |
| ProbeFilterMemory.setMRU(address); |
| } |
| |
| action(d_writeDataToMemory, "d", desc="Write data to memory") { |
| peek(responseNetwork_in, ResponseMsg) { |
| getDirectoryEntry(address).DataBlk := in_msg.DataBlk; |
| DPRINTF(RubySlicc, "Writing Data: %s to address %s\n", in_msg.DataBlk, |
| in_msg.addr); |
| } |
| } |
| |
| action(te_allocateTBEForEviction, "te", desc="allocate TBE Entry") { |
| check_allocate(TBEs); |
| TBEs.allocate(address); |
| set_tbe(TBEs.lookup(address)); |
| tbe.writeMask.clear(); |
| tbe.wtData := false; |
| tbe.atomicData := false; |
| tbe.DataBlk := getDirectoryEntry(address).DataBlk; // Data only for WBs |
| tbe.Dirty := false; |
| tbe.NumPendingAcks := 0; |
| } |
| |
| action(t_allocateTBE, "t", desc="allocate TBE Entry") { |
| check_allocate(TBEs); |
| peek(requestNetwork_in, CPURequestMsg) { |
| TBEs.allocate(address); |
| set_tbe(TBEs.lookup(address)); |
| if (in_msg.Type == CoherenceRequestType:WriteThrough) { |
| tbe.writeMask.clear(); |
| tbe.writeMask.orMask(in_msg.writeMask); |
| tbe.wtData := true; |
| tbe.WTRequestor := in_msg.WTRequestor; |
| tbe.LastSender := in_msg.Requestor; |
| } |
| if (in_msg.Type == CoherenceRequestType:Atomic) { |
| tbe.writeMask.clear(); |
| tbe.writeMask.orMask(in_msg.writeMask); |
| tbe.atomicData := true; |
| tbe.WTRequestor := in_msg.WTRequestor; |
| tbe.LastSender := in_msg.Requestor; |
| } |
| tbe.DataBlk := getDirectoryEntry(address).DataBlk; // Data only for WBs |
| tbe.Dirty := false; |
| if (in_msg.Type == CoherenceRequestType:WriteThrough) { |
| tbe.DataBlk.copyPartial(in_msg.DataBlk,tbe.writeMask); |
| tbe.Dirty := false; |
| } |
| tbe.OriginalRequestor := in_msg.Requestor; |
| tbe.NumPendingAcks := 0; |
| tbe.Cached := in_msg.ForceShared; |
| tbe.InitialRequestTime := in_msg.InitialRequestTime; |
| } |
| } |
| |
| action(dt_deallocateTBE, "dt", desc="deallocate TBE Entry") { |
| if (tbe.Dirty == false) { |
| getDirectoryEntry(address).DataBlk := tbe.DataBlk; |
| } |
| TBEs.deallocate(address); |
| unset_tbe(); |
| } |
| |
| action(wd_writeBackData, "wd", desc="Write back data if needed") { |
| if (tbe.wtData) { |
| DataBlock tmp := getDirectoryEntry(address).DataBlk; |
| tmp.copyPartial(tbe.DataBlk,tbe.writeMask); |
| tbe.DataBlk := tmp; |
| getDirectoryEntry(address).DataBlk := tbe.DataBlk; |
| } else if (tbe.atomicData) { |
| tbe.DataBlk.atomicPartial(getDirectoryEntry(address).DataBlk, |
| tbe.writeMask); |
| getDirectoryEntry(address).DataBlk := tbe.DataBlk; |
| } else if (tbe.Dirty == false) { |
| getDirectoryEntry(address).DataBlk := tbe.DataBlk; |
| } |
| } |
| |
| action(mt_writeMemDataToTBE, "mt", desc="write Mem data to TBE") { |
| peek(memQueue_in, MemoryMsg) { |
| if (tbe.wtData == true) { |
| // DO Nothing (already have the directory data) |
| } else if (tbe.Dirty == false) { |
| tbe.DataBlk := getDirectoryEntry(address).DataBlk; |
| } |
| tbe.MemData := true; |
| } |
| } |
| |
| action(y_writeProbeDataToTBE, "y", desc="write Probe Data to TBE") { |
| peek(responseNetwork_in, ResponseMsg) { |
| if (in_msg.Dirty) { |
| DPRINTF(RubySlicc, "Got dirty data for %s from %s\n", address, in_msg.Sender); |
| DPRINTF(RubySlicc, "Data is %s\n", in_msg.DataBlk); |
| if (tbe.wtData) { |
| DataBlock tmp := in_msg.DataBlk; |
| tmp.copyPartial(tbe.DataBlk,tbe.writeMask); |
| tbe.DataBlk := tmp; |
| } else if (tbe.Dirty) { |
| if(tbe.atomicData == false && tbe.wtData == false) { |
| DPRINTF(RubySlicc, "Got double data for %s from %s\n", address, in_msg.Sender); |
| assert(tbe.DataBlk == in_msg.DataBlk); // in case of double data |
| } |
| } else { |
| tbe.DataBlk := in_msg.DataBlk; |
| tbe.Dirty := in_msg.Dirty; |
| tbe.LastSender := in_msg.Sender; |
| } |
| } |
| if (in_msg.Hit) { |
| tbe.Cached := true; |
| } |
| } |
| } |
| |
| action(mwc_markSinkWriteCancel, "mwc", desc="Mark to sink impending VicDirty") { |
| peek(responseNetwork_in, ResponseMsg) { |
| DPRINTF(RubySlicc, "Write cancel bit set on address %s\n", address); |
| getDirectoryEntry(address).VicDirtyIgnore.add(in_msg.Sender); |
| APPEND_TRANSITION_COMMENT(" setting bit to sink VicDirty "); |
| } |
| } |
| |
| action(x_decrementAcks, "x", desc="decrement Acks pending") { |
| tbe.NumPendingAcks := tbe.NumPendingAcks - 1; |
| APPEND_TRANSITION_COMMENT(" Acks remaining: "); |
| APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks); |
| } |
| |
| action(o_checkForCompletion, "o", desc="check for ack completion") { |
| if (tbe.NumPendingAcks == 0) { |
| enqueue(triggerQueue_out, TriggerMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := TriggerType:AcksComplete; |
| } |
| } |
| APPEND_TRANSITION_COMMENT(" Check: Acks remaining: "); |
| APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks); |
| } |
| |
| action(rv_removeVicDirtyIgnore, "rv", desc="Remove ignored core") { |
| peek(requestNetwork_in, CPURequestMsg) { |
| getDirectoryEntry(address).VicDirtyIgnore.remove(in_msg.Requestor); |
| } |
| } |
| |
| action(al_allocateL3Block, "al", desc="allocate the L3 block on WB") { |
| peek(responseNetwork_in, ResponseMsg) { |
| if (L3CacheMemory.isTagPresent(address)) { |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(address)); |
| APPEND_TRANSITION_COMMENT(" al wrote data to L3 (hit) "); |
| entry.DataBlk := in_msg.DataBlk; |
| entry.LastSender := in_msg.Sender; |
| } else { |
| if (L3CacheMemory.cacheAvail(address) == false) { |
| Addr victim := L3CacheMemory.cacheProbe(address); |
| CacheEntry victim_entry := static_cast(CacheEntry, "pointer", |
| L3CacheMemory.lookup(victim)); |
| queueMemoryWrite(machineID, victim, to_memory_controller_latency, |
| victim_entry.DataBlk); |
| L3CacheMemory.deallocate(victim); |
| } |
| assert(L3CacheMemory.cacheAvail(address)); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.allocate(address, new CacheEntry)); |
| APPEND_TRANSITION_COMMENT(" al wrote data to L3 "); |
| entry.DataBlk := in_msg.DataBlk; |
| |
| entry.LastSender := in_msg.Sender; |
| } |
| } |
| } |
| |
| action(alwt_allocateL3BlockOnWT, "alwt", desc="allocate the L3 block on WT") { |
| if ((tbe.wtData || tbe.atomicData) && useL3OnWT) { |
| if (L3CacheMemory.isTagPresent(address)) { |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(address)); |
| APPEND_TRANSITION_COMMENT(" al wrote data to L3 (hit) "); |
| entry.DataBlk := tbe.DataBlk; |
| entry.LastSender := tbe.LastSender; |
| } else { |
| if (L3CacheMemory.cacheAvail(address) == false) { |
| Addr victim := L3CacheMemory.cacheProbe(address); |
| CacheEntry victim_entry := static_cast(CacheEntry, "pointer", |
| L3CacheMemory.lookup(victim)); |
| queueMemoryWrite(machineID, victim, to_memory_controller_latency, |
| victim_entry.DataBlk); |
| L3CacheMemory.deallocate(victim); |
| } |
| assert(L3CacheMemory.cacheAvail(address)); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.allocate(address, new CacheEntry)); |
| APPEND_TRANSITION_COMMENT(" al wrote data to L3 "); |
| entry.DataBlk := tbe.DataBlk; |
| entry.LastSender := tbe.LastSender; |
| } |
| } |
| } |
| |
| action(apf_allocateProbeFilterEntry, "apf", desc="Allocate probe filte entry") { |
| if (!ProbeFilterMemory.isTagPresent(address)) { |
| if (inclusiveDir) { |
| assert(ProbeFilterMemory.cacheAvail(address)); |
| } else if (ProbeFilterMemory.cacheAvail(address) == false) { |
| Addr victim := ProbeFilterMemory.cacheProbe(address); |
| ProbeFilterMemory.deallocate(victim); |
| } |
| assert(ProbeFilterMemory.cacheAvail(address)); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", ProbeFilterMemory.allocate(address, new CacheEntry)); |
| APPEND_TRANSITION_COMMENT(" allocating a new probe filter entry"); |
| entry.pfState := ProbeFilterState:NT; |
| if (inclusiveDir) { |
| entry.pfState := ProbeFilterState:T; |
| } |
| entry.isOnCPU := false; |
| entry.isOnGPU := false; |
| } |
| } |
| |
| action(mpfe_markPFEntryForEviction, "mpfe", desc="Mark this PF entry is being evicted") { |
| assert(ProbeFilterMemory.isTagPresent(address)); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", ProbeFilterMemory.lookup(address)); |
| entry.pfState := ProbeFilterState:B; |
| peek(requestNetwork_in, CPURequestMsg) { |
| tbe.demandAddress := in_msg.addr; |
| } |
| } |
| |
| action(we_wakeUpEvictionDependents, "we", desc="Wake up requests waiting for demand address and victim address") { |
| wakeUpBuffers(address); |
| wakeUpBuffers(tbe.demandAddress); |
| } |
| |
| action(dpf_deallocateProbeFilter, "dpf", desc="deallocate PF entry") { |
| assert(ProbeFilterMemory.isTagPresent(address)); |
| ProbeFilterMemory.deallocate(address); |
| } |
| |
| action(upf_updateProbeFilter, "upf", desc="") { |
| peek(requestNetwork_in, CPURequestMsg) { |
| assert(ProbeFilterMemory.isTagPresent(address)); |
| CacheEntry entry := static_cast(CacheEntry, "pointer", ProbeFilterMemory.lookup(address)); |
| if (in_msg.Type == CoherenceRequestType:WriteThrough) { |
| entry.pfState := ProbeFilterState:T; |
| entry.isOnCPU := false; |
| entry.isOnGPU := false; |
| } else if (in_msg.Type == CoherenceRequestType:Atomic) { |
| entry.pfState := ProbeFilterState:T; |
| entry.isOnCPU := false; |
| entry.isOnGPU := false; |
| } else if (in_msg.Type == CoherenceRequestType:RdBlkM) { |
| entry.pfState := ProbeFilterState:T; |
| entry.isOnCPU := false; |
| entry.isOnGPU := false; |
| } else if (in_msg.Type == CoherenceRequestType:CtoD) { |
| entry.pfState := ProbeFilterState:T; |
| entry.isOnCPU := false; |
| entry.isOnGPU := false; |
| } |
| if(machineIDToMachineType(in_msg.Requestor) == MachineType:CorePair) { |
| entry.isOnCPU := true; |
| } else { |
| entry.isOnGPU := true; |
| } |
| } |
| } |
| |
| action(rmcd_removeSharerConditional, "rmcd", desc="remove sharer from probe Filter, conditional") { |
| peek(requestNetwork_in, CPURequestMsg) { |
| if (ProbeFilterMemory.isTagPresent(address)) { |
| CacheEntry entry := static_cast(CacheEntry, "pointer", ProbeFilterMemory.lookup(address)); |
| if(machineIDToMachineType(in_msg.Requestor) == MachineType:CorePair) {//CorePair has inclusive L2 |
| if (in_msg.Type == CoherenceRequestType:VicDirty) { |
| entry.isOnCPU := false; |
| } else if (in_msg.Type == CoherenceRequestType:VicClean) { |
| entry.isOnCPU := false; |
| } |
| } |
| } |
| } |
| } |
| |
| action(sf_setForwardReqTime, "sf", desc="...") { |
| tbe.ForwardRequestTime := curCycle(); |
| } |
| |
| action(dl_deallocateL3, "dl", desc="deallocate the L3 block") { |
| L3CacheMemory.deallocate(address); |
| } |
| |
| action(p_popRequestQueue, "p", desc="pop request queue") { |
| requestNetwork_in.dequeue(clockEdge()); |
| } |
| |
| action(pr_popResponseQueue, "pr", desc="pop response queue") { |
| responseNetwork_in.dequeue(clockEdge()); |
| } |
| |
| action(pm_popMemQueue, "pm", desc="pop mem queue") { |
| memQueue_in.dequeue(clockEdge()); |
| } |
| |
| action(pt_popTriggerQueue, "pt", desc="pop trigger queue") { |
| triggerQueue_in.dequeue(clockEdge()); |
| } |
| |
| action(ptl_popTriggerQueue, "ptl", desc="pop L3 trigger queue") { |
| L3TriggerQueue_in.dequeue(clockEdge()); |
| } |
| |
| action(pu_popUnblockQueue, "pu", desc="pop unblock queue") { |
| unblockNetwork_in.dequeue(clockEdge()); |
| } |
| |
| action(zz_recycleRequestQueue, "zz", desc="recycle request queue") { |
| requestNetwork_in.recycle(clockEdge(), cyclesToTicks(recycle_latency)); |
| } |
| |
| action(yy_recycleResponseQueue, "yy", desc="recycle response queue") { |
| responseNetwork_in.recycle(clockEdge(), cyclesToTicks(recycle_latency)); |
| } |
| |
| action(st_stallAndWaitRequest, "st", desc="Stall and wait on the address") { |
| stall_and_wait(requestNetwork_in, address); |
| } |
| |
| action(wa_wakeUpDependents, "wa", desc="Wake up any requests waiting for this address") { |
| wakeUpBuffers(address); |
| } |
| |
| action(wa_wakeUpAllDependents, "waa", desc="Wake up any requests waiting for this region") { |
| wakeUpAllBuffers(); |
| } |
| |
| action(z_stall, "z", desc="...") { |
| } |
| |
| // TRANSITIONS |
| transition({BL, BS_M, BM_M, B_M, BP, BS_PM, BM_PM, B_PM, BS_Pm, BM_Pm, B_Pm, B_P, B}, {RdBlkS, RdBlkM, RdBlk, CtoD}) { |
| st_stallAndWaitRequest; |
| } |
| |
| // It may be possible to save multiple invalidations here! |
| transition({BL, BS_M, BM_M, B_M, BP, BS_PM, BM_PM, B_PM, BS_Pm, BM_Pm, B_Pm, B_P, B}, {Atomic, WriteThrough}) { |
| st_stallAndWaitRequest; |
| } |
| |
| |
| // transitions from U |
| transition(U, PF_Repl, B_P) {PFTagArrayRead, PFTagArrayWrite}{ |
| te_allocateTBEForEviction; |
| apf_allocateProbeFilterEntry; |
| bp_backProbe; |
| sm_setMRU; |
| mpfe_markPFEntryForEviction; |
| } |
| |
| transition(U, {RdBlkS}, BS_PM) {L3TagArrayRead, PFTagArrayRead, PFTagArrayWrite} { |
| t_allocateTBE; |
| apf_allocateProbeFilterEntry; |
| l_queueMemRdReq; |
| sc_probeShrCoreData; |
| sm_setMRU; |
| upf_updateProbeFilter; |
| p_popRequestQueue; |
| } |
| |
| transition(U, WriteThrough, BM_PM) {L3TagArrayRead, L3TagArrayWrite, PFTagArrayRead, PFTagArrayWrite} { |
| t_allocateTBE; |
| apf_allocateProbeFilterEntry; |
| w_sendResponseWBAck; |
| l_queueMemRdReq; |
| dc_probeInvCoreData; |
| sm_setMRU; |
| upf_updateProbeFilter; |
| p_popRequestQueue; |
| } |
| |
| transition(U, Atomic, BM_PM) {L3TagArrayRead, L3TagArrayWrite, PFTagArrayRead, PFTagArrayWrite} { |
| t_allocateTBE; |
| apf_allocateProbeFilterEntry; |
| l_queueMemRdReq; |
| dc_probeInvCoreData; |
| sm_setMRU; |
| upf_updateProbeFilter; |
| p_popRequestQueue; |
| } |
| |
| transition(U, {RdBlkM}, BM_PM) {L3TagArrayRead, PFTagArrayRead, PFTagArrayWrite} { |
| t_allocateTBE; |
| apf_allocateProbeFilterEntry; |
| l_queueMemRdReq; |
| dc_probeInvCoreData; |
| sm_setMRU; |
| upf_updateProbeFilter; |
| p_popRequestQueue; |
| } |
| |
| transition(U, RdBlk, B_PM) {L3TagArrayRead, PFTagArrayRead, PFTagArrayWrite}{ |
| t_allocateTBE; |
| apf_allocateProbeFilterEntry; |
| l_queueMemRdReq; |
| sc_probeShrCoreData; |
| sm_setMRU; |
| upf_updateProbeFilter; |
| p_popRequestQueue; |
| } |
| |
| transition(U, CtoD, BP) {L3TagArrayRead, PFTagArrayRead, PFTagArrayWrite} { |
| t_allocateTBE; |
| apf_allocateProbeFilterEntry; |
| ic_probeInvCore; |
| sm_setMRU; |
| upf_updateProbeFilter; |
| p_popRequestQueue; |
| } |
| |
| transition(U, VicDirty, BL) {L3TagArrayRead} { |
| t_allocateTBE; |
| w_sendResponseWBAck; |
| rmcd_removeSharerConditional; |
| p_popRequestQueue; |
| } |
| |
| transition(U, VicClean, BL) {L3TagArrayRead} { |
| t_allocateTBE; |
| w_sendResponseWBAck; |
| rmcd_removeSharerConditional; |
| p_popRequestQueue; |
| } |
| |
| transition(BL, {VicDirty, VicClean}) { |
| zz_recycleRequestQueue; |
| } |
| |
| transition(BL, CPUData, U) {L3TagArrayWrite, L3DataArrayWrite} { |
| d_writeDataToMemory; |
| al_allocateL3Block; |
| wa_wakeUpDependents; |
| dt_deallocateTBE; |
| //l_queueMemWBReq; // why need an ack? esp. with DRAMSim, just put it in queue no ack needed |
| pr_popResponseQueue; |
| } |
| |
| transition(BL, StaleWB, U) {L3TagArrayWrite} { |
| dt_deallocateTBE; |
| wa_wakeUpAllDependents; |
| pr_popResponseQueue; |
| } |
| |
| transition({B, BS_M, BM_M, B_M, BP, BS_PM, BM_PM, B_PM, BS_Pm, BM_Pm, B_Pm, B_P}, {VicDirty, VicClean}) { |
| z_stall; |
| } |
| |
| transition({U, BL, BS_M, BM_M, B_M, BP, BS_PM, BM_PM, B_PM, BS_Pm, BM_Pm, B_Pm, B_P, B}, WBAck) { |
| pm_popMemQueue; |
| } |
| |
| transition({BL, BS_M, BM_M, B_M, BP, BS_PM, BM_PM, B_PM, BS_Pm, BM_Pm, B_Pm, B_P, B}, PF_Repl) { |
| zz_recycleRequestQueue; |
| } |
| |
| transition({U, BL, BS_M, BM_M, B_M, BP, BS_PM, BM_PM, B_PM, BS_Pm, BM_Pm, B_Pm, B_P, B}, StaleVicDirty) { |
| rv_removeVicDirtyIgnore; |
| w_sendResponseWBAck; |
| p_popRequestQueue; |
| } |
| |
| transition({B}, CoreUnblock, U) { |
| wa_wakeUpDependents; |
| pu_popUnblockQueue; |
| } |
| |
| transition(B, UnblockWriteThrough, U) { |
| wa_wakeUpDependents; |
| pt_popTriggerQueue; |
| } |
| |
| transition(BS_PM, MemData, BS_Pm) {} { |
| mt_writeMemDataToTBE; |
| pm_popMemQueue; |
| } |
| |
| transition(BM_PM, MemData, BM_Pm){} { |
| mt_writeMemDataToTBE; |
| pm_popMemQueue; |
| } |
| |
| transition(B_PM, MemData, B_Pm){} { |
| mt_writeMemDataToTBE; |
| pm_popMemQueue; |
| } |
| |
| transition(BS_PM, L3Hit, BS_Pm) {} { |
| ptl_popTriggerQueue; |
| } |
| |
| transition(BM_PM, L3Hit, BM_Pm) {} { |
| ptl_popTriggerQueue; |
| } |
| |
| transition(B_PM, L3Hit, B_Pm) {} { |
| ptl_popTriggerQueue; |
| } |
| |
| transition(BS_M, MemData, B){L3TagArrayWrite, L3DataArrayWrite} { |
| mt_writeMemDataToTBE; |
| s_sendResponseS; |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| dt_deallocateTBE; |
| pm_popMemQueue; |
| } |
| |
| transition(BM_M, MemData, B){L3TagArrayWrite, L3DataArrayWrite} { |
| mt_writeMemDataToTBE; |
| m_sendResponseM; |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| dt_deallocateTBE; |
| pm_popMemQueue; |
| } |
| |
| transition(B_M, MemData, B){L3TagArrayWrite, L3DataArrayWrite} { |
| mt_writeMemDataToTBE; |
| es_sendResponseES; |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| dt_deallocateTBE; |
| pm_popMemQueue; |
| } |
| |
| transition(BS_M, L3Hit, B) {L3TagArrayWrite, L3DataArrayWrite} { |
| s_sendResponseS; |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| dt_deallocateTBE; |
| ptl_popTriggerQueue; |
| } |
| |
| transition(BM_M, L3Hit, B) {L3DataArrayWrite, L3TagArrayWrite} { |
| m_sendResponseM; |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| dt_deallocateTBE; |
| ptl_popTriggerQueue; |
| } |
| |
| transition(B_M, L3Hit, B) {L3DataArrayWrite, L3TagArrayWrite} { |
| es_sendResponseES; |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| dt_deallocateTBE; |
| ptl_popTriggerQueue; |
| } |
| |
| transition({BS_PM, BM_PM, B_PM, BS_Pm, BM_Pm, B_Pm, B_P, BP}, CPUPrbResp) { |
| y_writeProbeDataToTBE; |
| x_decrementAcks; |
| o_checkForCompletion; |
| pr_popResponseQueue; |
| } |
| |
| transition(BS_PM, ProbeAcksComplete, BS_M) {} { |
| sf_setForwardReqTime; |
| pt_popTriggerQueue; |
| } |
| |
| transition(BM_PM, ProbeAcksComplete, BM_M) {} { |
| sf_setForwardReqTime; |
| pt_popTriggerQueue; |
| } |
| |
| transition(B_PM, ProbeAcksComplete, B_M){} { |
| sf_setForwardReqTime; |
| pt_popTriggerQueue; |
| } |
| |
| transition(BS_Pm, ProbeAcksComplete, B){L3DataArrayWrite, L3TagArrayWrite} { |
| sf_setForwardReqTime; |
| s_sendResponseS; |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| dt_deallocateTBE; |
| pt_popTriggerQueue; |
| } |
| |
| transition(BM_Pm, ProbeAcksComplete, B){L3DataArrayWrite, L3TagArrayWrite} { |
| sf_setForwardReqTime; |
| m_sendResponseM; |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| dt_deallocateTBE; |
| pt_popTriggerQueue; |
| } |
| |
| transition(B_Pm, ProbeAcksComplete, B){L3DataArrayWrite, L3TagArrayWrite} { |
| sf_setForwardReqTime; |
| es_sendResponseES; |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| dt_deallocateTBE; |
| pt_popTriggerQueue; |
| } |
| |
| transition(B_P, ProbeAcksComplete, U) { |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| we_wakeUpEvictionDependents; |
| dpf_deallocateProbeFilter; |
| dt_deallocateTBE; |
| pt_popTriggerQueue; |
| } |
| |
| transition(BP, ProbeAcksComplete, B){L3TagArrayWrite, L3TagArrayWrite} { |
| sf_setForwardReqTime; |
| c_sendResponseCtoD; |
| wd_writeBackData; |
| alwt_allocateL3BlockOnWT; |
| dt_deallocateTBE; |
| pt_popTriggerQueue; |
| } |
| } |