| /* |
| * Copyright (c) 2021-2022 ARM Limited |
| * All rights reserved |
| * |
| * The license below extends only to copyright in the software and shall |
| * not be construed as granting a license to any other intellectual |
| * property including but not limited to intellectual property relating |
| * to a hardware implementation of the functionality of the software |
| * licensed hereunder. You may use the software subject to the license |
| * terms below provided that you ensure that this notice is replicated |
| * unmodified and in its entirety in all distributions of the software, |
| * modified or unmodified, in source code or in binary form. |
| * |
| * 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. |
| */ |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // CHI-cache actions definitions |
| //////////////////////////////////////////////////////////////////////////// |
| |
| action(AllocateTBE_Request, desc="") { |
| if (storTBEs.areNSlotsAvailable(1)) { |
| // reserve a slot for this request |
| storTBEs.incrementReserved(); |
| |
| // Move request to rdy queue |
| peek(reqInPort, CHIRequestMsg) { |
| enqueue(reqRdyOutPort, CHIRequestMsg, allocation_latency) { |
| assert(in_msg.addr == address); |
| assert(in_msg.is_local_pf == false); |
| out_msg := in_msg; |
| } |
| } |
| |
| } else { |
| // we don't have resources to track this request; enqueue a retry |
| peek(reqInPort, CHIRequestMsg) { |
| assert(in_msg.allowRetry); |
| enqueue(retryTriggerOutPort, RetryTriggerMsg, 0) { |
| out_msg.addr := in_msg.addr; |
| out_msg.usesTxnId := false; |
| out_msg.event := Event:SendRetryAck; |
| out_msg.retryDest := in_msg.requestor; |
| retryQueue.emplace(in_msg.addr,false,in_msg.requestor); |
| } |
| } |
| } |
| |
| reqInPort.dequeue(clockEdge()); |
| } |
| |
| action(AllocateTBE_Request_WithCredit, desc="") { |
| // TBE slot already reserved |
| // Move request to rdy queue |
| peek(reqInPort, CHIRequestMsg) { |
| assert(in_msg.allowRetry == false); |
| enqueue(reqRdyOutPort, CHIRequestMsg, allocation_latency) { |
| assert(in_msg.addr == address); |
| out_msg := in_msg; |
| } |
| } |
| reqInPort.dequeue(clockEdge()); |
| } |
| |
| action(AllocateTBE_Snoop, desc="") { |
| // No retry for snoop requests; just create resource stall |
| check_allocate(storSnpTBEs); |
| |
| storSnpTBEs.incrementReserved(); |
| |
| // Move request to rdy queue |
| peek(snpInPort, CHIRequestMsg) { |
| enqueue(snpRdyOutPort, CHIRequestMsg, allocation_latency) { |
| assert(in_msg.addr == address); |
| out_msg := in_msg; |
| } |
| |
| // also remove snoop source from waiting retry destinations to prevent |
| // deadlocks in which this snoop is blocked by a transaction that needs to |
| // send a request to the snoop destination before going to BUSY_INTR, |
| // but the destination needs the snoop to complete before sending retry |
| // credit |
| destsWaitingRetry.remove(in_msg.requestor); |
| } |
| snpInPort.dequeue(clockEdge()); |
| } |
| |
| action(AllocateTBE_DvmSnoop, desc="") { |
| // No retry for snoop requests; just create resource stall |
| check_allocate(storDvmSnpTBEs); |
| |
| storDvmSnpTBEs.incrementReserved(); |
| |
| // Move request to rdy queue |
| peek(snpInPort, CHIRequestMsg) { |
| enqueue(snpRdyOutPort, CHIRequestMsg, allocation_latency) { |
| assert(in_msg.usesTxnId); |
| assert(in_msg.addr == address); |
| out_msg := in_msg; |
| } |
| } |
| snpInPort.dequeue(clockEdge()); |
| } |
| |
| action(AllocateTBE_SeqRequest, desc="") { |
| // No retry for sequencer requests; just create resource stall |
| check_allocate(storTBEs); |
| |
| // reserve a slot for this request |
| storTBEs.incrementReserved(); |
| |
| // Move request to rdy queue |
| peek(seqInPort, RubyRequest) { |
| enqueue(reqRdyOutPort, CHIRequestMsg, allocation_latency) { |
| out_msg.addr := in_msg.LineAddress; |
| assert((in_msg.Size > 0) && (in_msg.Size <= blockSize)); |
| out_msg.accAddr := in_msg.PhysicalAddress; |
| out_msg.accSize := in_msg.Size; |
| out_msg.requestor := machineID; |
| out_msg.fwdRequestor := machineID; |
| out_msg.seqReq := in_msg.getRequestPtr(); |
| out_msg.isSeqReqValid := true; |
| assert(in_msg.Prefetch == PrefetchBit:No); |
| out_msg.is_local_pf := false; |
| out_msg.is_remote_pf := false; |
| |
| if ((in_msg.Type == RubyRequestType:LD) || |
| (in_msg.Type == RubyRequestType:IFETCH)) { |
| out_msg.type := CHIRequestType:Load; |
| } else if (in_msg.Type == RubyRequestType:ST) { |
| if (in_msg.Size == blockSize) { |
| out_msg.type := CHIRequestType:StoreLine; |
| } else { |
| out_msg.type := CHIRequestType:Store; |
| } |
| } else { |
| error("Invalid RubyRequestType"); |
| } |
| } |
| } |
| seqInPort.dequeue(clockEdge()); |
| } |
| |
| action(AllocateTBE_SeqDvmRequest, desc="") { |
| // No retry for sequencer requests; just create resource stall |
| check_allocate(storDvmTBEs); |
| |
| // reserve a slot for this request |
| storDvmTBEs.incrementReserved(); |
| |
| // Move request to rdy queue |
| peek(seqInPort, RubyRequest) { |
| enqueue(reqRdyOutPort, CHIRequestMsg, allocation_latency) { |
| // DVM operations do not relate to memory addresses |
| // Use the DVM transaction ID instead |
| out_msg.usesTxnId := true; |
| out_msg.txnId := in_msg.tlbiTransactionUid; |
| |
| // TODO - zero these out? |
| out_msg.addr := in_msg.tlbiTransactionUid; |
| out_msg.accAddr := in_msg.tlbiTransactionUid; |
| out_msg.accSize := blockSize; |
| assert(in_msg.Prefetch == PrefetchBit:No); |
| out_msg.is_local_pf := false; |
| out_msg.is_remote_pf := false; |
| |
| out_msg.requestor := machineID; |
| out_msg.fwdRequestor := machineID; |
| out_msg.seqReq := in_msg.getRequestPtr(); |
| out_msg.isSeqReqValid := true; |
| |
| |
| if (in_msg.Type == RubyRequestType:TLBI) { |
| out_msg.type := CHIRequestType:DvmTlbi_Initiate; |
| } else if (in_msg.Type == RubyRequestType:TLBI_SYNC) { |
| out_msg.type := CHIRequestType:DvmSync_Initiate; |
| } else if (in_msg.Type == RubyRequestType:TLBI_EXT_SYNC_COMP) { |
| out_msg.type := CHIRequestType:DvmSync_ExternCompleted; |
| } else { |
| error("Invalid RubyRequestType"); |
| } |
| } |
| } |
| seqInPort.dequeue(clockEdge()); |
| } |
| |
| action(AllocateTBE_PfRequest, desc="Allocate TBE for prefetch request") { |
| // No retry for prefetch requests; just create resource stall |
| check_allocate(storTBEs); |
| |
| // reserve a slot for this request |
| storTBEs.incrementReserved(); |
| |
| // Move request to rdy queue |
| peek(pfInPort, RubyRequest) { |
| enqueue(reqRdyOutPort, CHIRequestMsg, 0) { |
| out_msg.addr := in_msg.LineAddress; |
| assert((in_msg.Size > 0) && (in_msg.Size <= blockSize)); |
| out_msg.accAddr := in_msg.PhysicalAddress; |
| out_msg.accSize := in_msg.Size; |
| out_msg.requestor := machineID; |
| out_msg.fwdRequestor := machineID; |
| out_msg.seqReq := in_msg.getRequestPtr(); |
| out_msg.isSeqReqValid := true; |
| assert(in_msg.Prefetch != PrefetchBit:No); |
| out_msg.is_local_pf := true; |
| out_msg.is_remote_pf := false; |
| |
| if (in_msg.Type == RubyRequestType:LD) { |
| out_msg.type := CHIRequestType:Load; |
| } else if (in_msg.Type == RubyRequestType:ST) { |
| error("CHI is not supporting prefetch store requests"); |
| } else { |
| error("Invalid RubyRequestType"); |
| } |
| } |
| } |
| pfInPort.dequeue(clockEdge()); |
| } |
| |
| action(Initiate_Request, desc="") { |
| State initial := getState(tbe, cache_entry, address); |
| bool was_retried := false; |
| peek(reqRdyPort, CHIRequestMsg) { |
| set_tbe(allocateRequestTBE(address, in_msg)); |
| // only a msg that was already retried doesn't allow a retry |
| was_retried := in_msg.allowRetry == false; |
| } |
| DirEntry dir_entry := getDirEntry(address); |
| copyCacheAndDir(cache_entry, dir_entry, tbe, initial); |
| |
| tbe.use_DMT := is_HN && enable_DMT; |
| tbe.use_DCT := enable_DCT; |
| |
| bool alloc_entry := needCacheEntry(tbe.reqType, |
| cache_entry, dir_entry, |
| tbe.is_local_pf); |
| bool dealloc_entry := needDeallocCacheEntry(tbe.reqType); |
| assert((alloc_entry && dealloc_entry) == false); |
| |
| // always drops any data when not caching it or when this transaction |
| // requires deallocation |
| tbe.dataToBeInvalid := dealloc_entry || |
| (is_invalid(cache_entry) && (alloc_entry == false)); |
| tbe.doCacheFill := alloc_entry || is_valid(cache_entry); |
| |
| // model the initial tag array read |
| tbe.actions.pushNB(Event:TagArrayRead); |
| |
| incomingTransactionStart(address, curTransitionEvent(), initial, was_retried); |
| } |
| |
| action(Initiate_Request_DVM, desc="") { |
| peek(reqRdyPort, CHIRequestMsg) { |
| // "address" for DVM = transaction ID |
| TBE tbe := allocateDvmRequestTBE(address, in_msg); |
| set_tbe(tbe); |
| } |
| } |
| |
| action(Initiate_Request_Stale, desc="") { |
| State initial := getState(tbe, cache_entry, address); |
| bool was_retried := false; |
| peek(reqRdyPort, CHIRequestMsg) { |
| set_tbe(allocateRequestTBE(address, in_msg)); |
| was_retried := in_msg.allowRetry == false; |
| } |
| copyCacheAndDir(cache_entry, getDirEntry(address), tbe, initial); |
| |
| // usually we consider data locally invalid on RU states even if we |
| // have a copy; so it override it to valid so we can comeback to UD_RU/UC_RU |
| // at the end of this transaction |
| if (tbe.dir_ownerExists && tbe.dir_ownerIsExcl && is_valid(cache_entry)) { |
| // treat the data we got from the cache as valid |
| tbe.dataBlk := cache_entry.DataBlk; |
| tbe.dataBlkValid.fillMask(); |
| tbe.dataValid := true; |
| } |
| |
| incomingTransactionStart(address, curTransitionEvent(), initial, was_retried); |
| } |
| |
| action(Initiate_Snoop, desc="") { |
| State initial := getState(tbe, cache_entry, address); |
| peek(snpRdyPort, CHIRequestMsg) { |
| set_tbe(allocateSnoopTBE(address, in_msg)); |
| } |
| copyCacheAndDir(cache_entry, getDirEntry(address), tbe, initial); |
| |
| // if we end up with valid data drop it if no entry allocated |
| tbe.dataToBeInvalid := is_invalid(cache_entry); |
| |
| // model the initial tag array read |
| tbe.actions.pushNB(Event:TagArrayRead); |
| |
| incomingTransactionStart(address, curTransitionEvent(), initial, false); |
| } |
| |
| action(Initiate_Snoop_Hazard, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.is_req_tbe || tbe.is_repl_tbe); |
| |
| // Switch to the new snoop TBE |
| TBE prev_tbe := tbe; |
| peek(snpRdyPort, CHIRequestMsg) { |
| set_tbe(allocateSnoopTBE(address, in_msg)); |
| } |
| assert(tbe.is_snp_tbe); |
| if (prev_tbe.is_req_tbe) { |
| assert(prev_tbe.is_repl_tbe == false); |
| tbe.is_req_hazard := true; |
| } else { |
| assert(prev_tbe.is_repl_tbe); |
| tbe.is_repl_hazard := true; |
| } |
| |
| // Use state from prev TBE |
| tbe.pendReqType := prev_tbe.pendReqType; |
| copyCacheAndDirTBEs(prev_tbe, tbe); |
| tbe.wakeup_pending_req := prev_tbe.wakeup_pending_req; |
| tbe.wakeup_pending_snp := prev_tbe.wakeup_pending_snp; |
| tbe.wakeup_pending_tgr := prev_tbe.wakeup_pending_tgr; |
| } |
| |
| action(RestoreFromHazard, desc="") { |
| TBE hazard_tbe := getHazardTBE(tbe); |
| |
| // update |
| setDataToBeStates(tbe); |
| |
| copyCacheAndDirTBEs(tbe, hazard_tbe); |
| hazard_tbe.wakeup_pending_req := tbe.wakeup_pending_req; |
| hazard_tbe.wakeup_pending_snp := tbe.wakeup_pending_snp; |
| hazard_tbe.wakeup_pending_tgr := tbe.wakeup_pending_tgr; |
| |
| deallocateSnpTBE(tbe); |
| set_tbe(hazard_tbe); |
| |
| // if the pending request is a WB or Evict then it becomes a stale request |
| // if data is no longer in the expected state |
| if (tbe.pendReqType == CHIRequestType:WriteBackFull) { |
| tbe.is_stale := (tbe.dataValid && tbe.dataDirty) == false; |
| } else if (tbe.pendReqType == CHIRequestType:WriteCleanFull) { |
| tbe.is_stale := (tbe.dataValid && tbe.dataDirty) == false; |
| } else if (hazard_tbe.pendReqType == CHIRequestType:WriteEvictFull) { |
| tbe.is_stale := (tbe.dataValid && tbe.dataUnique) == false; |
| } else if (hazard_tbe.pendReqType == CHIRequestType:Evict) { |
| tbe.is_stale := tbe.dataValid == false; |
| } else if (hazard_tbe.pendReqType == CHIRequestType:CleanUnique) { |
| tbe.is_stale := tbe.dataValid == false; |
| } |
| |
| // a pending action from the original request may have been stalled during |
| // the hazard and needs to wakeup up now |
| wakeupPendingTgrs(tbe); |
| } |
| |
| action(Initiate_Replacement, desc="") { |
| assert(is_invalid(tbe)); |
| State initial := getState(tbe, cache_entry, address); |
| if (unify_repl_TBEs) { |
| peek(replTriggerInPort, ReplacementMsg) { |
| set_tbe(allocateReplacementTBEOnSlot(address, in_msg.slot)); |
| DPRINTF(RubySlicc, "Allocated replacement TBE on slot %d\n", tbe.storSlot); |
| } |
| } else { |
| set_tbe(allocateReplacementTBE(address)); |
| DPRINTF(RubySlicc, "Allocated replacement TBE on new slot %d\n", tbe.storSlot); |
| } |
| copyCacheAndDir(cache_entry, getDirEntry(address), tbe, initial); |
| |
| // model the initial tag array read |
| tbe.actions.pushNB(Event:TagArrayRead); |
| |
| incomingTransactionStart(address, curTransitionEvent(), initial, false); |
| } |
| |
| |
| |
| action(StallRequest, desc="") { |
| // was stalled because of an existing request |
| assert(is_valid(tbe)); |
| assert(tbe.addr == address); |
| // tracks pending |
| tbe.wakeup_pending_req := true; |
| stall_and_wait(reqRdyPort, address); |
| } |
| |
| action(StallSnoop, desc="") { |
| // was stalled because of an existing request |
| assert(is_valid(tbe)); |
| assert(tbe.addr == address); |
| // tracks pending |
| tbe.wakeup_pending_snp := true; |
| stall_and_wait(snpRdyPort, address); |
| } |
| |
| action(StallLocalEviction, desc="") { |
| // was stalled because of an existing request |
| assert(is_valid(tbe)); |
| assert(tbe.addr == address); |
| |
| // Just pop the queue and When this transaction finishes wake-up the original |
| // msgs that caused this eviction |
| tbe.wakeup_pending_tgr := true; |
| replTriggerInPort.dequeue(clockEdge()); |
| } |
| |
| action(StallSnoop_NoTBE, desc="") { |
| stall_and_wait(snpRdyPort, address); |
| } |
| |
| action(StallActionOnHazard, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.is_req_hazard || tbe.is_repl_hazard); |
| tbe.wakeup_pending_tgr := true; |
| stall_and_wait(triggerInPort, address); |
| } |
| |
| action(Initiate_ReadShared_Miss, desc="") { |
| tbe.actions.push(Event:ReadMissPipe); |
| if (is_HN && tbe.use_DMT) { |
| tbe.requestorToBeExclusiveOwner := true; |
| tbe.dataMaybeDirtyUpstream := true; // SNF always replies with CompData_UC |
| if (enable_DMT_early_dealloc) { |
| tbe.actions.push(Event:SendRespSepData); |
| } |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendReadNoSnpDMT); |
| } else if (is_HN) { |
| tbe.actions.push(Event:SendReadNoSnp); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } else { |
| tbe.actions.push(Event:SendReadShared); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } |
| tbe.actions.push(Event:CheckCacheFill); |
| tbe.actions.push(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_ReadShared_Hit, desc="") { |
| tbe.actions.push(Event:ReadHitPipe); |
| tbe.actions.push(Event:DataArrayRead); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_ReadShared_HitUpstream, desc="") { |
| tbe.actions.push(Event:ReadMissPipe); |
| if (tbe.use_DCT) { |
| tbe.actions.push(Event:SendSnpSharedFwdToOwner); |
| tbe.actions.pushNB(Event:WaitCompAck); |
| tbe.updateDirOnCompAck := false; |
| } else { |
| tbe.actions.push(Event:SendSnpShared); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } |
| tbe.actions.push(Event:MaintainCoherence); |
| } |
| |
| action(Initiate_ReadShared_HitUpstream_NoOwner, desc="") { |
| tbe.actions.push(Event:ReadMissPipe); |
| if (tbe.use_DCT) { |
| tbe.actions.push(Event:SendSnpSharedFwdToSharer); |
| tbe.actions.pushNB(Event:WaitCompAck); |
| tbe.updateDirOnCompAck := false; |
| } else { |
| tbe.actions.push(Event:SendSnpOnce); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } |
| tbe.actions.push(Event:MaintainCoherence); |
| } |
| |
| |
| action(Initiate_ReadOnce_Miss, desc="") { |
| // drop at the end if not doing a fill |
| tbe.dataToBeInvalid := tbe.doCacheFill == false; |
| |
| tbe.actions.push(Event:ReadMissPipe); |
| if (is_HN && tbe.use_DMT) { |
| assert(is_invalid(cache_entry)); |
| tbe.requestorToBeExclusiveOwner := true; |
| tbe.dataMaybeDirtyUpstream := true; // SNF always replies with CompData_UC |
| if (enable_DMT_early_dealloc) { |
| tbe.actions.push(Event:SendRespSepData); |
| } |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendReadNoSnpDMT); |
| } else if (is_HN) { |
| tbe.actions.push(Event:SendReadNoSnp); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } else { |
| // if not allocating an entry send a ReadOnce |
| if (tbe.dataToBeInvalid) { |
| tbe.actions.push(Event:SendReadOnce); |
| } else { |
| tbe.actions.push(Event:SendReadShared); |
| } |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } |
| |
| tbe.updateDirOnCompAck := false; |
| |
| tbe.actions.push(Event:CheckCacheFill); |
| tbe.actions.push(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_ReadOnce_Hit, desc="") { |
| tbe.actions.push(Event:ReadHitPipe); |
| tbe.actions.push(Event:DataArrayRead); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| tbe.updateDirOnCompAck := false; |
| } |
| |
| action(Initiate_ReadOnce_HitUpstream, desc="") { |
| tbe.actions.push(Event:ReadMissPipe); |
| if (tbe.use_DCT) { |
| tbe.actions.push(Event:SendSnpOnceFwd); |
| tbe.actions.pushNB(Event:WaitCompAck); |
| } else { |
| tbe.actions.push(Event:SendSnpOnce); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } |
| tbe.updateDirOnCompAck := false; |
| // no need to update or access tags/data on ReadOnce served from upstream |
| |
| if (is_invalid(cache_entry)) { |
| // if we receive data, invalidate at the end so it can be dropped |
| tbe.dataToBeInvalid := true; |
| } else if (tbe.dataValid == false) { |
| // possible on UD_RU,UC_RU where cache_entry valid but tbe.dataValid == false |
| // this prevents going to RU if no data is received from snoop |
| tbe.dataValid := true; |
| } |
| } |
| |
| |
| |
| action(Initiate_ReadUnique_Miss, desc="") { |
| tbe.actions.push(Event:ReadMissPipe); |
| if (is_HN && tbe.use_DMT) { |
| tbe.requestorToBeExclusiveOwner := true; |
| tbe.dataMaybeDirtyUpstream := true; // SNF always replies with CompData_UC |
| if (enable_DMT_early_dealloc) { |
| tbe.actions.push(Event:SendRespSepData); |
| } |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendReadNoSnpDMT); |
| } else if (is_HN) { |
| tbe.actions.push(Event:SendReadNoSnp); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } else { |
| tbe.actions.push(Event:SendReadUnique); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } |
| tbe.actions.push(Event:CheckCacheFill); |
| tbe.actions.push(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_ReadUnique_AutoUpgrade, desc="") { |
| assert(is_HN); |
| tbe.dataUnique := true; |
| } |
| |
| action(Initiate_ReadUnique_Upgrade, desc="") { |
| // must use the transitions with auto upgrade otherwise |
| assert(is_HN == false); |
| assert(tbe.use_DCT == false); |
| assert((tbe.dataValid && tbe.dataUnique) == false); |
| assert((tbe.dir_ownerExists && tbe.dir_ownerIsExcl) == false); |
| |
| tbe.actions.push(Event:ReadMissPipe); |
| if (tbe.dataMaybeDirtyUpstream) { |
| tbe.actions.push(Event:SendSnpUnique); |
| } else if (tbe.dir_sharers.count() > 0) { |
| // no one will send us data unless we explicitly ask |
| tbe.actions.push(Event:SendSnpUniqueRetToSrc); |
| } else { |
| assert(tbe.dataValid); |
| } |
| // then attempt to upgrade our data |
| tbe.actions.push(Event:SendCleanUnique); |
| tbe.actions.push(Event:CheckUpgrade_FromRU); |
| |
| // send up the upgraded data or fresh data if we failed, see CheckUpgrade_FromRU |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| tbe.actions.push(Event:CheckCacheFill); |
| tbe.actions.push(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_ReadUnique_Hit, desc="") { |
| tbe.actions.push(Event:ReadHitPipe); |
| tbe.actions.push(Event:DataArrayRead); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_ReadUnique_HitUpstream, desc="") { |
| tbe.actions.push(Event:ReadMissPipe); |
| // SnpUniqueFwd can be used only if the line is cached at a single |
| // requester; so force it off if that's the case |
| tbe.use_DCT := tbe.use_DCT && (tbe.dir_sharers.count() == 1) && |
| (tbe.dir_sharers.isElement(tbe.requestor) == false); |
| if (tbe.use_DCT) { |
| tbe.actions.push(Event:SendSnpUniqueFwd); |
| tbe.actions.pushNB(Event:WaitCompAck); |
| tbe.updateDirOnCompAck := false; |
| } else if (tbe.dataMaybeDirtyUpstream) { |
| tbe.actions.push(Event:SendSnpUnique); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } else { |
| // no one will send us data unless we explicitly ask |
| tbe.actions.push(Event:SendSnpUniqueRetToSrc); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| } |
| // just tag update since data any data would become stale |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_ReadUnique_Hit_InvUpstream, desc="") { |
| tbe.actions.push(Event:ReadHitPipe); |
| tbe.actions.push(Event:SendSnpCleanInvalid); |
| tbe.actions.pushNB(Event:DataArrayRead); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.pushNB(Event:SendCompData); |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_CleanUnique, desc="") { |
| tbe.actions.push(Event:ReadMissPipe); // TODO need another latency pipe ?? |
| |
| // invalidates everyone except requestor |
| if (tbe.dir_sharers.count() > 1) { |
| tbe.actions.push(Event:SendSnpCleanInvalidNoReq); |
| } |
| // auto upgrade if HN |
| tbe.dataUnique := tbe.dataUnique || is_HN; |
| // get unique permission |
| if (tbe.dataUnique == false) { |
| tbe.actions.push(Event:SendCleanUnique); |
| tbe.actions.push(Event:CheckUpgrade_FromCU); |
| } |
| // next actions will depend on the data state after snoops+CleanUnique |
| tbe.actions.push(Event:FinishCleanUnique); |
| } |
| |
| action(Initiate_CleanUnique_Stale, desc="") { |
| // requestor don't have the line anymore; send response but don't update the |
| // directory on CompAck. The requestor knows we are not tracking it and will |
| // send a ReadUnique later |
| tbe.actions.push(Event:SendCompUCRespStale); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.updateDirOnCompAck := false; |
| } |
| |
| action(Finish_CleanUnique, desc="") { |
| // This is should be executed at the end of a transaction |
| assert(tbe.actions.empty()); |
| |
| // everyone may have been hit by an invalidation so check again |
| if (tbe.dir_sharers.isElement(tbe.requestor) == false) { |
| tbe.updateDirOnCompAck := false; |
| assert(tbe.dataValid == false); |
| assert(tbe.is_stale); |
| tbe.is_stale := false; |
| tbe.actions.push(Event:SendCompUCRespStale); |
| tbe.actions.push(Event:WaitCompAck); |
| tbe.actions.push(Event:TagArrayWrite); |
| } else { |
| // must be the only one in sharers map |
| assert(tbe.dir_sharers.count() == 1); |
| assert(tbe.dataUnique); |
| |
| // needed by UpdateDirState_FromReqResp triggered by the expected CompAck |
| tbe.dataMaybeDirtyUpstream := true; |
| tbe.requestorToBeExclusiveOwner := true; |
| tbe.dir_ownerExists := false; |
| |
| tbe.actions.push(Event:SendCompUCResp); |
| tbe.actions.push(Event:WaitCompAck); |
| |
| // Ensure we writeback or update the cache if the owner has data as |
| // clean data and we have it dirty. |
| // MaintainCoherence queues the TagArrayWrite |
| tbe.actions.push(Event:MaintainCoherence); |
| } |
| } |
| |
| |
| action(Initiate_LoadHit, desc="") { |
| // Local prefetch requests do not read data array |
| if (tbe.is_local_pf == false) { |
| tbe.actions.push(Event:DataArrayRead); |
| } |
| tbe.actions.push(Event:LoadHit); |
| } |
| |
| action(Initiate_LoadMiss, desc="") { |
| if (tbe.doCacheFill) { |
| tbe.actions.push(Event:SendReadShared); |
| tbe.actions.push(Event:CheckCacheFill); |
| tbe.actions.push(Event:TagArrayWrite); |
| } else { |
| tbe.actions.push(Event:SendReadOnce); |
| tbe.dataToBeInvalid := true; |
| } |
| } |
| |
| |
| |
| action(Initiate_StoreHit, desc="") { |
| tbe.actions.push(Event:DataArrayRead); |
| tbe.actions.push(Event:StoreHit); |
| tbe.actions.push(Event:CheckCacheFill); |
| tbe.actions.push(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_StoreMiss, desc="") { |
| if (tbe.doCacheFill) { |
| tbe.actions.push(Event:SendReadUnique); |
| tbe.actions.push(Event:CheckCacheFill); |
| tbe.actions.push(Event:TagArrayWrite); |
| } else { |
| tbe.actions.push(Event:SendWriteUnique); |
| tbe.actions.push(Event:SendWUDataCB); |
| tbe.dataToBeInvalid := true; |
| } |
| } |
| |
| action(Initiate_StoreUpgrade, desc="") { |
| assert(tbe.dataValid); |
| assert(is_valid(cache_entry)); |
| tbe.actions.push(Event:SendCleanUnique); |
| tbe.actions.push(Event:CheckUpgrade_FromStore); |
| tbe.actions.push(Event:CheckCacheFill); |
| tbe.actions.push(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_WriteUnique_LocalWrite, desc="") { |
| // auto-upgrade if hn but state was not unique |
| assert(is_HN || tbe.dataUnique); |
| tbe.dataUnique := true; |
| if (tbe.dir_sharers.count() > 0) { |
| tbe.actions.push(Event:SendSnpCleanInvalid); |
| } |
| if (comp_wu) { |
| tbe.actions.push(Event:SendDBIDResp_WU); |
| tbe.actions.pushNB(Event:WriteFEPipe); |
| tbe.actions.pushNB(Event:SendComp_WU); |
| } else { |
| tbe.actions.push(Event:SendCompDBIDResp_WU); |
| tbe.actions.pushNB(Event:WriteFEPipe); |
| } |
| tbe.actions.push(Event:CheckCacheFill); |
| tbe.actions.push(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_WriteUnique_LocalWrite_AfterUpgrade, desc="") { |
| assert(is_HN == false); |
| assert((tbe.dataValid && tbe.dataUnique) == false); |
| tbe.actions.push(Event:SendReadUnique); |
| if (comp_wu) { |
| tbe.actions.push(Event:SendDBIDResp_WU); |
| tbe.actions.pushNB(Event:WriteFEPipe); |
| tbe.actions.pushNB(Event:SendComp_WU); |
| } else { |
| tbe.actions.push(Event:SendCompDBIDResp_WU); |
| tbe.actions.pushNB(Event:WriteFEPipe); |
| } |
| tbe.actions.push(Event:CheckCacheFill); |
| tbe.actions.push(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_WriteUnique_Writeback, desc="") { |
| assert(is_HN); |
| assert(tbe.dir_sharers.count() > 0); |
| tbe.actions.push(Event:SendSnpUnique); |
| if (comp_wu) { |
| tbe.actions.push(Event:SendDBIDResp_WU); |
| tbe.actions.pushNB(Event:WriteFEPipe); |
| tbe.actions.pushNB(Event:SendWriteNoSnp); |
| tbe.actions.pushNB(Event:SendComp_WU); |
| } else { |
| tbe.actions.push(Event:SendCompDBIDResp_WU); |
| tbe.actions.pushNB(Event:WriteFEPipe); |
| tbe.actions.pushNB(Event:SendWriteNoSnp); |
| } |
| tbe.actions.push(Event:WriteBEPipe); |
| tbe.actions.push(Event:SendWBData); |
| tbe.dataToBeInvalid := true; |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_WriteUnique_PartialWrite, desc="") { |
| assert(is_HN); |
| if (tbe.dir_sharers.count() > 0) { |
| tbe.actions.push(Event:SendSnpCleanInvalid); |
| } |
| if (comp_wu) { |
| tbe.actions.push(Event:SendDBIDResp_WU); |
| tbe.actions.pushNB(Event:WriteFEPipe); |
| tbe.actions.pushNB(Event:SendWriteNoSnpPartial); |
| tbe.actions.pushNB(Event:SendComp_WU); |
| } else { |
| tbe.actions.push(Event:SendCompDBIDResp_WU); |
| tbe.actions.pushNB(Event:WriteFEPipe); |
| tbe.actions.pushNB(Event:SendWriteNoSnpPartial); |
| } |
| tbe.actions.push(Event:WriteBEPipe); |
| tbe.actions.push(Event:SendWUData); |
| tbe.dataToBeInvalid := true; |
| |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_WriteUnique_Forward, desc="") { |
| tbe.actions.push(Event:WriteFEPipe); |
| tbe.actions.push(Event:SendWriteUnique); |
| tbe.actions.push(Event:SendCompDBIDResp_WU); |
| tbe.actions.push(Event:WriteBEPipe); |
| tbe.actions.push(Event:SendWUData); |
| tbe.dataToBeInvalid := true; |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| |
| |
| action(Initiate_CopyBack, desc="") { |
| // expect to receive this data after Send_CompDBIDResp |
| if (tbe.reqType == CHIRequestType:WriteBackFull) { |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CBWrData_UD_PD); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CBWrData_SD_PD); |
| } else if (tbe.reqType == CHIRequestType:WriteEvictFull) { |
| assert(tbe.reqType == CHIRequestType:WriteEvictFull); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CBWrData_UC); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CBWrData_SC); |
| } else { |
| assert(tbe.reqType == CHIRequestType:WriteCleanFull); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CBWrData_UD_PD); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CBWrData_SD_PD); |
| } |
| tbe.expected_req_resp.setExpectedCount(1); |
| |
| tbe.actions.pushNB(Event:SendCompDBIDResp); |
| tbe.actions.pushNB(Event:WriteFEPipe); |
| tbe.actions.push(Event:MaintainCoherence); |
| // MaintainCoherence queues the Tag/Data updates |
| } |
| |
| action(Initiate_CopyBack_Stale, desc="") { |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CBWrData_SC); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CBWrData_I); |
| tbe.expected_req_resp.setExpectedCount(1); |
| |
| tbe.actions.pushNB(Event:SendCompDBIDRespStale); |
| tbe.actions.pushNB(Event:WriteFEPipe); |
| |
| // eviction condition should be examined if it is the last sharer |
| if (tbe.dir_sharers.count() == 1) { |
| tbe.actions.push(Event:FinishCopyBack_Stale); |
| } |
| |
| assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != tbe.requestor)); |
| } |
| |
| action(Finish_CopyBack_Stale, desc="") { |
| // if it was the last known sharer and we don't have the data do the same |
| // the Initiate_Evict |
| if ((is_HN == false) && (tbe.dir_sharers.count() == 0) && |
| (tbe.dataValid == false)) { |
| tbe.actions.push(Event:SendEvict); |
| } |
| } |
| |
| action(Initiate_Evict, desc="") { |
| if ((is_HN == false) && (tbe.dir_sharers.count() == 1) && |
| (tbe.dataValid == false)) { |
| // last sharer and we also don't have a copy the line, so we also need to |
| // send an eviction downstream |
| if (tbe.dataUnique) { |
| // we need to send a WriteEvictFull so need the upstream data before |
| // we ack the evict |
| tbe.actions.push(Event:SendSnpOnce); |
| tbe.actions.push(Event:SendCompIResp); |
| tbe.actions.push(Event:SendWriteBackOrWriteEvict); |
| tbe.actions.push(Event:WriteBEPipe); |
| tbe.actions.push(Event:SendWBData); |
| } else { |
| tbe.actions.push(Event:SendCompIResp); |
| tbe.actions.push(Event:SendEvict); |
| } |
| } else { |
| tbe.actions.push(Event:SendCompIResp); |
| } |
| tbe.actions.push(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_MaintainCoherence, desc="") { |
| // issue a copy back if necessary to maintain coherence for data we are |
| // droping. This is should be executed at the end of a transaction |
| assert(tbe.actions.empty()); |
| // go through either the fill or the writeback pipeline |
| if (tbe.dataValid && tbe.dataToBeInvalid) { |
| // we don't need to WB if the upstream is SD, because the |
| // owner is responsible for the WB |
| bool has_non_ex_owner := tbe.dir_ownerExists && !tbe.dir_ownerIsExcl; |
| if (is_HN) { |
| if (tbe.dataDirty && !has_non_ex_owner) { |
| tbe.actions.push(Event:SendWriteNoSnp); |
| tbe.actions.push(Event:WriteBEPipe); |
| tbe.actions.push(Event:SendWBData); |
| } |
| } else { |
| if (tbe.dir_sharers.isEmpty() && (tbe.dataDirty || tbe.dataUnique)) { |
| assert(!has_non_ex_owner); |
| tbe.actions.push(Event:SendWriteBackOrWriteEvict); |
| tbe.actions.push(Event:WriteBEPipe); |
| tbe.actions.push(Event:SendWBData); |
| } else if (tbe.dataDirty && !has_non_ex_owner) { |
| assert(!tbe.dir_sharers.isEmpty()); |
| tbe.actions.push(Event:SendWriteClean); |
| tbe.actions.push(Event:WriteBEPipe); |
| tbe.actions.push(Event:SendWBData); |
| } |
| } |
| } else if (tbe.dataValid) { |
| tbe.actions.push(Event:CheckCacheFill); |
| } |
| tbe.actions.push(Event:TagArrayWrite); |
| } |
| |
| |
| |
| // Too many common stuff between SnpUnique/SnpUniqueFwd/SnpCleanInvalid |
| // so do one action for all of them here |
| action(Initiate_InvalidationSnoop, desc="") { |
| tbe.actions.push(Event:SnpInvPipe); |
| // Propagate a snoop upwards depending on the type |
| if (tbe.dir_sharers.count() > 0) { |
| if ((tbe.reqType == CHIRequestType:SnpUniqueFwd) || |
| (tbe.reqType == CHIRequestType:SnpUnique)) { |
| if ((tbe.snpNeedsData && (tbe.dataMaybeDirtyUpstream == false)) || |
| (tbe.dataValid == false)) { |
| tbe.actions.push(Event:SendSnpUniqueRetToSrc); |
| } else { |
| tbe.actions.push(Event:SendSnpUnique); |
| } |
| } else { |
| assert(tbe.reqType == CHIRequestType:SnpCleanInvalid); |
| tbe.actions.push(Event:SendSnpCleanInvalid); |
| } |
| } |
| |
| if (tbe.reqType == CHIRequestType:SnpUniqueFwd) { |
| tbe.actions.push(Event:SendSnpUniqueFwdCompData); |
| } else { |
| tbe.actions.push(Event:SendInvSnpResp); |
| } |
| |
| if(tbe.is_req_hazard || tbe.is_repl_hazard) { |
| tbe.actions.push(Event:RestoreFromHazard); |
| } else { |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| tbe.dataToBeInvalid := true; |
| } |
| |
| action(Initiate_SnpShared, desc="") { |
| // Handles both SnpShared,SnpSharedFwd,SnpNotSharedDirtyFwd |
| tbe.actions.push(Event:SnpSharedPipe); |
| if (tbe.dir_ownerExists) { |
| assert(tbe.dataMaybeDirtyUpstream); |
| tbe.actions.push(Event:SendSnpShared); |
| } else if (tbe.dataValid == false) { |
| // must get a copy of shared data upstream |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| assert(tbe.dir_sharers.count() > 0); |
| tbe.actions.push(Event:SendSnpOnce); |
| } else { |
| tbe.actions.push(Event:DataArrayRead); |
| } |
| |
| if (tbe.reqType == CHIRequestType:SnpSharedFwd) { |
| tbe.actions.push(Event:SendSnpSharedFwdCompData); |
| } else if (tbe.reqType == CHIRequestType:SnpNotSharedDirtyFwd) { |
| tbe.actions.push(Event:SendSnpNotSharedDirtyFwdCompData); |
| } else { |
| assert(tbe.reqType == CHIRequestType:SnpShared); |
| tbe.actions.push(Event:SendSnpData); |
| } |
| if (tbe.is_req_hazard || tbe.is_repl_hazard) { |
| tbe.actions.push(Event:RestoreFromHazard); |
| } else { |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| tbe.dataToBeSharedClean := true; |
| } |
| |
| action(Initiate_SnpOnce, desc="") { |
| tbe.actions.push(Event:SnpOncePipe); |
| if (tbe.dataValid == false) { |
| assert(tbe.dir_sharers.count() > 0); |
| tbe.actions.push(Event:SendSnpOnce); |
| } else { |
| tbe.actions.push(Event:DataArrayRead); |
| } |
| |
| if (tbe.reqType == CHIRequestType:SnpOnceFwd) { |
| tbe.actions.push(Event:SendSnpOnceFwdCompData); |
| } else { |
| assert(tbe.reqType == CHIRequestType:SnpOnce); |
| assert(tbe.snpNeedsData); |
| tbe.actions.push(Event:SendSnpData); |
| } |
| |
| if (tbe.is_req_hazard || tbe.is_repl_hazard) { |
| tbe.actions.push(Event:RestoreFromHazard); |
| } else { |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| } |
| |
| |
| |
| action(Initiate_Replacement_Evict_BackInvalidte, desc="") { |
| assert(is_HN == false); |
| tbe.actions.push(Event:SendSnpCleanInvalid); |
| tbe.actions.push(Event:SendEvict); |
| tbe.dataToBeInvalid := true; |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_Replacement_Evict, desc="") { |
| assert(is_HN == false); |
| assert(tbe.dir_sharers.isEmpty()); |
| tbe.actions.push(Event:SendEvict); |
| tbe.dataToBeInvalid := true; |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_Replacement_JustDrop, desc="") { |
| tbe.dataToBeInvalid := true; |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_Replacement_WB_BackInvalidate, desc="") { |
| assert(tbe.dataDirty || tbe.dataUnique || tbe.dataMaybeDirtyUpstream); |
| tbe.actions.push(Event:SendSnpCleanInvalid); |
| tbe.actions.push(Event:WriteFEPipe); |
| if (is_HN) { |
| if (tbe.dataDirty || tbe.dataMaybeDirtyUpstream) { |
| tbe.actions.push(Event:SendWriteNoSnp); |
| } |
| } else { |
| tbe.actions.push(Event:SendWriteBackOrWriteEvict); |
| } |
| tbe.actions.pushNB(Event:DataArrayRead); |
| tbe.actions.push(Event:WriteBEPipe); |
| tbe.actions.push(Event:SendWBData); |
| tbe.dataToBeInvalid := true; |
| |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| action(Initiate_Replacement_WB, desc="") { |
| tbe.actions.push(Event:WriteFEPipe); |
| if (is_HN) { |
| assert(tbe.dataDirty); |
| tbe.actions.push(Event:SendWriteNoSnp); |
| } else if (tbe.dir_sharers.isEmpty()) { |
| assert(tbe.dataDirty || tbe.dataUnique); |
| tbe.actions.push(Event:SendWriteBackOrWriteEvict); |
| } else { |
| assert(tbe.dataDirty); |
| tbe.actions.push(Event:SendWriteClean); |
| } |
| tbe.actions.pushNB(Event:DataArrayRead); |
| tbe.actions.push(Event:WriteBEPipe); |
| tbe.actions.push(Event:SendWBData); |
| tbe.dataToBeInvalid := true; |
| tbe.actions.pushNB(Event:TagArrayWrite); |
| } |
| |
| |
| |
| action(Send_ReadShared, desc="") { |
| assert(is_HN == false); |
| assert(tbe.dataValid == false); |
| |
| clearExpectedReqResp(tbe); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:DataSepResp_UC); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_UC); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_UD_PD); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_SC); |
| if (allow_SD) { |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_SD_PD); |
| } |
| // NOTE: the first CompData received counts as RespSepData |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:RespSepData); |
| tbe.expected_req_resp.setExpectedCount(2); |
| tbe.dataBlkValid.clear(); |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| if (allow_SD) { |
| prepareRequest(tbe, CHIRequestType:ReadShared, out_msg); |
| } else { |
| prepareRequest(tbe, CHIRequestType:ReadNotSharedDirty, out_msg); |
| } |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| out_msg.dataToFwdRequestor := false; |
| allowRequestRetry(tbe, out_msg); |
| } |
| } |
| |
| action(Send_ReadNoSnp, desc="") { |
| assert(is_HN); |
| assert(tbe.use_DMT == false); |
| |
| clearExpectedReqResp(tbe); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_UC); |
| // NOTE: the first CompData received counts as RespSepData |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:RespSepData); |
| tbe.expected_req_resp.setExpectedCount(2); |
| tbe.dataBlkValid.clear(); |
| outgoingTransactionStart(address, curTransitionEvent()); |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, CHIRequestType:ReadNoSnp, out_msg); |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| out_msg.dataToFwdRequestor := false; |
| allowRequestRetry(tbe, out_msg); |
| } |
| } |
| |
| action(Send_ReadNoSnpDMT, desc="") { |
| assert(is_HN); |
| assert(tbe.use_DMT); |
| |
| CHIRequestType req := CHIRequestType:ReadNoSnp; |
| if (enable_DMT_early_dealloc) { |
| req := CHIRequestType:ReadNoSnpSep; |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:ReadReceipt); |
| tbe.expected_req_resp.addExpectedCount(1); |
| } |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, req, out_msg); |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| out_msg.dataToFwdRequestor := true; |
| allowRequestRetry(tbe, out_msg); |
| } |
| } |
| |
| action(Send_ReadOnce, desc="") { |
| assert(is_HN == false); |
| assert(tbe.dataValid == false); |
| |
| clearExpectedReqResp(tbe); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:DataSepResp_UC); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_UC); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_I); |
| // NOTE: the first CompData received counts as RespSepData |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:RespSepData); |
| tbe.expected_req_resp.setExpectedCount(2); |
| tbe.dataBlkValid.clear(); |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, CHIRequestType:ReadOnce, out_msg); |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| out_msg.dataToFwdRequestor := false; |
| allowRequestRetry(tbe, out_msg); |
| } |
| } |
| |
| action(Send_ReadUnique, desc="") { |
| assert((tbe.dataValid && tbe.dataUnique) == false); |
| |
| assert(tbe.expected_req_resp.hasExpected() == false); |
| clearExpectedReqResp(tbe); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:DataSepResp_UC); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_UC); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_UD_PD); |
| // NOTE: the first CompData received counts as RespSepData |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:RespSepData); |
| tbe.expected_req_resp.setExpectedCount(2); |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, CHIRequestType:ReadUnique, out_msg); |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| out_msg.dataToFwdRequestor := false; |
| allowRequestRetry(tbe, out_msg); |
| } |
| } |
| |
| action(Send_CleanUnique, desc="") { |
| assert(tbe.dataValid || (tbe.dir_sharers.count() > 0)); |
| assert(tbe.dataUnique == false); |
| |
| assert(tbe.expected_req_resp.hasExpected() == false); |
| clearExpectedReqResp(tbe); |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:Comp_UC); |
| tbe.expected_req_resp.setExpectedCount(1); |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, CHIRequestType:CleanUnique, out_msg); |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| allowRequestRetry(tbe, out_msg); |
| } |
| } |
| |
| action(Send_Evict, desc="") { |
| assert(is_valid(tbe)); |
| assert(is_HN == false); |
| assert(tbe.expected_req_resp.hasExpected() == false); |
| clearExpectedReqResp(tbe); |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, CHIRequestType:Evict, out_msg); |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| allowRequestRetry(tbe, out_msg); |
| } |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:Comp_I); |
| tbe.expected_req_resp.setExpectedCount(1); |
| } |
| |
| action(Send_InvSnpResp, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| if (tbe.dataDirty || tbe.snpNeedsData || |
| (tbe.dataUnique && (tbe.reqType == CHIRequestType:SnpUnique))) { |
| tbe.actions.pushFront(Event:SendSnpData); |
| } else { |
| tbe.actions.pushFront(Event:SendSnpIResp); |
| } |
| } |
| |
| action(Send_WriteBackOrWriteEvict, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.dataBlkValid.isFull()); |
| assert(tbe.dataValid); |
| assert(is_HN == false); |
| |
| assert(tbe.dataUnique || tbe.dataDirty); |
| assert(tbe.dir_sharers.isEmpty()); |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| if (tbe.dataDirty) { |
| prepareRequest(tbe, CHIRequestType:WriteBackFull, out_msg); |
| } else { |
| prepareRequest(tbe, CHIRequestType:WriteEvictFull, out_msg); |
| } |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| allowRequestRetry(tbe, out_msg); |
| } |
| clearExpectedReqResp(tbe); |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:CompDBIDResp); |
| tbe.expected_req_resp.setExpectedCount(1); |
| } |
| |
| action(Send_WriteCleanFull, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.dataBlkValid.isFull()); |
| assert(tbe.dataValid); |
| assert(is_HN == false); |
| assert(tbe.dataDirty); |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, CHIRequestType:WriteCleanFull, out_msg); |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| allowRequestRetry(tbe, out_msg); |
| } |
| clearExpectedReqResp(tbe); |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:CompDBIDResp); |
| tbe.expected_req_resp.setExpectedCount(1); |
| } |
| |
| action(Send_WriteNoSnp, desc="") { |
| assert(is_valid(tbe)); |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, CHIRequestType:WriteNoSnp, out_msg); |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| allowRequestRetry(tbe, out_msg); |
| } |
| // allow to expect this on top of data coming from upstream; |
| // so addExpectedCount |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:CompDBIDResp); |
| tbe.expected_req_resp.addExpectedCount(1); |
| } |
| |
| action(Send_WriteNoSnp_Partial, desc="") { |
| assert(is_valid(tbe)); |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, CHIRequestType:WriteNoSnpPtl, out_msg); |
| out_msg.accAddr := tbe.accAddr; |
| out_msg.accSize := tbe.accSize; |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| allowRequestRetry(tbe, out_msg); |
| } |
| // allow to expect this on top of data coming from upstream; |
| // so addExpectedCount |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:CompDBIDResp); |
| tbe.expected_req_resp.addExpectedCount(1); |
| } |
| |
| action(Send_WriteUnique, desc="") { |
| assert(is_valid(tbe)); |
| |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| if (tbe.accSize == blockSize) { |
| prepareRequest(tbe, CHIRequestType:WriteUniqueFull, out_msg); |
| } else { |
| prepareRequest(tbe, CHIRequestType:WriteUniquePtl, out_msg); |
| out_msg.accAddr := tbe.accAddr; |
| out_msg.accSize := tbe.accSize; |
| } |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| allowRequestRetry(tbe, out_msg); |
| } |
| // allow to expect this on top of data coming from upstream; |
| // so addExpectedCount |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:CompDBIDResp); |
| // if receive only DBIDResp then will expect Comp later |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:DBIDResp); |
| tbe.expected_req_resp.addExpectedCount(1); |
| } |
| |
| action(Send_SnpCleanInvalid, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.expected_snp_resp.hasExpected() == false); |
| // at least one sharer or owner othrwise should not execute this |
| assert(tbe.dir_sharers.count() > 0); |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| prepareRequest(tbe, CHIRequestType:SnpCleanInvalid, out_msg); |
| out_msg.Destination.addNetDest(tbe.dir_sharers); |
| out_msg.retToSrc := false; |
| } |
| setExpectedForInvSnoop(tbe, false); |
| } |
| |
| action(Send_SnpCleanInvalid_NoReq, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.expected_snp_resp.hasExpected() == false); |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| prepareRequest(tbe, CHIRequestType:SnpCleanInvalid, out_msg); |
| out_msg.Destination.addNetDest(tbe.dir_sharers); |
| out_msg.Destination.remove(tbe.requestor); |
| // at least one sharer other than requestor |
| assert(out_msg.Destination.count() > 0); |
| out_msg.retToSrc := false; |
| setExpectedForInvSnoop(tbe, false); |
| tbe.expected_snp_resp.setExpectedCount(out_msg.Destination.count()); |
| } |
| } |
| |
| action(Send_SnpUnique, desc="") { |
| assert(is_valid(tbe)); |
| // at least one sharer or owner othrwise should not execute this |
| assert(tbe.dir_sharers.count() > 0); |
| |
| setExpectedForInvSnoop(tbe, true); |
| |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| prepareRequest(tbe, CHIRequestType:SnpUnique, out_msg); |
| out_msg.Destination.addNetDest(tbe.dir_sharers); |
| out_msg.retToSrc := false; |
| } |
| } |
| |
| action(Send_SnpUnique_RetToSrc, desc="") { |
| assert(is_valid(tbe)); |
| // at least one sharer or owner othrwise should not execute this |
| assert(tbe.dir_sharers.count() > 0); |
| |
| setExpectedForInvSnoop(tbe, true); |
| |
| MachineID dest; |
| if (tbe.dir_ownerExists) { |
| dest := tbe.dir_owner; |
| } else { |
| // TODO should be random or the closest one |
| dest := tbe.dir_sharers.smallestElement(); |
| } |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| prepareRequest(tbe, CHIRequestType:SnpUnique, out_msg); |
| out_msg.Destination.add(dest); |
| out_msg.retToSrc := true; |
| } |
| // if other sharers send with retToSrc=false to others |
| if (tbe.dir_sharers.count() > 1) { |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| prepareRequest(tbe, CHIRequestType:SnpUnique, out_msg); |
| out_msg.Destination.addNetDest(tbe.dir_sharers); |
| out_msg.Destination.remove(dest); |
| out_msg.retToSrc := false; |
| } |
| } |
| } |
| |
| action(Send_SnpUniqueFwd, desc="") { |
| assert(is_valid(tbe)); |
| // single sharer or owner otherwise should not execute this |
| assert(tbe.dir_sharers.count() == 1); |
| |
| assert(tbe.expected_snp_resp.expected() == 0); |
| clearExpectedSnpResp(tbe); |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_I_Fwded_UC); |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_I_Fwded_UD_PD); |
| tbe.expected_snp_resp.addExpectedCount(1); |
| |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| prepareRequest(tbe, CHIRequestType:SnpUniqueFwd, out_msg); |
| out_msg.Destination.addNetDest(tbe.dir_sharers); |
| out_msg.retToSrc := false; |
| } |
| } |
| |
| action(Send_SnpShared, desc="") { |
| assert(is_valid(tbe)); |
| |
| // only sent to a dirty or exclusive snoopee |
| assert(tbe.dataMaybeDirtyUpstream); |
| assert(tbe.dir_ownerExists); |
| assert(tbe.dir_sharers.count() > 0); |
| |
| assert(tbe.expected_snp_resp.expected() == 0); |
| clearExpectedSnpResp(tbe); |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_SC); |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_SC_PD); |
| tbe.expected_snp_resp.setExpectedCount(1); |
| |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| prepareRequest(tbe, CHIRequestType:SnpShared, out_msg); |
| out_msg.Destination.add(tbe.dir_owner); |
| out_msg.retToSrc := false; |
| } |
| } |
| |
| action(Send_SnpSharedFwd_ToOwner, desc="") { |
| assert(is_valid(tbe)); |
| |
| // the dirty snoopee must go to SC and send data |
| assert(tbe.dataMaybeDirtyUpstream); |
| assert(tbe.dir_ownerExists); |
| assert(tbe.dir_sharers.count() > 0); |
| |
| assert(tbe.expected_snp_resp.expected() == 0); |
| clearExpectedSnpResp(tbe); |
| |
| bool allowFwdSD := tbe.reqType != CHIRequestType:ReadNotSharedDirty; |
| |
| // get us a copy if we have allocated a cache entry for this block |
| bool retToSrc := tbe.doCacheFill && (tbe.dataToBeInvalid == false); |
| |
| if (allowFwdSD) { |
| if (retToSrc) { |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_SC_Fwded_SC); |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_SC_Fwded_SD_PD); |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_I_Fwded_SC); |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_I_Fwded_SD_PD); |
| } else { |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_SC_Fwded_SC); |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_SC_Fwded_SD_PD); |
| } |
| } else { |
| if (retToSrc) { |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_SC_Fwded_SC); |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_I_Fwded_SC); |
| } else { |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_SC_Fwded_SC); |
| } |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_SC_PD_Fwded_SC); |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_I_PD_Fwded_SC); |
| } |
| tbe.expected_snp_resp.addExpectedCount(1); |
| |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| if (allowFwdSD) { |
| prepareRequest(tbe, CHIRequestType:SnpSharedFwd, out_msg); |
| } else { |
| prepareRequest(tbe, CHIRequestType:SnpNotSharedDirtyFwd, out_msg); |
| } |
| out_msg.Destination.add(tbe.dir_owner); |
| out_msg.retToSrc := retToSrc; |
| } |
| } |
| |
| action(Send_SnpSharedFwd_ToSharer, desc="") { |
| assert(is_valid(tbe)); |
| // send to onde of the sharers with shared clean data |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| assert(tbe.dir_ownerExists == false); |
| assert(tbe.dir_sharers.count() > 0); |
| |
| assert(tbe.expected_snp_resp.expected() == 0); |
| clearExpectedSnpResp(tbe); |
| // if we have a block allocated for this line, asks snoopee to forward |
| // data to us as well |
| bool retToSrc := tbe.doCacheFill; |
| if (retToSrc) { |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_SC_Fwded_SC); |
| } else { |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_SC_Fwded_SC); |
| } |
| tbe.expected_snp_resp.addExpectedCount(1); |
| |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| prepareRequest(tbe, CHIRequestType:SnpSharedFwd, out_msg); |
| // TODO should be random or the closest one to the fwd dest |
| out_msg.Destination.add(tbe.dir_sharers.smallestElement()); |
| out_msg.retToSrc := retToSrc; |
| } |
| } |
| |
| action(Send_SnpOnce, desc="") { |
| assert(is_valid(tbe)); |
| |
| // send to one of the sharers or owner to get a copy of the line |
| assert(tbe.dir_sharers.count() > 0); |
| |
| assert(tbe.expected_snp_resp.expected() == 0); |
| clearExpectedSnpResp(tbe); |
| |
| if (tbe.dir_ownerExists) { |
| if (tbe.dir_ownerIsExcl) { |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_UC); |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_UD); |
| } else { |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_SD); |
| } |
| } else { |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_SC); |
| } |
| tbe.expected_snp_resp.addExpectedCount(1); |
| |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| prepareRequest(tbe, CHIRequestType:SnpOnce, out_msg); |
| if (tbe.dir_ownerExists) { |
| out_msg.Destination.add(tbe.dir_owner); |
| } else { |
| // TODO should be random or the closest one |
| out_msg.Destination.add(tbe.dir_sharers.smallestElement()); |
| } |
| out_msg.retToSrc := true; |
| } |
| } |
| |
| action(Send_SnpOnceFwd, desc="") { |
| assert(is_valid(tbe)); |
| |
| // send to one of the sharers or owner to get a copy of the line |
| assert(tbe.dir_sharers.count() > 0); |
| |
| assert(tbe.expected_snp_resp.expected() == 0); |
| clearExpectedSnpResp(tbe); |
| |
| if (tbe.dir_ownerExists) { |
| if (tbe.dir_ownerIsExcl) { |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_UC_Fwded_I); |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_UD_Fwded_I); |
| } else { |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_SD_Fwded_I); |
| } |
| } else { |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_SC_Fwded_I); |
| } |
| tbe.expected_snp_resp.addExpectedCount(1); |
| |
| enqueue(snpOutPort, CHIRequestMsg, snoop_latency) { |
| prepareRequest(tbe, CHIRequestType:SnpOnceFwd, out_msg); |
| if (tbe.dir_ownerExists) { |
| out_msg.Destination.add(tbe.dir_owner); |
| } else { |
| // TODO should be random or the closest one |
| out_msg.Destination.add(tbe.dir_sharers.smallestElement()); |
| } |
| out_msg.retToSrc := false; |
| } |
| } |
| |
| |
| action(ExpectNCBWrData, desc="") { |
| // Expected data |
| int num_msgs := tbe.accSize / data_channel_size; |
| if ((tbe.accSize % data_channel_size) != 0) { |
| num_msgs := num_msgs + 1; |
| } |
| tbe.expected_req_resp.clear(num_msgs); |
| tbe.expected_req_resp.addExpectedDataType(CHIDataType:NCBWrData); |
| tbe.expected_req_resp.setExpectedCount(1); |
| |
| // Clear the mask bits we expect to receive |
| tbe.dataBlkValid.setMask(addressOffset(tbe.accAddr, tbe.addr), tbe.accSize, false); |
| } |
| |
| action(ExpectCompAck, desc="") { |
| assert(is_valid(tbe)); |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:CompAck); |
| tbe.expected_req_resp.addExpectedCount(1); |
| } |
| |
| action(ExpectComp, desc="") { |
| assert(is_valid(tbe)); |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:Comp); |
| tbe.expected_req_resp.addExpectedCount(1); |
| } |
| |
| action(Receive_ReqDataResp, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.expected_req_resp.hasExpected()); |
| peek(datInPort, CHIDataMsg) { |
| // Decrement pending |
| if (tbe.expected_req_resp.receiveData(in_msg.type) == false) { |
| error("Received unexpected message"); |
| } |
| // Copy data to tbe only if we didn't have valid data or the received |
| // data is dirty |
| if ((tbe.dataBlkValid.isFull() == false) || |
| (in_msg.type == CHIDataType:CompData_UD_PD) || |
| (in_msg.type == CHIDataType:CompData_SD_PD) || |
| (in_msg.type == CHIDataType:CBWrData_UD_PD) || |
| (in_msg.type == CHIDataType:CBWrData_SD_PD) || |
| (in_msg.type == CHIDataType:NCBWrData)) { |
| // clear mask if started to receive new data |
| if(tbe.dataBlkValid.isFull()){ |
| tbe.dataBlkValid.clear(); |
| } |
| tbe.dataBlk.copyPartial(in_msg.dataBlk, in_msg.bitMask); |
| assert(tbe.dataBlkValid.isOverlap(in_msg.bitMask) == false); |
| tbe.dataBlkValid.orMask(in_msg.bitMask); |
| } |
| } |
| } |
| |
| action(Receive_RespSepDataFromCompData, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.expected_req_resp.hasExpected()); |
| // check if a previous CompData msg already counted as a RespSepData |
| if (tbe.expected_req_resp.receivedRespType(CHIResponseType:RespSepData) == false) { |
| if (tbe.expected_req_resp.receiveResp(CHIResponseType:RespSepData) == false) { |
| error("Received unexpected message"); |
| } |
| if (is_HN == false) { |
| // must now ack the responder |
| tbe.actions.pushFrontNB(Event:SendCompAck); |
| } |
| } |
| } |
| |
| action(Receive_RespSepData, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.expected_req_resp.hasExpected()); |
| if (tbe.expected_req_resp.receiveResp(CHIResponseType:RespSepData) == false) { |
| error("Received unexpected message"); |
| } |
| if (is_HN == false) { |
| // must now ack the responder |
| tbe.actions.pushFrontNB(Event:SendCompAck); |
| } |
| } |
| |
| action(Receive_ReadReceipt, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.expected_req_resp.hasExpected()); |
| if (tbe.expected_req_resp.receiveResp(CHIResponseType:ReadReceipt) == false) { |
| error("Received unexpected message"); |
| } |
| } |
| |
| action(Receive_SnpDataResp, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.expected_snp_resp.hasExpected()); |
| peek(datInPort, CHIDataMsg) { |
| // Decrement pending |
| if (tbe.expected_snp_resp.receiveData(in_msg.type) == false) { |
| error("Received unexpected message"); |
| } |
| // Copy data to tbe only if we didn't have valid data or the received |
| // data is dirty |
| if ((tbe.dataBlkValid.isFull() == false) || |
| (in_msg.type == CHIDataType:SnpRespData_I_PD) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_PD) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_Fwded_SD_PD) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_PD_Fwded_SC) || |
| (in_msg.type == CHIDataType:SnpRespData_I_Fwded_SD_PD) || |
| (in_msg.type == CHIDataType:SnpRespData_I_PD_Fwded_SC)) { |
| // clear mask if started to receive new data |
| if(tbe.dataBlkValid.isFull()){ |
| tbe.dataBlkValid.clear(); |
| } |
| tbe.dataBlk.copyPartial(in_msg.dataBlk, in_msg.bitMask); |
| assert(tbe.dataBlkValid.isOverlap(in_msg.bitMask) == false); |
| tbe.dataBlkValid.orMask(in_msg.bitMask); |
| } |
| } |
| } |
| |
| action(UpdateDirState_FromReqDataResp, desc="") { |
| assert(is_valid(tbe)); |
| // only perform the update once we received all chunks |
| if (tbe.expected_req_resp.hasReceivedData()) { |
| assert(tbe.dataBlkValid.isFull()); |
| peek(datInPort, CHIDataMsg) { |
| |
| if (in_msg.type == CHIDataType:CBWrData_UC) { |
| assert(tbe.dir_ownerExists && tbe.dir_ownerIsExcl && (tbe.dir_owner == in_msg.responder)); |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| tbe.dir_ownerExists := false; |
| tbe.dir_ownerIsExcl := false; |
| tbe.dir_sharers.remove(in_msg.responder); |
| |
| } else if (in_msg.type == CHIDataType:CBWrData_UD_PD) { |
| assert(tbe.dir_ownerExists && tbe.dir_ownerIsExcl && (tbe.dir_owner == in_msg.responder)); |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| if (tbe.reqType != CHIRequestType:WriteCleanFull) { |
| tbe.dir_ownerExists := false; |
| tbe.dir_ownerIsExcl := false; |
| tbe.dir_sharers.remove(in_msg.responder); |
| } |
| |
| } else if (in_msg.type == CHIDataType:CBWrData_SC) { |
| assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != in_msg.responder)); |
| // Do not remove the responder in case of stale WriteCleanFull |
| if (tbe.reqType != CHIRequestType:WriteCleanFull) { |
| tbe.dir_sharers.remove(in_msg.responder); |
| } |
| |
| } else if (in_msg.type == CHIDataType:CBWrData_SD_PD) { |
| assert(tbe.dir_ownerExists && (tbe.dir_ownerIsExcl == false) && (tbe.dir_owner == in_msg.responder)); |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| tbe.dir_ownerExists := false; |
| tbe.dir_ownerIsExcl := false; |
| if (tbe.reqType != CHIRequestType:WriteCleanFull) { |
| tbe.dir_sharers.remove(in_msg.responder); |
| } |
| |
| } else if (in_msg.type == CHIDataType:CBWrData_I) { |
| // nothing to do here; just check |
| assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != in_msg.responder)); |
| assert(tbe.dir_sharers.isElement(in_msg.responder) == false); |
| |
| } else { |
| error("Unsuported data type"); |
| } |
| } |
| } |
| printTBEState(tbe); |
| } |
| |
| action(UpdateDirState_FromSnpDataResp, desc="") { |
| assert(is_valid(tbe)); |
| // only perform the update once we received all chunks |
| if (tbe.expected_snp_resp.hasReceivedData()) { |
| assert(tbe.dataBlkValid.isFull()); |
| peek(datInPort, CHIDataMsg) { |
| |
| if (in_msg.type == CHIDataType:SnpRespData_I) { |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| tbe.dir_ownerExists := false; |
| tbe.dir_ownerIsExcl := false; |
| tbe.dir_sharers.remove(in_msg.responder); |
| |
| } else if (in_msg.type == CHIDataType:SnpRespData_I_PD) { |
| assert(tbe.dir_ownerExists && (tbe.dir_owner == in_msg.responder)); |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| tbe.dir_ownerExists := false; |
| tbe.dir_ownerIsExcl := false; |
| tbe.dir_sharers.remove(in_msg.responder); |
| |
| } else if ((in_msg.type == CHIDataType:SnpRespData_SC_PD) || |
| (in_msg.type == CHIDataType:SnpRespData_SC) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_Fwded_SC) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_Fwded_SD_PD) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_PD_Fwded_SC)) { |
| // the owner must have been the responder, if there was one |
| assert((tbe.dir_ownerExists == false) || |
| (tbe.dir_ownerExists && (tbe.dir_owner == in_msg.responder))); |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| tbe.dir_ownerExists := false; |
| tbe.dir_ownerIsExcl := false; |
| if ((in_msg.type == CHIDataType:SnpRespData_SC_Fwded_SC) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_PD_Fwded_SC) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_Fwded_SD_PD)) { |
| tbe.dir_sharers.add(tbe.requestor); |
| } |
| if (in_msg.type == CHIDataType:SnpRespData_SC_Fwded_SD_PD) { |
| tbe.dir_ownerExists := true; |
| tbe.dir_owner := tbe.requestor; |
| } |
| |
| } else if ((in_msg.type == CHIDataType:SnpRespData_I_Fwded_SD_PD) || |
| (in_msg.type == CHIDataType:SnpRespData_I_PD_Fwded_SC) || |
| (in_msg.type == CHIDataType:SnpRespData_I_Fwded_SC)) { |
| // the owner must have been the responder, if there was one |
| assert((tbe.dir_ownerExists == false) || |
| (tbe.dir_ownerExists && (tbe.dir_owner == in_msg.responder))); |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| tbe.dir_ownerExists := false; |
| tbe.dir_ownerIsExcl := false; |
| tbe.dir_sharers.remove(in_msg.responder); |
| tbe.dir_sharers.add(tbe.requestor); |
| if (in_msg.type == CHIDataType:SnpRespData_I_Fwded_SD_PD) { |
| tbe.dir_ownerExists := true; |
| tbe.dir_owner := tbe.requestor; |
| } |
| |
| } else if ((in_msg.type == CHIDataType:SnpRespData_SD) || |
| (in_msg.type == CHIDataType:SnpRespData_UC) || |
| (in_msg.type == CHIDataType:SnpRespData_UD)) { |
| // expected only in response to a SnpOnce; just do some checks |
| // also may get SnpRespData_SC, but handled properly above |
| assert(tbe.dir_ownerExists && (tbe.dir_owner == in_msg.responder)); |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| |
| } else { |
| error("Unsuported data type"); |
| } |
| } |
| } |
| printTBEState(tbe); |
| } |
| |
| action(UpdateDataState_FromReqDataResp, desc="") { |
| assert(is_valid(tbe)); |
| // only perform the update once we received all chunks |
| if (tbe.expected_req_resp.hasReceivedData()) { |
| assert(tbe.dataBlkValid.isFull()); |
| peek(datInPort, CHIDataMsg) { |
| |
| if ((in_msg.type == CHIDataType:CompData_UC) || |
| (in_msg.type == CHIDataType:DataSepResp_UC)) { |
| assert(tbe.dataUnique == false); |
| assert((tbe.dataValid && tbe.dataDirty) == false); |
| tbe.dataDirty := false; |
| tbe.dataUnique := true; |
| tbe.dataValid := true; |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| |
| } else if (in_msg.type == CHIDataType:CompData_UD_PD) { |
| assert(tbe.dataUnique == false); |
| assert((tbe.dataValid && tbe.dataDirty) == false); |
| tbe.dataDirty := true; |
| tbe.dataUnique := true; |
| tbe.dataValid := true; |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| |
| } else if (in_msg.type == CHIDataType:CompData_SC) { |
| assert(tbe.dataUnique == false); |
| assert((tbe.dataValid && tbe.dataDirty) == false); |
| tbe.dataDirty := false; |
| tbe.dataUnique := false; |
| tbe.dataValid := true; |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| |
| } else if (in_msg.type == CHIDataType:CompData_SD_PD) { |
| assert(tbe.dataUnique == false); |
| assert((tbe.dataValid && tbe.dataDirty) == false); |
| tbe.dataDirty := true; |
| tbe.dataUnique := false; |
| tbe.dataValid := true; |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| |
| } else if (in_msg.type == CHIDataType:CompData_I) { |
| tbe.dataValid := true; |
| tbe.dataToBeInvalid := true; |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| |
| } else if (in_msg.type == CHIDataType:CBWrData_UC) { |
| assert(tbe.dataUnique); |
| tbe.dataMaybeDirtyUpstream := false; |
| tbe.dataValid := true; |
| |
| } else if (in_msg.type == CHIDataType:CBWrData_SC) { |
| // stale WB, nothing to do ?? |
| |
| } else if (in_msg.type == CHIDataType:CBWrData_UD_PD) { |
| assert(tbe.dataUnique); |
| tbe.dataDirty := true; |
| tbe.dataValid := true; |
| if (tbe.reqType == CHIRequestType:WriteCleanFull) { |
| // upstream data can still be UC if this is a WriteCleanFull |
| assert(tbe.dir_ownerExists && tbe.dir_ownerIsExcl); |
| tbe.dataMaybeDirtyUpstream := true; |
| } else { |
| tbe.dataMaybeDirtyUpstream := false; |
| } |
| |
| } else if (in_msg.type == CHIDataType:CBWrData_SD_PD) { |
| tbe.dataDirty := true; |
| tbe.dataValid := true; |
| tbe.dataMaybeDirtyUpstream := false; |
| |
| } else if (in_msg.type == CHIDataType:CBWrData_I) { |
| // stale WB, nothing to do ?? |
| |
| } else { |
| error("Unsuported data type"); |
| } |
| } |
| } |
| printTBEState(tbe); |
| } |
| |
| action(UpdateDataState_FromWUDataResp, desc="") { |
| assert(is_valid(tbe)); |
| if (tbe.expected_req_resp.hasReceivedData()) { |
| assert(tbe.dataBlkValid.test(addressOffset(tbe.accAddr, tbe.addr))); |
| assert(tbe.dataBlkValid.test(addressOffset(tbe.accAddr, tbe.addr) |
| + tbe.accSize - 1)); |
| peek(datInPort, CHIDataMsg) { |
| assert(in_msg.type == CHIDataType:NCBWrData); |
| tbe.dataDirty := true; |
| if (tbe.reqType == CHIRequestType:WriteUniquePtl) { |
| // we are just updating any valid data we already had |
| tbe.dataValid := tbe.dataValid || (tbe.accSize == blockSize); |
| } else { |
| tbe.dataValid := tbe.accSize == blockSize; |
| } |
| } |
| } |
| printTBEState(tbe); |
| } |
| |
| action(UpdateDataState_FromCUResp, desc="") { |
| assert(is_valid(tbe)); |
| peek(rspInPort, CHIResponseMsg) { |
| assert(in_msg.type == CHIResponseType:Comp_UC); |
| assert(tbe.dataUnique == false); |
| tbe.dataUnique := tbe.dataValid || (tbe.dir_sharers.count() > 0); |
| // self and upstream may have been invalidated while waiting for this |
| // expect to follow up with a ReadUnique |
| } |
| printTBEState(tbe); |
| } |
| |
| action(UpdateDataState_FromSnpDataResp, desc="") { |
| assert(is_valid(tbe)); |
| // only perform the update once we received all chunks |
| if (tbe.expected_snp_resp.hasReceivedData()) { |
| assert(tbe.dataBlkValid.isFull()); |
| peek(datInPort, CHIDataMsg) { |
| |
| if ((in_msg.type == CHIDataType:SnpRespData_I_PD) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_PD) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_PD_Fwded_SC) || |
| (in_msg.type == CHIDataType:SnpRespData_I_PD_Fwded_SC)) { |
| tbe.dataDirty := true; |
| tbe.dataValid := true; |
| tbe.dataMaybeDirtyUpstream := false; |
| |
| } else if ((in_msg.type == CHIDataType:SnpRespData_SD) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_Fwded_SD_PD) || |
| (in_msg.type == CHIDataType:SnpRespData_I_Fwded_SD_PD)) { |
| tbe.dataDirty := true; |
| tbe.dataValid := true; |
| tbe.dataMaybeDirtyUpstream := true; |
| |
| } else if ((in_msg.type == CHIDataType:SnpRespData_I) || |
| (in_msg.type == CHIDataType:SnpRespData_SC) || |
| (in_msg.type == CHIDataType:SnpRespData_SC_Fwded_SC) || |
| (in_msg.type == CHIDataType:SnpRespData_I_Fwded_SC)) { |
| tbe.dataValid := true; |
| tbe.dataMaybeDirtyUpstream := false; |
| |
| } else if ((in_msg.type == CHIDataType:SnpRespData_UC) || |
| (in_msg.type == CHIDataType:SnpRespData_UD)) { |
| tbe.dataValid := true; |
| tbe.dataUnique := true; |
| tbe.dataMaybeDirtyUpstream := true; |
| if (in_msg.type == CHIDataType:SnpRespData_UD){ |
| tbe.dataDirty := true; |
| } |
| |
| } else { |
| error("Unsuported data type"); |
| } |
| } |
| } |
| printTBEState(tbe); |
| } |
| |
| action(UpdateDirState_FromReqResp, desc="") { |
| peek(rspInPort, CHIResponseMsg) { |
| if ((in_msg.type == CHIResponseType:CompAck) && tbe.updateDirOnCompAck) { |
| assert(tbe.requestor == in_msg.responder); |
| |
| tbe.dir_sharers.add(in_msg.responder); |
| |
| if (tbe.requestorToBeOwner) { |
| assert(tbe.dataMaybeDirtyUpstream); |
| assert(tbe.dir_ownerExists == false); |
| assert(tbe.requestorToBeExclusiveOwner == false); |
| tbe.dir_owner := in_msg.responder; |
| tbe.dir_ownerExists := true; |
| tbe.dir_ownerIsExcl := false; |
| |
| } else if (tbe.requestorToBeExclusiveOwner) { |
| assert(tbe.dataMaybeDirtyUpstream); |
| assert(tbe.dir_ownerExists == false); |
| assert(tbe.dir_sharers.count() == 1); |
| tbe.dir_owner := in_msg.responder; |
| tbe.dir_ownerExists := true; |
| tbe.dir_ownerIsExcl := true; |
| } |
| } |
| } |
| printTBEState(tbe); |
| } |
| |
| action(UpdateDirState_FromSnpResp, desc="") { |
| peek(rspInPort, CHIResponseMsg) { |
| |
| if (in_msg.type == CHIResponseType:SnpResp_I) { |
| // must have been a known sharer otherwise we would receive data |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| tbe.dir_sharers.remove(in_msg.responder); |
| if (tbe.dir_ownerExists && (tbe.dir_owner == in_msg.responder)){ |
| tbe.dir_ownerExists := false; |
| } |
| |
| } else if (in_msg.type == CHIResponseType:SnpResp_SC) { |
| // expected from a sharer that already has it in shared state |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != in_msg.responder)); |
| |
| } else if ((in_msg.type == CHIResponseType:SnpResp_SC_Fwded_SC) || |
| (in_msg.type == CHIResponseType:SnpResp_SC_Fwded_SD_PD)) { |
| // the SnpSharedFwd must have been sent to the owner if there was one |
| assert((tbe.dir_ownerExists == false) || |
| (tbe.dir_ownerExists && (tbe.dir_owner == in_msg.responder))); |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| tbe.dir_ownerExists := false; |
| tbe.dir_ownerIsExcl := false; |
| tbe.dir_sharers.add(tbe.requestor); |
| if (in_msg.type == CHIResponseType:SnpResp_SC_Fwded_SD_PD) { |
| // Requestor is new owner |
| tbe.dir_ownerExists := true; |
| tbe.dir_owner := tbe.requestor; |
| } |
| |
| } else if ((in_msg.type == CHIResponseType:SnpResp_I_Fwded_UC) || |
| (in_msg.type == CHIResponseType:SnpResp_I_Fwded_UD_PD)) { |
| // must have been a single sharer that received SnpUniqueFwd |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| assert(tbe.dir_sharers.count() == 1); |
| tbe.dir_sharers.remove(in_msg.responder); |
| // requestor is the new owner |
| tbe.dir_sharers.add(tbe.requestor); |
| tbe.dir_ownerExists := true; |
| tbe.dir_ownerIsExcl := true; |
| tbe.dir_owner := tbe.requestor; |
| |
| } else if ((in_msg.type == CHIResponseType:SnpResp_UC_Fwded_I) || |
| (in_msg.type == CHIResponseType:SnpResp_UD_Fwded_I) || |
| (in_msg.type == CHIResponseType:SnpResp_SD_Fwded_I)) { |
| // SnpSharedFwd; just confirm |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| assert(tbe.dir_ownerExists && (tbe.dir_owner == in_msg.responder)); |
| |
| } else if (in_msg.type == CHIResponseType:SnpResp_SC_Fwded_I) { |
| // SnpSharedFwd; just confirm |
| assert(tbe.dir_sharers.isElement(in_msg.responder)); |
| assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != in_msg.responder)); |
| } |
| |
| tbe.dataMaybeDirtyUpstream := tbe.dir_ownerExists; |
| |
| } |
| printTBEState(tbe); |
| } |
| |
| action(Receive_ReqResp, desc="") { |
| assert(tbe.expected_req_resp.hasExpected()); |
| peek(rspInPort, CHIResponseMsg) { |
| // Decrement pending |
| if (tbe.expected_req_resp.receiveResp(in_msg.type) == false) { |
| error("Received unexpected message"); |
| } |
| assert(in_msg.stale == tbe.is_stale); |
| } |
| } |
| |
| action(Receive_ReqResp_WUNeedComp, desc="") { |
| tbe.defer_expected_comp := true; |
| } |
| |
| action(Receive_ReqResp_WUComp, desc="") { |
| if (tbe.defer_expected_comp) { |
| tbe.defer_expected_comp := false; |
| } else if (tbe.expected_req_resp.receiveResp(CHIResponseType:Comp) == false) { |
| error("Received unexpected message"); |
| } |
| } |
| |
| action(Receive_SnpResp, desc="") { |
| assert(tbe.expected_snp_resp.hasExpected()); |
| peek(rspInPort, CHIResponseMsg) { |
| // Decrement pending |
| if (tbe.expected_snp_resp.receiveResp(in_msg.type) == false) { |
| error("Received unexpected message"); |
| } |
| assert(in_msg.stale == tbe.is_stale); |
| } |
| } |
| |
| action(Receive_RetryAck, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.pendReqAllowRetry); |
| assert(tbe.rcvdRetryAck == false); |
| tbe.rcvdRetryAck := true; |
| destsWaitingRetry.addNetDest(tbe.pendReqDest); |
| enqueueDoRetry(tbe); |
| } |
| |
| action(Receive_PCrdGrant, desc="") { |
| assert(tbe.pendReqAllowRetry); |
| assert(tbe.rcvdRetryCredit == false); |
| tbe.rcvdRetryCredit := true; |
| enqueueDoRetry(tbe); |
| } |
| |
| action(Send_Retry, desc="") { |
| assert(tbe.pendReqAllowRetry); |
| assert(tbe.rcvdRetryCredit); |
| assert(tbe.rcvdRetryAck); |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequestRetry(tbe, out_msg); |
| } |
| } |
| |
| action(Send_Retry_DVM, desc="") { |
| assert(tbe.pendReqAllowRetry); |
| assert(tbe.rcvdRetryCredit); |
| assert(tbe.rcvdRetryAck); |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequestRetryDVM(tbe, out_msg); |
| } |
| destsWaitingRetry.removeNetDest(tbe.pendReqDest); |
| } |
| |
| action(Receive_RetryAck_Hazard, desc="") { |
| TBE hazard_tbe := getHazardTBE(tbe); |
| assert(hazard_tbe.pendReqAllowRetry); |
| assert(hazard_tbe.rcvdRetryAck == false); |
| hazard_tbe.rcvdRetryAck := true; |
| destsWaitingRetry.addNetDest(hazard_tbe.pendReqDest); |
| enqueueDoRetry(hazard_tbe); |
| } |
| |
| action(Receive_PCrdGrant_Hazard, desc="") { |
| TBE hazard_tbe := getHazardTBE(tbe); |
| assert(hazard_tbe.pendReqAllowRetry); |
| assert(hazard_tbe.rcvdRetryCredit == false); |
| hazard_tbe.rcvdRetryCredit := true; |
| enqueueDoRetry(hazard_tbe); |
| } |
| |
| action(Send_Retry_Hazard, desc="") { |
| TBE hazard_tbe := getHazardTBE(tbe); |
| assert(hazard_tbe.pendReqAllowRetry); |
| assert(hazard_tbe.rcvdRetryCredit); |
| assert(hazard_tbe.rcvdRetryAck); |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequestRetry(hazard_tbe, out_msg); |
| } |
| } |
| |
| action(Send_CompData, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.dataValid); |
| |
| bool is_rd_once := tbe.reqType == CHIRequestType:ReadOnce; |
| bool is_rd_shared := (tbe.reqType == CHIRequestType:ReadShared) || |
| (tbe.reqType == CHIRequestType:ReadNotSharedDirty); |
| bool is_rd_nsd := tbe.reqType == CHIRequestType:ReadNotSharedDirty; |
| bool is_rd_unique := tbe.reqType == CHIRequestType:ReadUnique; |
| |
| // if the config allows (or not caching the data) and line has no sharers |
| bool snd_unique_on_rs := (fwd_unique_on_readshared || tbe.dataToBeInvalid) |
| && tbe.dataUnique && tbe.dir_sharers.isEmpty(); |
| // if the request type allows and we won't be caching the data |
| bool snd_dirty_on_rs := is_rd_shared && !is_rd_nsd && tbe.dataToBeInvalid; |
| |
| if (is_rd_once) { |
| tbe.snd_msgType := CHIDataType:CompData_I; |
| } else if (is_rd_unique || (is_rd_shared && snd_unique_on_rs)) { |
| assert(tbe.dataUnique); |
| if (tbe.dataDirty) { |
| tbe.snd_msgType := CHIDataType:CompData_UD_PD; |
| } else { |
| tbe.snd_msgType := CHIDataType:CompData_UC; |
| } |
| } else if (is_rd_shared) { |
| if (tbe.dataDirty && snd_dirty_on_rs) { |
| tbe.snd_msgType := CHIDataType:CompData_SD_PD; |
| } else { |
| // notice the MaintainCoherence will send WriteClean if the line |
| // is dirty and we won't be caching the data |
| tbe.snd_msgType := CHIDataType:CompData_SC; |
| } |
| } else { |
| error("Invalid request type"); |
| } |
| |
| tbe.dataMaybeDirtyUpstream := tbe.dataMaybeDirtyUpstream || |
| (tbe.snd_msgType == CHIDataType:CompData_UD_PD) || |
| (tbe.snd_msgType == CHIDataType:CompData_SD_PD) || |
| (tbe.snd_msgType == CHIDataType:CompData_UC); |
| tbe.requestorToBeExclusiveOwner := tbe.requestorToBeExclusiveOwner || |
| (tbe.snd_msgType == CHIDataType:CompData_UD_PD) || |
| (tbe.snd_msgType == CHIDataType:CompData_UC); |
| tbe.requestorToBeOwner := tbe.requestorToBeOwner || |
| (tbe.snd_msgType == CHIDataType:CompData_SD_PD); |
| |
| tbe.snd_destination := tbe.requestor; |
| setupPendingSend(tbe); |
| printTBEState(tbe); |
| } |
| |
| action(Send_WBData, desc="") { |
| assert(is_valid(tbe)); |
| if (is_HN) { |
| assert(tbe.dataBlkValid.isFull()); |
| assert(tbe.dataDirty); |
| assert(tbe.dataValid); |
| tbe.snd_msgType := CHIDataType:NCBWrData; |
| } else { |
| if (tbe.dataValid == false) { |
| // only possible when the WB was made stale by a snoop |
| assert(tbe.is_stale); |
| tbe.dataBlkValid.fillMask(); |
| tbe.snd_msgType := CHIDataType:CBWrData_I; |
| } else if (tbe.dataUnique) { |
| assert(tbe.dataBlkValid.isFull()); |
| if (tbe.dataDirty) { |
| tbe.snd_msgType := CHIDataType:CBWrData_UD_PD; |
| } else { |
| tbe.snd_msgType := CHIDataType:CBWrData_UC; |
| } |
| } else { |
| assert(tbe.dataBlkValid.isFull()); |
| if (tbe.dataDirty) { |
| tbe.snd_msgType := CHIDataType:CBWrData_SD_PD; |
| } else { |
| tbe.snd_msgType := CHIDataType:CBWrData_SC; |
| } |
| } |
| } |
| tbe.snd_destination := mapAddressToDownstreamMachine(tbe.addr); |
| setupPendingSend(tbe); |
| } |
| |
| action(Send_WUData, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.dataBlkValid.count() > 0); |
| tbe.snd_msgType := CHIDataType:NCBWrData; |
| tbe.snd_destination := mapAddressToDownstreamMachine(tbe.addr); |
| setupPendingPartialSend(tbe); |
| } |
| |
| action(CheckWUComp, desc="") { |
| assert(is_valid(tbe)); |
| if (tbe.defer_expected_comp) { |
| tbe.defer_expected_comp := false; |
| tbe.expected_req_resp.addExpectedCount(1); |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:Comp); |
| } |
| } |
| |
| action(Send_SnpRespData, desc="") { |
| assert(is_HN == false); |
| assert(is_valid(tbe)); |
| assert(tbe.dataBlkValid.isFull()); |
| assert(tbe.dataValid); |
| |
| assert(tbe.snpNeedsData || |
| (tbe.dataDirty && (tbe.reqType == CHIRequestType:SnpCleanInvalid)) || |
| ((tbe.dataDirty || tbe.dataUnique) && (tbe.reqType == CHIRequestType:SnpShared)) || |
| ((tbe.dataDirty || tbe.dataUnique) && (tbe.reqType == CHIRequestType:SnpUnique))); |
| |
| if (tbe.dataToBeInvalid) { |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| if (tbe.dataDirty) { |
| tbe.snd_msgType := CHIDataType:SnpRespData_I_PD; |
| } else { |
| tbe.snd_msgType := CHIDataType:SnpRespData_I; |
| } |
| } else if (tbe.dataToBeSharedClean) { |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| if (tbe.dataDirty) { |
| tbe.snd_msgType := CHIDataType:SnpRespData_SC_PD; |
| } else { |
| tbe.snd_msgType := CHIDataType:SnpRespData_SC; |
| } |
| } else { |
| assert(tbe.reqType == CHIRequestType:SnpOnce); |
| if (tbe.dataDirty && tbe.dataUnique) { |
| tbe.snd_msgType := CHIDataType:SnpRespData_UD; |
| } else if (tbe.dataDirty) { |
| tbe.snd_msgType := CHIDataType:SnpRespData_SD; |
| } else if (tbe.dataUnique) { |
| tbe.snd_msgType := CHIDataType:SnpRespData_UC; |
| } else { |
| tbe.snd_msgType := CHIDataType:SnpRespData_SC; |
| } |
| } |
| |
| tbe.snd_destination := tbe.requestor; |
| setupPendingSend(tbe); |
| } |
| |
| action(Send_CompData_SnpUniqueFwd, desc="") { |
| assert(tbe.dataValid); |
| assert(tbe.dataToBeInvalid); |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| |
| if (tbe.dataDirty) { |
| tbe.fwdedState := State:UD; |
| tbe.snd_msgType := CHIDataType:CompData_UD_PD; |
| } else { |
| tbe.fwdedState := State:UC; |
| tbe.snd_msgType := CHIDataType:CompData_UC; |
| } |
| tbe.actions.pushFront(Event:SendSnpFwdedResp); |
| |
| tbe.snd_destination := tbe.fwdRequestor; |
| setupPendingSend(tbe); |
| } |
| |
| action(Send_CompData_SnpSharedFwd, desc="") { |
| assert(tbe.dataValid); |
| assert(tbe.dataToBeSharedClean); |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| |
| if (tbe.dataDirty) { |
| tbe.fwdedState := State:SD; |
| tbe.snd_msgType := CHIDataType:CompData_SD_PD; |
| } else { |
| tbe.fwdedState := State:SC; |
| tbe.snd_msgType := CHIDataType:CompData_SC; |
| } |
| if (tbe.snpNeedsData) { |
| tbe.actions.pushFront(Event:SendSnpFwdedData); |
| } else { |
| tbe.actions.pushFront(Event:SendSnpFwdedResp); |
| } |
| |
| tbe.snd_destination := tbe.fwdRequestor; |
| setupPendingSend(tbe); |
| } |
| |
| action(Send_CompData_SnpNSDFwd, desc="") { |
| assert(tbe.dataValid); |
| assert(tbe.dataToBeSharedClean); |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| |
| tbe.snd_msgType := CHIDataType:CompData_SC; |
| tbe.fwdedState := State:SC; |
| if (tbe.dataDirty || tbe.snpNeedsData) { |
| tbe.actions.pushFront(Event:SendSnpFwdedData); |
| } else { |
| tbe.actions.pushFront(Event:SendSnpFwdedResp); |
| } |
| |
| tbe.snd_destination := tbe.fwdRequestor; |
| setupPendingSend(tbe); |
| } |
| |
| action(Send_CompData_SnpOnceFwd, desc="") { |
| assert(tbe.dataValid); |
| |
| tbe.fwdedState := State:I; |
| tbe.snd_msgType := CHIDataType:CompData_I; |
| tbe.actions.pushFront(Event:SendSnpFwdedResp); |
| |
| tbe.snd_destination := tbe.fwdRequestor; |
| setupPendingSend(tbe); |
| } |
| |
| action(Send_SnpRespDataFwded, desc="") { |
| assert(tbe.dataValid); |
| |
| // right only using this for the SnpShared/SnpNSD, so check |
| assert(tbe.dataToBeSharedClean); |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| |
| // We have the data (locally or upstream) or are dropping it |
| bool keepData := (tbe.dir_sharers.count() > 0) || |
| (tbe.dataToBeInvalid == false); |
| |
| if (keepData) { |
| if (tbe.fwdedState == State:SD) { |
| tbe.snd_msgType := CHIDataType:SnpRespData_SC_Fwded_SD_PD; |
| } else if (tbe.dataDirty && (tbe.fwdedState == State:SC)) { |
| tbe.snd_msgType := CHIDataType:SnpRespData_SC_PD_Fwded_SC; |
| } else { |
| tbe.snd_msgType := CHIDataType:SnpRespData_SC_Fwded_SC; |
| } |
| } else { |
| if (tbe.fwdedState == State:SD) { |
| tbe.snd_msgType := CHIDataType:SnpRespData_I_Fwded_SD_PD; |
| } else if (tbe.dataDirty && (tbe.fwdedState == State:SC)) { |
| tbe.snd_msgType := CHIDataType:SnpRespData_I_PD_Fwded_SC; |
| } else { |
| tbe.snd_msgType := CHIDataType:SnpRespData_I_Fwded_SC; |
| } |
| } |
| |
| tbe.snd_destination := tbe.requestor; |
| setupPendingSend(tbe); |
| } |
| |
| action(Send_FwdSnpResp, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.dataValid); |
| |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| |
| // We have the data (locally or upstream) or are dropping it |
| bool keepData := (tbe.dir_sharers.count() > 0) || |
| (tbe.dataToBeInvalid == false); |
| |
| if (keepData && tbe.dataToBeSharedClean) { |
| assert((tbe.reqType == CHIRequestType:SnpSharedFwd) || |
| (tbe.reqType == CHIRequestType:SnpNotSharedDirtyFwd)); |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| if (tbe.fwdedState == State:SD) { |
| out_msg.type := CHIResponseType:SnpResp_SC_Fwded_SD_PD; |
| } else { |
| assert(tbe.fwdedState == State:SC); |
| out_msg.type := CHIResponseType:SnpResp_SC_Fwded_SC; |
| } |
| |
| } else if (keepData) { |
| assert(tbe.reqType == CHIRequestType:SnpOnceFwd); |
| assert(tbe.fwdedState == State:I); |
| if (tbe.dataUnique && (tbe.dataDirty || tbe.dataMaybeDirtyUpstream)) { |
| out_msg.type := CHIResponseType:SnpResp_UD_Fwded_I; |
| } else if (tbe.dataUnique) { |
| out_msg.type := CHIResponseType:SnpResp_UC_Fwded_I; |
| } else if (tbe.dataDirty || tbe.dataMaybeDirtyUpstream) { |
| out_msg.type := CHIResponseType:SnpResp_SD_Fwded_I; |
| } else { |
| out_msg.type := CHIResponseType:SnpResp_SC_Fwded_I; |
| } |
| |
| } else { |
| assert(tbe.reqType == CHIRequestType:SnpUniqueFwd); |
| assert(tbe.dataMaybeDirtyUpstream == false); |
| if (tbe.fwdedState == State:UD) { |
| out_msg.type := CHIResponseType:SnpResp_I_Fwded_UD_PD; |
| } else { |
| assert(tbe.fwdedState == State:UC); |
| out_msg.type := CHIResponseType:SnpResp_I_Fwded_UC; |
| } |
| } |
| } |
| } |
| |
| action(Send_Data, desc="") { |
| assert(tbe.snd_pendEv); |
| assert(tbe.snd_pendBytes.count() > 0); |
| tbe.snd_pendEv := false; |
| enqueue(datOutPort, CHIDataMsg, data_latency) { |
| out_msg.addr := tbe.addr; |
| out_msg.type := tbe.snd_msgType; |
| |
| int offset := tbe.snd_pendBytes.firstBitSet(true); |
| assert(offset < blockSize); |
| int range := tbe.snd_pendBytes.firstBitSet(false, offset) - offset; |
| assert((range > 0) && (range <= blockSize)); |
| if (range > data_channel_size) { |
| range := data_channel_size; |
| } |
| tbe.snd_pendBytes.setMask(offset, range, false); |
| |
| out_msg.dataBlk := tbe.dataBlk; |
| out_msg.bitMask.setMask(offset, range); |
| |
| out_msg.responder := machineID; |
| |
| out_msg.Destination.add(tbe.snd_destination); |
| } |
| |
| // send next chunk (if any) next cycle |
| scheduleSendData(tbe, 1); |
| } |
| |
| action(Send_RespSepData, desc="") { |
| assert(is_valid(tbe)); |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.type := CHIResponseType:RespSepData; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| } |
| } |
| |
| action(Send_CompI, desc="") { |
| assert(is_valid(tbe)); |
| |
| // Used to ack Evict request |
| assert(tbe.dir_sharers.isElement(tbe.requestor)); |
| assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != tbe.requestor)); |
| |
| tbe.dir_sharers.remove(tbe.requestor); |
| |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.type := CHIResponseType:Comp_I; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| } |
| } |
| |
| action(Send_CompUC, desc="") { |
| assert(is_valid(tbe)); |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.type := CHIResponseType:Comp_UC; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| } |
| } |
| |
| action(Send_CompUC_Stale, desc="") { |
| assert(is_valid(tbe)); |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.type := CHIResponseType:Comp_UC; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| // We don't know if this is a stale clean unique or a bug, so flag the |
| // reponse so the requestor can make further checks |
| out_msg.stale := true; |
| } |
| } |
| |
| action(Send_CompAck, desc="") { |
| assert(is_valid(tbe)); |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.type := CHIResponseType:CompAck; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); |
| } |
| } |
| |
| action(Send_CompI_Stale, desc="") { |
| assert(is_valid(tbe)); |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.type := CHIResponseType:Comp_I; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| // We don't know if this is a stale writeback or a bug, so flag the |
| // reponse so the requestor can make further checks |
| out_msg.stale := true; |
| } |
| } |
| |
| action(Send_CompDBIDResp, desc="") { |
| assert(is_valid(tbe)); |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.type := CHIResponseType:CompDBIDResp; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| } |
| } |
| |
| action(Send_CompDBIDResp_Stale, desc="") { |
| assert(is_valid(tbe)); |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.type := CHIResponseType:CompDBIDResp; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| // We don't know if this is a stale writeback or a bug, so flag the |
| // reponse so the requestor can make further checks |
| out_msg.stale := true; |
| } |
| } |
| |
| action(Send_DBIDResp, desc="") { |
| assert(is_valid(tbe)); |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| out_msg.type := CHIResponseType:DBIDResp; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| } |
| } |
| |
| action(Send_Comp_WU, desc="") { |
| assert(is_valid(tbe)); |
| enqueue(rspOutPort, CHIResponseMsg, comp_wu_latency + response_latency) { |
| out_msg.addr := address; |
| out_msg.type := CHIResponseType:Comp; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| } |
| } |
| |
| action(Send_SnpRespI, desc="") { |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := address; |
| if (tbe.is_dvm_tbe || tbe.is_dvm_snp_tbe) { |
| out_msg.usesTxnId := true; |
| out_msg.txnId := tbe.addr; |
| } |
| out_msg.type := CHIResponseType:SnpResp_I; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(tbe.requestor); |
| } |
| } |
| |
| action(Send_RetryAck, desc="") { |
| peek(retryTriggerInPort, RetryTriggerMsg) { |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := in_msg.addr; |
| out_msg.usesTxnId := in_msg.usesTxnId; |
| out_msg.type := CHIResponseType:RetryAck; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(in_msg.retryDest); |
| } |
| } |
| } |
| |
| action(Send_PCrdGrant, desc="") { |
| peek(retryTriggerInPort, RetryTriggerMsg) { |
| enqueue(rspOutPort, CHIResponseMsg, response_latency) { |
| out_msg.addr := in_msg.addr; |
| out_msg.usesTxnId := in_msg.usesTxnId; |
| out_msg.type := CHIResponseType:PCrdGrant; |
| out_msg.responder := machineID; |
| out_msg.Destination.add(in_msg.retryDest); |
| } |
| } |
| } |
| |
| // Note on CheckUpgrade_FromStoreOrRU/CheckUpgrade_FromCU |
| // We will always get Comp_UC; but if our data is invalidated before |
| // Comp_UC we would need to go to UCE. Since we don't use the UCE state |
| // we remain in the transient state and follow-up with ReadUnique. |
| // Note this assumes the responder knows we have invalid data when sending |
| // us Comp_UC and does not register us as owner. |
| |
| action(CheckUpgrade_FromStoreOrRU, desc="") { |
| assert(is_HN == false); |
| if (tbe.dataUnique) { |
| // success, just send CompAck next |
| assert(tbe.dataValid); |
| } else { |
| // will need to get data instead |
| tbe.actions.pushFront(Event:SendReadUnique); |
| // we must have received an invalidation snoop that marked |
| // the req as stale |
| assert(tbe.is_stale); |
| tbe.is_stale := false; |
| } |
| tbe.actions.pushFront(Event:SendCompAck); |
| } |
| |
| action(CheckUpgrade_FromCU, desc="") { |
| assert(is_HN == false); |
| if (tbe.dataUnique == false) { |
| // actually failed, so just cancel the directory update |
| assert(tbe.dir_sharers.isElement(tbe.requestor) == false); |
| tbe.requestorToBeExclusiveOwner := false; |
| tbe.updateDirOnCompAck := false; |
| } |
| // otherwise nothing else to do here other than acking the CleanUnique |
| tbe.actions.pushFront(Event:SendCompAck); |
| } |
| |
| |
| action(Finalize_UpdateCacheFromTBE, desc="") { |
| assert(is_valid(tbe)); |
| State final := tbe.finalState; |
| if ((final == State:UD_RSC) || (final == State:SD_RSC) || (final == State:UC_RSC) || |
| (final == State:SC_RSC) || (final == State:UD) || (final == State:UD_T) || |
| (final == State:SD) || (final == State:UC) || (final == State:SC) || |
| (final == State:UC_RU) || (final == State:UD_RU) || (final == State:UD_RSD) || |
| (final == State:SD_RSD)) { |
| assert(tbe.dataBlkValid.isFull()); |
| assert(tbe.dataValid); |
| assert(is_valid(cache_entry)); |
| cache_entry.DataBlk := tbe.dataBlk; |
| DPRINTF(RubySlicc, "Cached data %s pfb %s\n", tbe.dataBlk, cache_entry.HWPrefetched); |
| } else { |
| // make sure only deallocate the cache entry if data is invalid |
| assert(tbe.dataValid == false); |
| if (is_valid(cache_entry)) { |
| cache.deallocate(address); |
| unset_cache_entry(); |
| } |
| } |
| } |
| |
| action(Finalize_UpdateDirectoryFromTBE, desc="") { |
| assert(is_valid(tbe)); |
| State final := tbe.finalState; |
| if ((final == State:UD_RSC) || (final == State:SD_RSC) || (final == State:UC_RSC) || |
| (final == State:SC_RSC) || (final == State:UC_RU) || (final == State:UD_RU) || |
| (final == State:UD_RSD) || (final == State:SD_RSD) || (final == State:RU) || |
| (final == State:RSC) || (final == State:RSD) || (final == State:RUSD) || |
| (final == State:RUSC)) { |
| DirEntry dir_entry := getDirEntry(address); |
| assert(is_valid(dir_entry)); |
| assert(tbe.dir_sharers.count() > 0); |
| dir_entry.ownerExists := tbe.dir_ownerExists; |
| dir_entry.ownerIsExcl := tbe.dir_ownerIsExcl; |
| dir_entry.owner := tbe.dir_owner; |
| dir_entry.sharers := tbe.dir_sharers; |
| } else { |
| assert((tbe.dir_ownerExists == false) && tbe.dir_sharers.isEmpty()); |
| if(directory.isTagPresent(address)) { |
| directory.deallocate(address); |
| } |
| } |
| } |
| |
| action(Deallocate_CacheBlock, desc="") { |
| assert(is_valid(cache_entry)); |
| cache.deallocate(address); |
| unset_cache_entry(); |
| } |
| |
| action(Allocate_DirEntry, desc="") { |
| assert(directory.isTagPresent(address) == false); |
| directory.allocate(address); |
| } |
| |
| action(Deallocate_DirEntry, desc="") { |
| assert(directory.isTagPresent(address)); |
| directory.deallocate(address); |
| } |
| |
| action(CheckCacheFill, desc="") { |
| assert(is_valid(tbe)); |
| |
| // only perform the write if we have valid data and need to write |
| bool need_fill := tbe.dataValid && (tbe.dataToBeInvalid == false) && tbe.doCacheFill; |
| bool execute_next := true; |
| |
| if (need_fill && is_valid(cache_entry)) { |
| // can write |
| tbe.actions.pushFront(Event:DataArrayWrite); |
| tbe.actions.pushFront(Event:FillPipe); |
| |
| } else if (need_fill && cache.cacheAvail(address)) { |
| // don't have a cache block, but there is space to allocate one |
| set_cache_entry(cache.allocate(address, new CacheEntry)); |
| tbe.actions.pushFront(Event:DataArrayWriteOnFill); |
| tbe.actions.pushFront(Event:FillPipe); |
| |
| } else if (need_fill) { |
| // performs a cache block replacement. CheckCacheFill executes again |
| // after the replacement |
| execute_next := false; |
| |
| // pick a victim to deallocate |
| Addr victim_addr := cache.cacheProbe(address); |
| CacheEntry victim_entry := getCacheEntry(victim_addr); |
| assert(is_valid(victim_entry)); |
| TBE victim_tbe := getCurrentActiveTBE(victim_addr); |
| |
| // The `is_valid(victim_entry)` condition here is to avoid an unused |
| // variable error when compiling to gem5.fast. |
| if (is_invalid(victim_tbe) && is_valid(victim_entry)) { |
| DPRINTF(RubySlicc, "Eviction for %#x victim: %#x state=%s\n", |
| address, victim_addr, victim_entry.state); |
| enqueue(replTriggerOutPort, ReplacementMsg, 0) { |
| out_msg.addr := victim_addr; |
| out_msg.from_addr := address; |
| if (unify_repl_TBEs) { |
| out_msg.slot := tbe.storSlot; |
| DPRINTF(RubySlicc, "Reusing slot %d\n", out_msg.slot); |
| } |
| } |
| } else { |
| DPRINTF(RubySlicc, "Eviction for %#x victim: %#x state=%s\n", |
| address, victim_addr, victim_tbe.state); |
| // just wait until the transaction finishes to try again |
| victim_tbe.wakeup_pending_tgr := true; |
| } |
| |
| // wait until we can deallocate the victim_addr |
| stall_and_wait(triggerInPort, victim_addr); |
| } |
| |
| // only do the usual Pop_TriggerQueue+ProcessNextState if we have a block |
| if (execute_next) { |
| triggerInPort.dequeue(clockEdge()); |
| clearPendingAction(tbe); |
| processNextState(address, tbe, cache_entry); |
| } else { |
| wakeupPendingSnps(tbe); // might have stalled snoops that can execute now |
| } |
| } |
| |
| |
| action(Finalize_DeallocateRequest, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.actions.empty()); |
| wakeupPendingReqs(tbe); |
| wakeupPendingSnps(tbe); |
| wakeupPendingTgrs(tbe); |
| |
| if (tbe.is_req_tbe) { |
| deallocateReqTBE(tbe); |
| processRetryQueue(); |
| |
| } else if (tbe.is_snp_tbe) { |
| deallocateSnpTBE(tbe); |
| |
| } else { |
| deallocateReplacementTBE(tbe); |
| if (unify_repl_TBEs) { |
| processRetryQueue(); |
| } |
| } |
| unset_tbe(); |
| |
| incomingTransactionEnd(address, curTransitionNextState()); |
| } |
| |
| action(Finalize_DeallocateDvmRequest, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.actions.empty()); |
| wakeupPendingReqs(tbe); |
| wakeupPendingSnps(tbe); |
| wakeupPendingTgrs(tbe); |
| |
| // Don't call processRetryQueue() because DVM ops don't interact with the retry queue |
| |
| assert(tbe.is_dvm_tbe); |
| deallocateDvmTBE(tbe); |
| unset_tbe(); |
| } |
| |
| action(Finalize_DeallocateDvmSnoop, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.actions.empty()); |
| wakeupPendingReqs(tbe); |
| wakeupPendingSnps(tbe); |
| wakeupPendingTgrs(tbe); |
| |
| // Don't call processRetryQueue() because DVM ops don't interact with the retry queue |
| |
| assert(tbe.is_dvm_snp_tbe); |
| deallocateDvmSnoopTBE(tbe); |
| unset_tbe(); |
| |
| // Last argument = false, so it uses a "unique ID" rather than an address |
| incomingTransactionEnd(address, curTransitionNextState(), false); |
| } |
| |
| action(Pop_ReqRdyQueue, desc="") { |
| reqRdyPort.dequeue(clockEdge()); |
| } |
| |
| action(Pop_RespInQueue, desc="") { |
| rspInPort.dequeue(clockEdge()); |
| } |
| |
| action(Pop_SnoopRdyQueue, desc="") { |
| snpRdyPort.dequeue(clockEdge()); |
| } |
| |
| action(Pop_DataInQueue, desc="") { |
| datInPort.dequeue(clockEdge()); |
| } |
| |
| // NOTICE a trigger event may wakeup another stalled trigger event so |
| // this is always called first in the transitions so we don't pop the |
| // wrong message |
| action(Pop_TriggerQueue, desc="") { |
| triggerInPort.dequeue(clockEdge()); |
| } |
| |
| action(Pop_ReplTriggerQueue, desc="") { |
| replTriggerInPort.dequeue(clockEdge()); |
| // wakeup the transaction that triggered this eviction |
| wakeup_port(triggerInPort, address); |
| } |
| |
| action(Pop_RetryTriggerQueue, desc="") { |
| retryTriggerInPort.dequeue(clockEdge()); |
| } |
| |
| action(Pop_SnpInPort, desc="") { |
| snpInPort.dequeue(clockEdge()); |
| } |
| action(Pop_SeqInPort, desc="") { |
| seqInPort.dequeue(clockEdge()); |
| } |
| |
| action(ProcessNextState, desc="") { |
| assert(is_valid(tbe)); |
| processNextState(address, tbe, cache_entry); |
| } |
| |
| action(ProcessNextState_ClearPending, desc="") { |
| assert(is_valid(tbe)); |
| clearPendingAction(tbe); |
| processNextState(address, tbe, cache_entry); |
| } |
| |
| action(Callback_LoadHit, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.reqType == CHIRequestType:Load); |
| if (tbe.is_local_pf == false) { |
| assert(tbe.dataValid); |
| DPRINTF(RubySlicc, "Read data %s\n", tbe.dataBlk); |
| sequencer.readCallback(tbe.addr, tbe.dataBlk, false); |
| } |
| } |
| |
| action(Callback_StoreHit, desc="") { |
| assert(is_valid(tbe)); |
| assert((tbe.reqType == CHIRequestType:StoreLine) || |
| (tbe.reqType == CHIRequestType:Store)); |
| if (tbe.is_local_pf == false) { |
| assert(tbe.dataValid); |
| DPRINTF(RubySlicc, "Write before %s\n", tbe.dataBlk); |
| sequencer.writeCallback(tbe.addr, tbe.dataBlk, false); |
| DPRINTF(RubySlicc, "Write after %s\n", tbe.dataBlk); |
| tbe.dataDirty := true; |
| } |
| } |
| |
| action(Callback_ExpressPrefetchHit, desc="") { |
| // have not allocated TBE, but must clear the reservation |
| assert(is_invalid(tbe)); |
| storTBEs.decrementReserved(); |
| assert(storTBEs.areNSlotsAvailable(1)); |
| assert(use_prefetcher); |
| |
| cache.profilePrefetchHit(); |
| peek(reqRdyPort, CHIRequestMsg) { |
| assert(in_msg.is_local_pf); |
| notifyPfComplete(in_msg.addr); |
| } |
| } |
| |
| // This is called everytime a data message is received but only goes |
| // though once all the blocks are present (tbe.dataValid) |
| // NOTE: should create a separate trigger for this callback ? |
| action(Callback_Miss, desc="") { |
| assert(is_valid(tbe)); |
| if (tbe.dataValid && tbe.is_local_pf) { |
| assert(use_prefetcher); |
| notifyPfComplete(tbe.addr); |
| |
| } else if (tbe.dataValid && (tbe.reqType == CHIRequestType:Load)) { |
| DPRINTF(RubySlicc, "Read data %s\n", tbe.dataBlk); |
| sequencer.readCallback(tbe.addr, tbe.dataBlk, true); |
| |
| } else if (tbe.dataValid && ((tbe.reqType == CHIRequestType:Store) || |
| (tbe.reqType == CHIRequestType:StoreLine))) { |
| DPRINTF(RubySlicc, "Write before %s\n", tbe.dataBlk); |
| sequencer.writeCallback(tbe.addr, tbe.dataBlk, true); |
| DPRINTF(RubySlicc, "Write after %s\n", tbe.dataBlk); |
| tbe.dataDirty := true; |
| |
| // sets a use time out for store misses to prevent LL/SC livelocks |
| int use_timeout_latency := scLockLatency(); |
| if (use_timeout_latency > 0) { |
| if (tbe.hasUseTimeout) { |
| assert(useTimerTable.isSet(tbe.addr)); |
| } else { |
| useTimerTable.set( |
| tbe.addr, |
| clockEdge() + cyclesToTicks(intToCycles(use_timeout_latency))); |
| tbe.hasUseTimeout := true; |
| } |
| // also decay the timeout |
| scLockDecayLatency(); |
| } |
| } |
| } |
| |
| action(Unset_Timeout_TBE, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.hasUseTimeout); |
| assert(useTimerTable.isSet(tbe.addr)); |
| useTimerTable.unset(tbe.addr); |
| tbe.hasUseTimeout := false; |
| // A snoop may have been stalled without setting the TBE flag |
| wakeup_port(snpRdyPort, address); |
| } |
| |
| action(Unset_Timeout_Cache, desc="") { |
| assert(useTimerTable.isSet(address)); |
| useTimerTable.unset(address); |
| wakeup_port(snpRdyPort, address); |
| } |
| |
| action(Callback_WriteUnique, desc="") { |
| assert(is_valid(tbe)); |
| assert((tbe.is_local_pf || tbe.is_remote_pf) == false); |
| assert((tbe.reqType == CHIRequestType:StoreLine) || |
| (tbe.reqType == CHIRequestType:Store)); |
| assert(tbe.dataValid == false); |
| sequencer.writeUniqueCallback(tbe.addr, tbe.dataBlk); |
| DPRINTF(RubySlicc, "WriteUnique data %s\n", tbe.dataBlk); |
| // set mask; note data is never considered valid |
| assert(tbe.dataBlkValid.isEmpty()); |
| tbe.dataBlkValid.setMask(addressOffset(tbe.accAddr, tbe.addr), tbe.accSize); |
| } |
| |
| action(Profile_Miss, desc="") { |
| assert(is_valid(tbe)); |
| bool is_demand := (tbe.is_local_pf || tbe.is_remote_pf) == false; |
| bool is_remote_can_notify := tbe.is_remote_pf && upstream_prefetch_trains_prefetcher; |
| if (is_demand) { |
| cache.profileDemandMiss(); |
| } else { |
| assert(use_prefetcher || tbe.is_remote_pf); |
| cache.profilePrefetchMiss(); |
| } |
| // notify prefetcher about this demand miss |
| if (use_prefetcher && tbe.isSeqReqValid && (is_demand || is_remote_can_notify)) { |
| bool is_read := false; |
| if (isReadReqType(tbe.reqType)) { |
| is_read := true; |
| } else { |
| assert(isWriteReqType(tbe.reqType)); |
| } |
| |
| // FIXME: this dataBlk is likely to have stale data. This should be fixed |
| // if our prefetcher uses cached data to make prefetch decisions. |
| notifyPfMiss(tbe.seqReq, is_read, tbe.dataBlk); |
| } |
| } |
| |
| action(Profile_Hit, desc="") { |
| assert(is_valid(tbe)); |
| assert(is_valid(cache_entry)); |
| assert(tbe.dataValid); |
| bool is_demand := (tbe.is_local_pf || tbe.is_remote_pf) == false; |
| bool is_remote_can_notify := tbe.is_remote_pf && upstream_prefetch_trains_prefetcher; |
| if (is_demand) { |
| cache.profileDemandHit(); |
| } else { |
| assert(use_prefetcher || tbe.is_remote_pf); |
| cache.profilePrefetchHit(); |
| } |
| // notify prefetcher about this demand hit |
| if (use_prefetcher && tbe.isSeqReqValid && (is_demand || is_remote_can_notify)) { |
| bool is_read := false; |
| if (isReadReqType(tbe.reqType)) { |
| is_read := true; |
| } else { |
| assert(isWriteReqType(tbe.reqType)); |
| } |
| notifyPfHit(tbe.seqReq, is_read, tbe.dataBlk); |
| |
| cache_entry.HWPrefetched := false; |
| } |
| } |
| |
| action(Profile_Fill, desc="") { |
| assert(is_valid(tbe)); |
| assert(is_valid(cache_entry)); |
| if (use_prefetcher && tbe.isSeqReqValid) { |
| |
| cache_entry.HWPrefetched := tbe.is_local_pf || |
| (tbe.is_remote_pf && |
| (upstream_prefetch_trains_prefetcher == false)); |
| |
| // Prefetchers that use this info require notifications from both |
| // demand and pf fills (unlike notifyPfHit/notifyPfMiss) |
| notifyPfFill(tbe.seqReq, tbe.dataBlk, tbe.is_local_pf); |
| } |
| } |
| |
| action(Profile_Eviction, desc="") { |
| if (sc_lock_enabled && sequencer.llscCheckMonitor(address)) { |
| DPRINTF(LLSC, "Invalidating monitored address %#x\n", address); |
| scLockIncLatency(); |
| } |
| if (send_evictions) { |
| DPRINTF(RubySlicc, "Sending invalidation for %#x to the sequencer\n", address); |
| sequencer.evictionCallback(address); |
| } |
| if (use_prefetcher && is_valid(cache_entry)) { |
| notifyPfEvict(address, cache_entry.HWPrefetched); |
| } |
| } |
| |
| action(Profile_OutgoingStart, desc="") { |
| outgoingTransactionStart(address, curTransitionEvent()); |
| } |
| |
| action(Profile_OutgoingEnd_DataResp, desc="") { |
| assert(is_valid(tbe)); |
| // completes once all data is received |
| if (tbe.expected_req_resp.hasReceivedData()) { |
| outgoingTransactionEnd(address, tbe.rcvdRetryAck); |
| } |
| } |
| |
| action(Profile_OutgoingEnd_DatalessResp, desc="") { |
| assert(is_valid(tbe)); |
| outgoingTransactionEnd(address, tbe.rcvdRetryAck); |
| } |
| |
| action(TagArrayRead, desc="") { |
| assert(is_valid(tbe)); |
| tbe.delayNextAction := curTick() + cyclesToTicks( |
| tagLatency((tbe.reqType == CHIRequestType:Load) || |
| (tbe.reqType == CHIRequestType:Store) || |
| (tbe.reqType == CHIRequestType:StoreLine))); |
| } |
| |
| action(TagArrayWrite, desc="") { |
| assert(is_valid(tbe)); |
| // when hasUseTimeout is set the final state is UD_T, but adding a delay |
| // between now and triggering Fin_UD_T may allow the timer to expire and then |
| // we end up in the wrong state |
| if (dealloc_wait_for_tag && (tbe.hasUseTimeout == false)) { |
| tbe.delayNextAction := curTick() + cyclesToTicks(tagLatency(false)); |
| } |
| } |
| |
| action(DataArrayRead, desc="") { |
| assert(is_valid(tbe)); |
| tbe.delayNextAction := curTick() + cyclesToTicks(dataLatency()); |
| } |
| |
| action(DataArrayWrite, desc="") { |
| assert(is_valid(tbe)); |
| assert(is_valid(cache_entry)); |
| assert(tbe.doCacheFill); |
| if(wait_for_cache_wr) { |
| tbe.delayNextAction := curTick() + cyclesToTicks(dataLatency()); |
| } |
| } |
| |
| action(ReadHitPipe, desc="") { |
| assert(is_valid(tbe)); |
| tbe.delayNextAction := curTick() + cyclesToTicks(read_hit_latency); |
| } |
| |
| action(ReadMissPipe, desc="") { |
| assert(is_valid(tbe)); |
| tbe.delayNextAction := curTick() + cyclesToTicks(read_miss_latency); |
| } |
| |
| action(WriteFEPipe, desc="") { |
| assert(is_valid(tbe)); |
| tbe.delayNextAction := curTick() + cyclesToTicks(write_fe_latency); |
| } |
| |
| action(WriteBEPipe, desc="") { |
| assert(is_valid(tbe)); |
| tbe.delayNextAction := curTick() + cyclesToTicks(write_be_latency); |
| } |
| |
| action(FillPipe, desc="") { |
| assert(is_valid(tbe)); |
| tbe.delayNextAction := curTick() + cyclesToTicks(fill_latency); |
| } |
| |
| action(SnpSharedPipe, desc="") { |
| assert(is_valid(tbe)); |
| tbe.delayNextAction := curTick() + cyclesToTicks(snp_latency); |
| } |
| |
| action(SnpInvPipe, desc="") { |
| assert(is_valid(tbe)); |
| tbe.delayNextAction := curTick() + cyclesToTicks(snp_latency + snp_inv_latency); |
| } |
| |
| action(SnpOncePipe, desc="") { |
| assert(is_valid(tbe)); |
| tbe.delayNextAction := curTick() + cyclesToTicks(snp_latency); |
| } |
| |
| ////////////////////////////////// |
| // DVM Actions |
| |
| action(Send_DvmTlbi, desc="") { |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, CHIRequestType:DvmOpNonSync, out_msg); |
| DPRINTF(RubyProtocol, "Sending DvmOpNonSync to %d\n", getMiscNodeMachine()); |
| |
| out_msg.usesTxnId := true; |
| out_msg.txnId := tbe.addr; // for DVM TBEs addr = txnId |
| |
| out_msg.Destination.clear(); |
| out_msg.Destination.add(getMiscNodeMachine()); |
| out_msg.dataToFwdRequestor := false; |
| |
| // Don't set message size, we don't use the data inside the messages |
| |
| allowRequestRetry(tbe, out_msg); |
| } |
| |
| // TLBIs can be ended early if the MN chooses to send CompDBIDResp. |
| // Otherwise, the MN sends a plain DBIDResp, and then sends a Comp later. |
| // => We add two possible response types, then add 1 to the count |
| // e.g. "expect exactly 1 (CompDBIDResp OR DBIDResp)" |
| clearExpectedReqResp(tbe); |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:CompDBIDResp); |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:DBIDResp); |
| tbe.expected_req_resp.addExpectedCount(1); |
| // If a plain DBIDResp is recieved, then Comp will be manually expected. |
| // (expect_sep_wu_comp also sort of handles this, but it's WU specific, |
| // and ProcessNextState doesn't respect it). |
| |
| // Push a value to the list of pending NonSyncs |
| // The actual value doesn't matter, but we have to pick |
| // a type which already has function signatures |
| // e.g. TriggerQueue has push(Event) specified in SLICC but not push(addr) |
| DPRINTF(RubyProtocol, "Pushing pending nonsync to blocklist %16x\n", tbe.addr); |
| dvmPendingNonSyncsBlockingSync.push(Event:DvmTlbi_Initiate); |
| } |
| |
| // Try to send a DVM Sync, but put it in the pending slot |
| // if there are pending Non-Syncs blocking it. |
| action(Try_Send_DvmSync, desc="") { |
| if (dvmPendingNonSyncsBlockingSync.empty()){ |
| DPRINTF(RubyProtocol, "Nonsync queue is empty so %016x can proceed\n", tbe.addr); |
| tbe.actions.push(Event:DvmSync_Send); |
| } else { |
| assert(!dvmHasPendingSyncOp); |
| DPRINTF(RubyProtocol, "Nonsync queue is not empty so %016x is now pending\n", tbe.addr); |
| dvmHasPendingSyncOp := true; |
| dvmPendingSyncOp := address; |
| } |
| } |
| |
| // Try to send a DVM sync that was put in the pending slot |
| // due to pending Non-Syncs blocking it. Those Non-Syncs may not be |
| // blocking it anymore. |
| action(Try_Send_Pending_DvmSync, desc="") { |
| // Pop an element off the list of pending NonSyncs |
| // It won't necessarily be ours, but that doesn't matter. |
| assert(!dvmPendingNonSyncsBlockingSync.empty()); |
| DPRINTF(RubyProtocol, "Popping nonsync from blocklist %16x\n", tbe.addr); |
| dvmPendingNonSyncsBlockingSync.pop(); |
| |
| if (dvmPendingNonSyncsBlockingSync.empty() && dvmHasPendingSyncOp) { |
| DPRINTF(RubyProtocol, "Blocklist now empty, pending op %16x can proceed\n", dvmPendingSyncOp); |
| TBE syncTBE := getDvmTBE(dvmPendingSyncOp); |
| assert(is_valid(syncTBE)); |
| syncTBE.actions.push(Event:DvmSync_Send); |
| |
| dvmHasPendingSyncOp := false; |
| } |
| } |
| |
| action(Send_DvmSync, desc="") { |
| enqueue(reqOutPort, CHIRequestMsg, request_latency) { |
| prepareRequest(tbe, CHIRequestType:DvmOpSync, out_msg); |
| DPRINTF(RubyProtocol, "Sending DvmOpSync to %d\n", getMiscNodeMachine()); |
| |
| out_msg.usesTxnId := true; |
| out_msg.txnId := tbe.addr; // for DVM TBEs addr = txnId |
| |
| out_msg.Destination.clear(); |
| out_msg.Destination.add(getMiscNodeMachine()); |
| out_msg.dataToFwdRequestor := false; |
| |
| // Don't set message size, we don't use the data inside the messages |
| |
| allowRequestRetry(tbe, out_msg); |
| } |
| |
| clearExpectedReqResp(tbe); |
| tbe.expected_req_resp.addExpectedRespType(CHIResponseType:DBIDResp); |
| tbe.expected_req_resp.addExpectedCount(1); |
| // Comp will be expected later |
| } |
| |
| action(Send_DvmTlbi_NCBWrData, desc="") { |
| enqueue(datOutPort, CHIDataMsg, data_latency) { |
| out_msg.addr := tbe.addr; |
| out_msg.type := CHIDataType:NCBWrData; |
| |
| out_msg.usesTxnId := true; |
| out_msg.txnId := tbe.addr; // for DVM TBEs addr = txnId |
| |
| // Set dataBlk to all 0 - we don't actually use the contents |
| out_msg.dataBlk.clear(); |
| // Data should be 8 bytes - this function is (offset, range) |
| out_msg.bitMask.setMask(0, 8); |
| |
| out_msg.responder := machineID; |
| |
| out_msg.Destination.clear(); |
| out_msg.Destination.add(getMiscNodeMachine()); |
| } |
| } |
| |
| action(Send_DvmSync_NCBWrData, desc="") { |
| enqueue(datOutPort, CHIDataMsg, data_latency) { |
| out_msg.addr := tbe.addr; |
| out_msg.type := CHIDataType:NCBWrData; |
| |
| out_msg.usesTxnId := true; |
| out_msg.txnId := tbe.addr; // for DVM TBEs addr = txnId |
| |
| // Set dataBlk to all 0 - we don't actually use the contents |
| out_msg.dataBlk.clear(); |
| // Data should be 8 bytes - this function is (offset, range) |
| // I assume the range is in bytes... |
| out_msg.bitMask.setMask(0, 8); |
| |
| out_msg.responder := machineID; |
| |
| out_msg.Destination.clear(); |
| out_msg.Destination.add(getMiscNodeMachine()); |
| } |
| } |
| |
| action(DvmTlbi_CompCallback, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.is_dvm_tbe); |
| assert(tbe.reqType == CHIRequestType:DvmTlbi_Initiate); |
| sequencer.unaddressedCallback(tbe.addr, RubyRequestType:TLBI); |
| } |
| |
| action(DvmSync_CompCallback, desc="") { |
| assert(is_valid(tbe)); |
| assert(tbe.is_dvm_tbe); |
| assert(tbe.reqType == CHIRequestType:DvmSync_Initiate); |
| sequencer.unaddressedCallback(tbe.addr, RubyRequestType:TLBI_SYNC); |
| } |
| |
| ////////////////////////////////// |
| // DVM Snoop Actions |
| |
| action(Initiate_DvmSnoop, desc="") { |
| // DvmSnoop cannot be retried |
| bool was_retried := false; |
| peek(snpRdyPort, CHIRequestMsg) { |
| set_tbe(allocateDvmSnoopTBE(address, in_msg)); |
| } |
| // Last argument = false, so it uses a "unique ID" rather than an address |
| // "Incoming" transactions for DVM = time between receiving a Snooped DVM op |
| // and sending the SnpResp_I |
| incomingTransactionStart(address, curTransitionEvent(), State:I, was_retried, false); |
| } |
| |
| action(DvmExtTlbi_EnqueueSnpResp, desc=""){ |
| tbe.delayNextAction := curTick() + cyclesToTicks(dvm_ext_tlbi_latency); |
| tbe.actions.push(Event:SendSnpIResp); |
| } |
| |
| action(DvmExtSync_TriggerCallback, desc=""){ |
| assert(is_valid(tbe)); |
| assert(tbe.is_dvm_snp_tbe); |
| sequencer.unaddressedCallback(tbe.addr, RubyRequestType:TLBI_EXT_SYNC); |
| } |
| |
| action(Profile_OutgoingStart_DVM, desc="") { |
| outgoingTransactionStart(address, curTransitionEvent(), false); |
| } |
| |
| action(Profile_OutgoingEnd_DVM, desc="") { |
| assert(is_valid(tbe)); |
| outgoingTransactionEnd(address, tbe.rcvdRetryAck, false); |
| } |