| /* | |
| * 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 function definitions | |
| //////////////////////////////////////////////////////////////////////////// | |
| //////////////////////////////////////////////////////////////////////////// | |
| // External functions | |
| Tick clockEdge(); | |
| Tick curTick(); | |
| Tick cyclesToTicks(Cycles c); | |
| Cycles ticksToCycles(Tick t); | |
| void set_cache_entry(AbstractCacheEntry b); | |
| void unset_cache_entry(); | |
| void set_tbe(TBE b); | |
| void unset_tbe(); | |
| MachineID mapAddressToDownstreamMachine(Addr addr); | |
| MachineID mapAddressToMachine(Addr addr, MachineType mtype); | |
| void incomingTransactionStart(Addr, Event, State, bool); | |
| void incomingTransactionEnd(Addr, State); | |
| void outgoingTransactionStart(Addr, Event); | |
| void outgoingTransactionEnd(Addr, bool); | |
| // Overloads for transaction-measuring functions | |
| // final bool = isAddressed | |
| // if false, uses a "unaddressed" table with unique IDs | |
| void incomingTransactionStart(Addr, Event, State, bool, bool); | |
| void incomingTransactionEnd(Addr, State, bool); | |
| void outgoingTransactionStart(Addr, Event, bool); | |
| void outgoingTransactionEnd(Addr, bool, bool); | |
| Event curTransitionEvent(); | |
| State curTransitionNextState(); | |
| // Placeholders for future prefetch support | |
| void notifyPfHit(RequestPtr req, bool is_read, DataBlock blk) { } | |
| void notifyPfMiss(RequestPtr req, bool is_read, DataBlock blk) { } | |
| void notifyPfFill(RequestPtr req, DataBlock blk, bool from_pf) { } | |
| void notifyPfEvict(Addr blkAddr, bool hwPrefetched) { } | |
| void notifyPfComplete(Addr addr) { } | |
| //////////////////////////////////////////////////////////////////////////// | |
| // Interface functions required by SLICC | |
| CacheEntry getCacheEntry(Addr addr), return_by_pointer="yes" { | |
| return static_cast(CacheEntry, "pointer", cache.lookup(addr)); | |
| } | |
| CacheEntry nullCacheEntry(), return_by_pointer="yes" { | |
| return OOD; | |
| } | |
| DirEntry getDirEntry(Addr addr), return_by_pointer = "yes" { | |
| if (directory.isTagPresent(addr)) { | |
| return directory.lookup(addr); | |
| } else { | |
| return OOD; | |
| } | |
| } | |
| State getState(TBE tbe, CacheEntry cache_entry, Addr addr) { | |
| if (is_valid(tbe)) { | |
| return tbe.state; | |
| } else if (is_valid(cache_entry)) { | |
| return cache_entry.state; | |
| } else { | |
| DirEntry dir_entry := getDirEntry(addr); | |
| if (is_valid(dir_entry)) { | |
| return dir_entry.state; | |
| } else { | |
| return State:I; | |
| } | |
| } | |
| } | |
| void setState(TBE tbe, CacheEntry cache_entry, Addr addr, State state) { | |
| if (is_valid(tbe)) { | |
| tbe.state := state; | |
| } | |
| if (is_valid(cache_entry)) { | |
| cache_entry.state := state; | |
| } | |
| DirEntry dir_entry := getDirEntry(addr); | |
| if (is_valid(dir_entry)) { | |
| dir_entry.state := state; | |
| } | |
| } | |
| TBE nullTBE(), return_by_pointer="yes" { | |
| return OOD; | |
| } | |
| TBE getDvmTBE(Addr txnId), return_by_pointer="yes" { | |
| TBE dvm_tbe := dvmTBEs[txnId]; | |
| if (is_valid(dvm_tbe)) { | |
| return dvm_tbe; | |
| } | |
| TBE dvm_snp_tbe := dvmSnpTBEs[txnId]; | |
| if (is_valid(dvm_snp_tbe)) { | |
| return dvm_snp_tbe; | |
| } | |
| return OOD; | |
| } | |
| TBE getCurrentActiveTBE(Addr addr), return_by_pointer="yes" { | |
| // snoops take precedence over wbs and reqs | |
| // it's invalid to have a replacement and a req active at the same time | |
| // for the same line | |
| TBE snp_tbe := snpTBEs[addr]; | |
| if (is_valid(snp_tbe)) { | |
| return snp_tbe; | |
| } | |
| TBE req_tbe := TBEs[addr]; | |
| TBE repl_tbe := replTBEs[addr]; | |
| if (is_valid(req_tbe)) { | |
| assert(is_invalid(repl_tbe)); | |
| return req_tbe; | |
| } | |
| if (is_valid(repl_tbe)) { | |
| assert(is_invalid(req_tbe)); | |
| return repl_tbe; | |
| } | |
| return OOD; | |
| } | |
| AccessPermission getAccessPermission(Addr addr) { | |
| TBE tbe := getCurrentActiveTBE(addr); | |
| if(is_valid(tbe)) { | |
| assert(Cache_State_to_permission(tbe.state) == AccessPermission:Busy); | |
| // It's assumed that if all caches are in transient state, the latest data | |
| // is 1) within an inflight message, then 2 ) in memory. But with | |
| // CleanUnique there may be no inflight messages with data, so it needs | |
| // special handling. | |
| bool cu_requester_or_responder := | |
| (tbe.reqType == CHIRequestType:CleanUnique) || | |
| (tbe.pendReqType == CHIRequestType:CleanUnique); | |
| if ((tbe.expected_req_resp.hasExpected() || | |
| tbe.expected_snp_resp.hasExpected()) && !cu_requester_or_responder) { | |
| DPRINTF(RubySlicc, "%x %s,%s\n", addr, tbe.state, AccessPermission:Busy); | |
| return AccessPermission:Busy; | |
| } | |
| else if (tbe.dataValid && (tbe.dataMaybeDirtyUpstream == false)) { | |
| if (tbe.dataUnique) { | |
| DPRINTF(RubySlicc, "%x %s,%s\n", addr, tbe.state, AccessPermission:Read_Write); | |
| return AccessPermission:Read_Write; | |
| } else { | |
| DPRINTF(RubySlicc, "%x %s,%s\n", addr, tbe.state, AccessPermission:Read_Only); | |
| return AccessPermission:Read_Only; | |
| } | |
| } else { | |
| DPRINTF(RubySlicc, "%x %s,%s\n", addr, tbe.state, AccessPermission:Busy); | |
| return AccessPermission:Busy; | |
| } | |
| } | |
| CacheEntry cache_entry := getCacheEntry(addr); | |
| if(is_valid(cache_entry)) { | |
| DPRINTF(RubySlicc, "%x %s,%s\n", addr, cache_entry.state, Cache_State_to_permission(cache_entry.state)); | |
| return Cache_State_to_permission(cache_entry.state); | |
| } | |
| DPRINTF(RubySlicc, "%x %s,%s\n", addr, State:I, AccessPermission:NotPresent); | |
| return AccessPermission:NotPresent; | |
| } | |
| void setAccessPermission(CacheEntry cache_entry, Addr addr, State state) { | |
| if (is_valid(cache_entry)) { | |
| cache_entry.changePermission(Cache_State_to_permission(state)); | |
| } | |
| } | |
| void functionalRead(Addr addr, Packet *pkt, WriteMask &mask) { | |
| // read if bitmask has bytes not in mask or if data is dirty | |
| TBE tbe := getCurrentActiveTBE(addr); | |
| CacheEntry cache_entry := getCacheEntry(addr); | |
| DPRINTF(RubySlicc, "functionalRead %x\n", addr); | |
| WriteMask read_mask; | |
| bool dirty := false; | |
| bool from_tbe := false; | |
| if (is_valid(tbe)) { | |
| from_tbe := true; | |
| dirty := tbe.dataDirty; | |
| if (tbe.dataValid) { | |
| read_mask.fillMask(); | |
| } else { | |
| read_mask := tbe.dataBlkValid; | |
| // could have received dirty data but tbe.dataDirty not set yet because | |
| // some data is pending, so check for dirty received message types | |
| dirty := dirty || | |
| tbe.expected_req_resp.receivedDataType(CHIDataType:CompData_UD_PD) || | |
| tbe.expected_req_resp.receivedDataType(CHIDataType:CompData_SD_PD) || | |
| tbe.expected_req_resp.receivedDataType(CHIDataType:CBWrData_UD_PD) || | |
| tbe.expected_req_resp.receivedDataType(CHIDataType:CBWrData_SD_PD) || | |
| tbe.expected_req_resp.receivedDataType(CHIDataType:NCBWrData) || | |
| tbe.expected_snp_resp.receivedDataType(CHIDataType:SnpRespData_I_PD) || | |
| tbe.expected_snp_resp.receivedDataType(CHIDataType:SnpRespData_SC_PD) || | |
| tbe.expected_snp_resp.receivedDataType(CHIDataType:SnpRespData_SD) || | |
| tbe.expected_snp_resp.receivedDataType(CHIDataType:SnpRespData_UD) || | |
| tbe.expected_snp_resp.receivedDataType(CHIDataType:SnpRespData_SC_Fwded_SD_PD) || | |
| tbe.expected_snp_resp.receivedDataType(CHIDataType:SnpRespData_SC_PD_Fwded_SC) || | |
| tbe.expected_snp_resp.receivedDataType(CHIDataType:SnpRespData_I_Fwded_SD_PD) || | |
| tbe.expected_snp_resp.receivedDataType(CHIDataType:SnpRespData_I_PD_Fwded_SC); | |
| } | |
| } else if (is_valid(cache_entry) && | |
| ((Cache_State_to_permission(cache_entry.state) == AccessPermission:Read_Write) || | |
| (Cache_State_to_permission(cache_entry.state) == AccessPermission:Read_Only))) { | |
| from_tbe := false; | |
| read_mask.fillMask(); | |
| dirty := (cache_entry.state == State:UD) || (cache_entry.state == State:UD_RSC) || | |
| (cache_entry.state == State:SD) || (cache_entry.state == State:SD_RSC) || | |
| (cache_entry.state == State:UD_RU) || (cache_entry.state == State:UD_RSD) || | |
| (cache_entry.state == State:SD_RSD) || (cache_entry.state == State:UD_T); | |
| } | |
| WriteMask test_mask := mask; | |
| test_mask.orMask(read_mask); | |
| if ((mask.containsMask(test_mask) == false) || dirty) { | |
| if (from_tbe) { | |
| if(testAndReadMask(addr, tbe.dataBlk, read_mask, pkt)) { | |
| DPRINTF(RubySlicc, "functionalRead tbe %x %s dirty=%d %s %s\n", addr, tbe.dataBlk, tbe.dataDirty, read_mask, mask); | |
| mask.orMask(read_mask); | |
| } | |
| } else { | |
| if (testAndReadMask(addr, cache_entry.DataBlk, read_mask, pkt)) { | |
| DPRINTF(RubySlicc, "functionalRead cache %x %s dirty=%d %s %s\n", addr, cache_entry.DataBlk, dirty, read_mask, mask); | |
| mask.orMask(read_mask); | |
| } | |
| } | |
| } | |
| } | |
| int functionalWrite(Addr addr, Packet *pkt) { | |
| int num_functional_writes := 0; | |
| TBE tbe := getCurrentActiveTBE(addr); | |
| if(is_valid(tbe)) { | |
| num_functional_writes := num_functional_writes + | |
| testAndWrite(addr, tbe.dataBlk, pkt); | |
| DPRINTF(RubySlicc, "functionalWrite tbe %x %s\n", addr, tbe.dataBlk); | |
| } | |
| CacheEntry cache_entry := getCacheEntry(addr); | |
| if (is_valid(cache_entry)) { | |
| num_functional_writes := num_functional_writes + | |
| testAndWrite(addr, cache_entry.DataBlk, pkt); | |
| DPRINTF(RubySlicc, "functionalWrite cache %x %s\n", addr, cache_entry.DataBlk); | |
| } | |
| return num_functional_writes; | |
| } | |
| Cycles mandatoryQueueLatency(RubyRequestType type) { | |
| return intToCycles(1); | |
| } | |
| Cycles tagLatency(bool from_sequencer) { | |
| if (from_sequencer) { | |
| //mandatoryQueueLatency accounts for 1 cy | |
| return cache.getTagLatency() - intToCycles(1); | |
| } else { | |
| return cache.getTagLatency(); | |
| } | |
| } | |
| Cycles dataLatency() { | |
| return cache.getDataLatency(); | |
| } | |
| bool inCache(Addr addr) { | |
| CacheEntry entry := getCacheEntry(makeLineAddress(addr)); | |
| // NOTE: we consider data for the addr to be in cache if it exists in local, | |
| // upstream, or both caches. | |
| if ((is_valid(entry) == false) || (entry.state == State:I)) { | |
| return false; | |
| } else { | |
| return true; | |
| } | |
| } | |
| bool hasBeenPrefetched(Addr addr) { | |
| CacheEntry entry := getCacheEntry(makeLineAddress(addr)); | |
| if (is_valid(entry)) { | |
| return entry.HWPrefetched; | |
| } else { | |
| return false; | |
| } | |
| } | |
| bool inMissQueue(Addr addr) { | |
| Addr line_addr := makeLineAddress(addr); | |
| TBE tbe := getCurrentActiveTBE(line_addr); | |
| return is_valid(tbe); | |
| } | |
| void notifyCoalesced(Addr addr, RubyRequestType type, RequestPtr req, | |
| DataBlock data_blk, bool was_miss) { | |
| DPRINTF(RubySlicc, "notifyCoalesced(addr=%#x, type=%s, was_miss=%d)\n", | |
| addr, type, was_miss); | |
| if (was_miss) { | |
| cache.profileDemandMiss(); | |
| } else { | |
| cache.profileDemandHit(); | |
| } | |
| if (use_prefetcher) { | |
| bool is_read := (type == RubyRequestType:LD) || | |
| (type == RubyRequestType:Load_Linked) || | |
| (type == RubyRequestType:IFETCH); | |
| if (was_miss) { | |
| notifyPfMiss(req, is_read, data_blk); | |
| } else { | |
| notifyPfHit(req, is_read, data_blk); | |
| } | |
| } | |
| } | |
| //////////////////////////////////////////////////////////////////////////// | |
| // Helper functions | |
| void clearExpectedReqResp(TBE tbe) { | |
| assert(blockSize >= data_channel_size); | |
| assert((blockSize % data_channel_size) == 0); | |
| tbe.expected_req_resp.clear(blockSize / data_channel_size); | |
| } | |
| void clearExpectedSnpResp(TBE tbe) { | |
| assert(blockSize >= data_channel_size); | |
| assert((blockSize % data_channel_size) == 0); | |
| tbe.expected_snp_resp.clear(blockSize / data_channel_size); | |
| } | |
| void initializeTBE(TBE tbe, Addr addr, int storSlot) { | |
| assert(is_valid(tbe)); | |
| tbe.wakeup_pending_req := false; | |
| tbe.wakeup_pending_snp := false; | |
| tbe.wakeup_pending_tgr := false; | |
| tbe.addr := addr; | |
| tbe.storSlot := storSlot; | |
| clearExpectedReqResp(tbe); | |
| clearExpectedSnpResp(tbe); | |
| tbe.defer_expected_comp := false; | |
| tbe.requestorToBeOwner := false; | |
| tbe.requestorToBeExclusiveOwner := false; | |
| tbe.updateDirOnCompAck := true; | |
| tbe.dataToBeInvalid := false; | |
| tbe.dataToBeSharedClean := false; | |
| tbe.doCacheFill := false; | |
| tbe.pendReqType := CHIRequestType:null; | |
| tbe.pendAction := Event:null; | |
| tbe.finalState := State:null; | |
| tbe.delayNextAction := intToTick(0); | |
| tbe.is_stale := false; | |
| } | |
| TBE allocateRequestTBE(Addr addr, CHIRequestMsg in_msg), return_by_pointer="yes" { | |
| // We must have reserved resources for this allocation | |
| storTBEs.decrementReserved(); | |
| assert(storTBEs.areNSlotsAvailable(1)); | |
| TBEs.allocate(addr); | |
| TBE tbe := TBEs[addr]; | |
| initializeTBE(tbe, addr, storTBEs.addEntryToNewSlot()); | |
| assert(tbe.is_snp_tbe == false); | |
| assert(tbe.is_repl_tbe == false); | |
| assert(tbe.is_dvm_tbe == false); | |
| assert(tbe.is_dvm_snp_tbe == false); | |
| tbe.is_req_tbe := true; | |
| tbe.accAddr := in_msg.accAddr; | |
| tbe.accSize := in_msg.accSize; | |
| tbe.requestor := in_msg.requestor; | |
| tbe.reqType := in_msg.type; | |
| tbe.isSeqReqValid := in_msg.isSeqReqValid; | |
| tbe.seqReq := in_msg.seqReq; | |
| tbe.is_local_pf := in_msg.is_local_pf; | |
| tbe.is_remote_pf := in_msg.is_remote_pf; | |
| tbe.use_DMT := false; | |
| tbe.use_DCT := false; | |
| tbe.hasUseTimeout := false; | |
| return tbe; | |
| } | |
| TBE allocateDvmRequestTBE(Addr txnId, CHIRequestMsg in_msg), return_by_pointer="yes" { | |
| // We must have reserved resources for this allocation | |
| storDvmTBEs.decrementReserved(); | |
| assert(storDvmTBEs.areNSlotsAvailable(1)); | |
| dvmTBEs.allocate(txnId); | |
| TBE tbe := dvmTBEs[txnId]; | |
| // Setting .addr = txnId | |
| initializeTBE(tbe, txnId, storDvmTBEs.addEntryToNewSlot()); | |
| assert(tbe.is_snp_tbe == false); | |
| assert(tbe.is_repl_tbe == false); | |
| assert(tbe.is_req_tbe == false); | |
| assert(tbe.is_dvm_snp_tbe == false); | |
| tbe.is_dvm_tbe := true; | |
| // TODO - zero these out? | |
| tbe.accAddr := txnId; | |
| tbe.accSize := blockSize; | |
| tbe.requestor := in_msg.requestor; | |
| tbe.reqType := in_msg.type; | |
| tbe.isSeqReqValid := in_msg.isSeqReqValid; | |
| tbe.seqReq := in_msg.seqReq; | |
| tbe.is_local_pf := in_msg.is_local_pf; | |
| tbe.is_remote_pf := in_msg.is_remote_pf; | |
| tbe.use_DMT := false; | |
| tbe.use_DCT := false; | |
| tbe.hasUseTimeout := false; | |
| return tbe; | |
| } | |
| TBE allocateSnoopTBE(Addr addr, CHIRequestMsg in_msg), return_by_pointer="yes" { | |
| // We must have reserved resources for this allocation | |
| storSnpTBEs.decrementReserved(); | |
| assert(storSnpTBEs.areNSlotsAvailable(1)); | |
| snpTBEs.allocate(addr); | |
| TBE tbe := snpTBEs[addr]; | |
| initializeTBE(tbe, addr, storSnpTBEs.addEntryToNewSlot()); | |
| assert(tbe.is_req_tbe == false); | |
| assert(tbe.is_repl_tbe == false); | |
| assert(tbe.is_dvm_tbe == false); | |
| assert(tbe.is_dvm_snp_tbe == false); | |
| tbe.is_snp_tbe := true; | |
| tbe.accAddr := addr; | |
| tbe.accSize := blockSize; | |
| tbe.requestor := in_msg.requestor; | |
| tbe.fwdRequestor := in_msg.fwdRequestor; | |
| tbe.reqType := in_msg.type; | |
| tbe.snpNeedsData := in_msg.retToSrc; | |
| tbe.use_DMT := false; | |
| tbe.use_DCT := false; | |
| return tbe; | |
| } | |
| TBE allocateDvmSnoopTBE(Addr txnId, CHIRequestMsg in_msg), return_by_pointer="yes" { | |
| // We must have reserved resources for this allocation | |
| storDvmSnpTBEs.decrementReserved(); | |
| assert(storDvmSnpTBEs.areNSlotsAvailable(1)); | |
| dvmSnpTBEs.allocate(txnId); | |
| TBE tbe := dvmSnpTBEs[txnId]; | |
| initializeTBE(tbe, txnId, storDvmSnpTBEs.addEntryToNewSlot()); | |
| assert(tbe.is_req_tbe == false); | |
| assert(tbe.is_repl_tbe == false); | |
| assert(tbe.is_dvm_tbe == false); | |
| assert(tbe.is_snp_tbe == false); | |
| tbe.is_dvm_snp_tbe := true; | |
| // TODO - zero these out? | |
| tbe.accAddr := txnId; | |
| tbe.accSize := blockSize; | |
| tbe.requestor := in_msg.requestor; | |
| tbe.fwdRequestor := in_msg.fwdRequestor; | |
| tbe.reqType := in_msg.type; | |
| tbe.snpNeedsData := in_msg.retToSrc; | |
| tbe.use_DMT := false; | |
| tbe.use_DCT := false; | |
| return tbe; | |
| } | |
| TBE _allocateReplacementTBE(Addr addr, int storSlot), return_by_pointer="yes" { | |
| TBE tbe := replTBEs[addr]; | |
| initializeTBE(tbe, addr, storSlot); | |
| assert(tbe.is_req_tbe == false); | |
| assert(tbe.is_snp_tbe == false); | |
| assert(tbe.is_dvm_tbe == false); | |
| tbe.is_repl_tbe := true; | |
| tbe.accAddr := addr; | |
| tbe.accSize := blockSize; | |
| tbe.requestor := machineID; | |
| tbe.reqType := CHIRequestType:null; | |
| tbe.use_DMT := false; | |
| tbe.use_DCT := false; | |
| return tbe; | |
| } | |
| TBE allocateReplacementTBE(Addr addr), return_by_pointer="yes" { | |
| // We must have resources for this allocation | |
| assert(storReplTBEs.areNSlotsAvailable(1)); | |
| replTBEs.allocate(addr); | |
| return _allocateReplacementTBE(addr, storReplTBEs.addEntryToNewSlot()); | |
| } | |
| TBE allocateReplacementTBEOnSlot(Addr addr, int slot), return_by_pointer="yes" { | |
| // only when reusing slot from main TBE table | |
| assert(unify_repl_TBEs); | |
| storTBEs.addEntryToSlot(slot); | |
| replTBEs.allocate(addr); | |
| return _allocateReplacementTBE(addr, slot); | |
| } | |
| TBE getHazardTBE(TBE tbe), return_by_pointer="yes" { | |
| assert(is_valid(tbe)); | |
| assert(tbe.is_snp_tbe); | |
| TBE hazard_tbe := TBEs[tbe.addr]; | |
| if (tbe.is_req_hazard) { | |
| assert(tbe.is_repl_hazard == false); | |
| } else { | |
| assert(tbe.is_repl_hazard); | |
| hazard_tbe := replTBEs[tbe.addr]; | |
| } | |
| assert(is_valid(hazard_tbe)); | |
| return hazard_tbe; | |
| } | |
| void scheduleSendData(TBE tbe, int when) { | |
| if (tbe.snd_pendBytes.count() > 0) { | |
| assert(tbe.snd_pendEv == false); | |
| tbe.snd_pendEv := true; | |
| // enqueue send event | |
| tbe.pendAction := Event:TX_Data; | |
| enqueue(triggerOutPort, TriggerMsg, intToCycles(when)) { | |
| out_msg.addr := tbe.addr; | |
| out_msg.from_hazard := tbe.is_req_hazard || tbe.is_repl_hazard; | |
| } | |
| } | |
| } | |
| void setupPendingSend(TBE tbe) { | |
| assert(blockSize >= data_channel_size); | |
| assert((blockSize % data_channel_size) == 0); | |
| // data must be complete in the TBE | |
| assert(tbe.dataBlkValid.isFull()); | |
| tbe.snd_pendBytes.fillMask(); | |
| scheduleSendData(tbe, 0); | |
| } | |
| void setupPendingPartialSend(TBE tbe) { | |
| assert(blockSize >= data_channel_size); | |
| assert((blockSize % data_channel_size) == 0); | |
| // data must be complete in the TBE | |
| assert(tbe.dataBlkValid.count() > 0); | |
| tbe.snd_pendBytes := tbe.dataBlkValid; | |
| scheduleSendData(tbe, 0); | |
| } | |
| // common code for downstream requests | |
| void prepareRequest(TBE tbe, CHIRequestType type, CHIRequestMsg & out_msg) { | |
| out_msg.addr := tbe.addr; | |
| out_msg.accAddr := tbe.addr; | |
| out_msg.accSize := blockSize; | |
| out_msg.requestor := machineID; | |
| out_msg.fwdRequestor := tbe.requestor; | |
| out_msg.type := type; | |
| out_msg.allowRetry := false; | |
| tbe.pendReqAllowRetry := false; | |
| tbe.rcvdRetryAck := false; | |
| tbe.rcvdRetryCredit := false; | |
| tbe.pendReqType := type; | |
| out_msg.isSeqReqValid := tbe.isSeqReqValid; | |
| out_msg.seqReq := tbe.seqReq; | |
| out_msg.is_local_pf := false; | |
| out_msg.is_remote_pf := tbe.is_local_pf || tbe.is_remote_pf; | |
| } | |
| void allowRequestRetry(TBE tbe, CHIRequestMsg & out_msg) { | |
| out_msg.allowRetry := true; | |
| tbe.pendReqAllowRetry := true; | |
| tbe.pendReqAccAddr := out_msg.accAddr; | |
| tbe.pendReqAccSize := out_msg.accSize; | |
| tbe.pendReqDest := out_msg.Destination; | |
| tbe.pendReqD2OrigReq := out_msg.dataToFwdRequestor; | |
| tbe.pendReqRetToSrc := out_msg.retToSrc; | |
| } | |
| void prepareRequestRetry(TBE tbe, CHIRequestMsg & out_msg) { | |
| assert(tbe.pendReqAllowRetry); | |
| tbe.pendReqAllowRetry := false; | |
| out_msg.allowRetry := false; | |
| out_msg.addr := tbe.addr; | |
| out_msg.requestor := machineID; | |
| out_msg.fwdRequestor := tbe.requestor; | |
| out_msg.accAddr := tbe.pendReqAccAddr; | |
| out_msg.accSize := tbe.pendReqAccSize; | |
| out_msg.type := tbe.pendReqType; | |
| out_msg.Destination := tbe.pendReqDest; | |
| out_msg.dataToFwdRequestor := tbe.pendReqD2OrigReq; | |
| out_msg.retToSrc := tbe.pendReqRetToSrc; | |
| out_msg.isSeqReqValid := tbe.isSeqReqValid; | |
| out_msg.seqReq := tbe.seqReq; | |
| out_msg.is_local_pf := false; | |
| out_msg.is_remote_pf := tbe.is_local_pf || tbe.is_remote_pf; | |
| } | |
| void prepareRequestRetryDVM(TBE tbe, CHIRequestMsg & out_msg) { | |
| assert(tbe.pendReqAllowRetry); | |
| tbe.pendReqAllowRetry := false; | |
| out_msg.allowRetry := false; | |
| out_msg.addr := tbe.addr; | |
| out_msg.usesTxnId := true; | |
| out_msg.txnId := tbe.addr; | |
| out_msg.requestor := machineID; | |
| out_msg.fwdRequestor := tbe.requestor; | |
| out_msg.accAddr := tbe.pendReqAccAddr; | |
| out_msg.accSize := tbe.pendReqAccSize; | |
| out_msg.type := tbe.pendReqType; | |
| out_msg.Destination := tbe.pendReqDest; | |
| out_msg.dataToFwdRequestor := tbe.pendReqD2OrigReq; | |
| out_msg.retToSrc := tbe.pendReqRetToSrc; | |
| out_msg.isSeqReqValid := tbe.isSeqReqValid; | |
| out_msg.seqReq := tbe.seqReq; | |
| out_msg.is_local_pf := false; | |
| out_msg.is_remote_pf := tbe.is_local_pf || tbe.is_remote_pf; | |
| } | |
| void enqueueDoRetry(TBE tbe) { | |
| if (tbe.rcvdRetryAck && tbe.rcvdRetryCredit) { | |
| enqueue(retryTriggerOutPort, RetryTriggerMsg, 0) { | |
| out_msg.addr := tbe.addr; | |
| out_msg.usesTxnId := tbe.is_dvm_tbe || tbe.is_dvm_snp_tbe; | |
| out_msg.event := Event:DoRetry; | |
| } | |
| destsWaitingRetry.removeNetDest(tbe.pendReqDest); | |
| } | |
| } | |
| void processRetryQueue() { | |
| // send credit if requestor waiting for it and we have resources | |
| bool has_avail := storTBEs.areNSlotsAvailable(1); | |
| assert(unify_repl_TBEs || has_avail); | |
| // the slot might still be used by a replacement if unify_repl_TBEs is set | |
| if (retryQueue.empty() == false && has_avail) { | |
| storTBEs.incrementReserved(); | |
| RetryQueueEntry e := retryQueue.next(); | |
| retryQueue.pop(); | |
| enqueue(retryTriggerOutPort, RetryTriggerMsg, 0) { | |
| out_msg.addr := e.addr; | |
| out_msg.usesTxnId := e.usesTxnId; | |
| out_msg.retryDest := e.retryDest; | |
| out_msg.event := Event:SendPCrdGrant; | |
| } | |
| } | |
| } | |
| void printResources() { | |
| if (unify_repl_TBEs) { | |
| assert(storReplTBEs.size() == 0); | |
| assert(storReplTBEs.reserved() == 0); | |
| DPRINTF(RubySlicc, "Resources(used/rsvd/max): TBEs=%d/%d/%d snpTBEs=%d/%d/%d replTBEs=%d/%d/%d dvmTBEs=%d/%d/%d\n", | |
| storTBEs.size(), storTBEs.reserved(), storTBEs.capacity(), | |
| storSnpTBEs.size(), storSnpTBEs.reserved(), storSnpTBEs.capacity(), | |
| storTBEs.size(), storTBEs.reserved(), storTBEs.capacity(), | |
| storDvmTBEs.size(), storDvmTBEs.reserved(), storDvmTBEs.capacity()); | |
| } else { | |
| DPRINTF(RubySlicc, "Resources(used/rsvd/max): TBEs=%d/%d/%d snpTBEs=%d/%d/%d replTBEs=%d/%d/%d dvmTBEs=%d/%d/%d\n", | |
| storTBEs.size(), storTBEs.reserved(), storTBEs.capacity(), | |
| storSnpTBEs.size(), storSnpTBEs.reserved(), storSnpTBEs.capacity(), | |
| storReplTBEs.size(), storReplTBEs.reserved(), storReplTBEs.capacity(), | |
| storDvmTBEs.size(), storDvmTBEs.reserved(), storDvmTBEs.capacity()); | |
| } | |
| DPRINTF(RubySlicc, "Resources(in/out size): req=%d/%d rsp=%d/%d dat=%d/%d snp=%d/%d trigger=%d\n", | |
| reqIn.getSize(curTick()), reqOut.getSize(curTick()), | |
| rspIn.getSize(curTick()), rspOut.getSize(curTick()), | |
| datIn.getSize(curTick()), datOut.getSize(curTick()), | |
| snpIn.getSize(curTick()), snpOut.getSize(curTick()), | |
| triggerQueue.getSize(curTick())); | |
| } | |
| bool needCacheEntry(CHIRequestType req_type, | |
| CacheEntry cache_entry, DirEntry dir_entry, | |
| bool is_prefetch) { | |
| // never allocates: | |
| // - if entry already valid | |
| // - if using DMT; the request is a Read*; and dir entry is invalid | |
| // oterwise follow config params | |
| if (is_valid(cache_entry) || | |
| (enable_DMT && is_invalid(dir_entry) && | |
| ((req_type == CHIRequestType:ReadShared) || | |
| (req_type == CHIRequestType:ReadUnique) || | |
| (req_type == CHIRequestType:ReadOnce)))) { | |
| return false; | |
| } else { | |
| return is_prefetch || | |
| (alloc_on_readshared && ((req_type == CHIRequestType:ReadShared) || | |
| (req_type == CHIRequestType:ReadNotSharedDirty))) || | |
| (alloc_on_readunique && (req_type == CHIRequestType:ReadUnique)) || | |
| (alloc_on_readonce && (req_type == CHIRequestType:ReadOnce)) || | |
| (alloc_on_writeback && ((req_type == CHIRequestType:WriteBackFull) || | |
| (req_type == CHIRequestType:WriteCleanFull) || | |
| (req_type == CHIRequestType:WriteEvictFull) || | |
| (is_HN && (req_type == CHIRequestType:WriteUniqueFull)))) || | |
| (alloc_on_seq_acc && ((req_type == CHIRequestType:Load) || | |
| (req_type == CHIRequestType:Store))) || | |
| (alloc_on_seq_line_write && (req_type == CHIRequestType:StoreLine)); | |
| } | |
| } | |
| bool needDeallocCacheEntry(CHIRequestType req_type) { | |
| return (dealloc_on_shared && ((req_type == CHIRequestType:ReadShared) || | |
| (req_type == CHIRequestType:ReadNotSharedDirty))) || | |
| (dealloc_on_unique && ((req_type == CHIRequestType:ReadUnique) || | |
| (req_type == CHIRequestType:CleanUnique))); | |
| } | |
| bool upstreamHasUnique(State state) { | |
| return (state == State:RU) || (state == State:UD_RU) || (state == State:UC_RU); | |
| } | |
| bool upstreamHasShared(State state) { | |
| return (state == State:RSC) || (state == State:RSD) || | |
| (state == State:RUSD) || (state == State:RUSC) || | |
| (state == State:UD_RSD) || (state == State:SD_RSD) || | |
| (state == State:UD_RSC) || (state == State:SD_RSC) || | |
| (state == State:UC_RSC) || (state == State:SC_RSC); | |
| } | |
| void printTBEState(TBE tbe) { | |
| DPRINTF(RubySlicc, "STATE: addr: %#x data present=%d valid=%d unique=%d dirty=%d mu_dirty=%d dir ownerV=%d ownerE=%d sharers=%d tobe_I=%d tobe_SC=%d doFill=%d pendAction=%s\n", | |
| tbe.addr, tbe.dataBlkValid.isFull(), tbe.dataValid, tbe.dataUnique, | |
| tbe.dataDirty, tbe.dataMaybeDirtyUpstream, tbe.dir_ownerExists, | |
| tbe.dir_ownerIsExcl,tbe.dir_sharers.count(), | |
| tbe.dataToBeInvalid, tbe.dataToBeSharedClean, | |
| tbe.doCacheFill, tbe.pendAction); | |
| DPRINTF(RubySlicc, "dataBlkValid = %s\n", tbe.dataBlkValid); | |
| } | |
| void printDvmTBEState(TBE tbe) { | |
| DPRINTF(RubySlicc, "STATE: addr=%#x reqType=%d state=%d pendAction=%s isDvmTBE=%d isReplTBE=%d isReqTBE=%d isSnpTBE=%d\n", | |
| tbe.addr, tbe.reqType, tbe.state, tbe.pendAction, | |
| tbe.is_dvm_tbe, tbe.is_repl_tbe, tbe.is_req_tbe, tbe.is_snp_tbe); | |
| } | |
| MachineID getMiscNodeMachine() { | |
| // return the MachineID of the misc node | |
| return mapAddressToMachine(intToAddress(0), MachineType:MiscNode); | |
| } | |
| void copyCacheAndDir(CacheEntry cache_entry, DirEntry dir_entry, | |
| TBE tbe, State initialState) { | |
| assert(is_valid(tbe)); | |
| // have dir entry | |
| if (is_valid(dir_entry)) { | |
| assert((initialState == State:UD_RSC) || (initialState == State:SD_RSC) || | |
| (initialState == State:UC_RSC) || (initialState == State:SC_RSC) || | |
| (initialState == State:UD_RU) || (initialState == State:UC_RU) || | |
| (initialState == State:RU) || (initialState == State:RSC) || | |
| (initialState == State:RSD) || (initialState == State:RUSD) || | |
| (initialState == State:RUSC) || | |
| (initialState == State:UD_RSD) || (initialState == State:SD_RSD)); | |
| tbe.dir_sharers := dir_entry.sharers; | |
| tbe.dir_owner := dir_entry.owner; | |
| tbe.dir_ownerExists := dir_entry.ownerExists; | |
| tbe.dir_ownerIsExcl := dir_entry.ownerIsExcl; | |
| assert(tbe.dir_sharers.count() > 0); | |
| } else { | |
| tbe.dir_sharers.clear(); | |
| tbe.dir_ownerExists := false; | |
| } | |
| // Sanity checks | |
| assert((tbe.dir_ownerExists && tbe.dir_ownerIsExcl) == | |
| ((initialState == State:UD_RU) || (initialState == State:UC_RU) || | |
| (initialState == State:RU))); | |
| assert((tbe.dir_ownerExists && (tbe.dir_ownerIsExcl == false)) == | |
| ((initialState == State:RSD) || (initialState == State:RUSD) || | |
| (initialState == State:UD_RSD) || (initialState == State:SD_RSD))); | |
| // have usable data | |
| if (is_valid(cache_entry) && | |
| ((initialState == State:UD) || (initialState == State:SD) || | |
| (initialState == State:UC) || (initialState == State:SC) || | |
| (initialState == State:UD_RSC) || (initialState == State:SD_RSC) || | |
| (initialState == State:UC_RSC) || (initialState == State:SC_RSC) || | |
| (initialState == State:UD_RSD) || (initialState == State:SD_RSD) || | |
| (initialState == State:UD_T))) { | |
| tbe.dataBlk := cache_entry.DataBlk; | |
| tbe.dataBlkValid.fillMask(); | |
| tbe.dataValid := true; | |
| DPRINTF(RubySlicc, "Cached data %s\n", tbe.dataBlk); | |
| } else { | |
| assert(is_invalid(cache_entry) || | |
| (is_valid(cache_entry) && (initialState == State:UD_RU) || | |
| (initialState == State:UC_RU))); | |
| tbe.dataBlkValid.clear(); | |
| tbe.dataValid := false; | |
| } | |
| // set MRU for accessed block | |
| if (is_valid(cache_entry) && ((tbe.is_local_pf || tbe.is_remote_pf) == false)) { | |
| cache.setMRU(cache_entry); | |
| } | |
| // data is dirty here | |
| tbe.dataDirty := (initialState == State:UD) || (initialState == State:UD_RSC) || | |
| (initialState == State:SD) || (initialState == State:SD_RSC) || | |
| (initialState == State:UD_RU) || (initialState == State:UD_RSD) || | |
| (initialState == State:SD_RSD) || (initialState == State:UD_T); | |
| // maybe dirty upstream | |
| tbe.dataMaybeDirtyUpstream := (initialState == State:UD_RU) || (initialState == State:UC_RU) || | |
| (initialState == State:UD_RSD) || (initialState == State:SD_RSD) || | |
| (initialState == State:RU) || (initialState == State:RSD) || | |
| (initialState == State:RUSD); | |
| assert(tbe.dir_ownerExists == tbe.dataMaybeDirtyUpstream); | |
| // data is unique here or upstream | |
| tbe.dataUnique := (initialState == State:UD) || (initialState == State:UD_RSC) || | |
| (initialState == State:UD_RU) || (initialState == State:UC) || | |
| (initialState == State:UC_RSC) || (initialState == State:UC_RU) || | |
| (initialState == State:RU) || (initialState == State:RUSD) || | |
| (initialState == State:RUSC) || | |
| (initialState == State:UD_RSD) || (initialState == State:UD_T); | |
| // it is locked until timeout ? | |
| tbe.hasUseTimeout := initialState == State:UD_T; | |
| tbe.dataToBeSharedClean := false; | |
| tbe.dataToBeInvalid := false; | |
| printTBEState(tbe); | |
| } | |
| void copyCacheAndDirTBEs(TBE src, TBE dst) { | |
| assert(is_valid(src)); | |
| assert(is_valid(dst)); | |
| dst.dataBlk := src.dataBlk; | |
| dst.dataBlkValid := src.dataBlkValid; | |
| dst.dataValid := src.dataValid; | |
| dst.dataDirty := src.dataDirty; | |
| dst.dataMaybeDirtyUpstream := src.dataMaybeDirtyUpstream; | |
| dst.dataUnique := src.dataUnique; | |
| dst.dir_sharers := src.dir_sharers; | |
| dst.dir_owner := src.dir_owner; | |
| dst.dir_ownerExists := src.dir_ownerExists; | |
| dst.dir_ownerIsExcl := src.dir_ownerIsExcl; | |
| printTBEState(dst); | |
| } | |
| void deallocateReqTBE(TBE tbe) { | |
| assert(is_valid(tbe)); | |
| assert(tbe.is_req_tbe); | |
| storTBEs.removeEntryFromSlot(tbe.storSlot); | |
| TBEs.deallocate(tbe.addr); | |
| } | |
| void deallocateSnpTBE(TBE tbe) { | |
| assert(is_valid(tbe)); | |
| assert(tbe.is_snp_tbe); | |
| storSnpTBEs.removeEntryFromSlot(tbe.storSlot); | |
| snpTBEs.deallocate(tbe.addr); | |
| } | |
| void deallocateReplacementTBE(TBE tbe) { | |
| assert(is_valid(tbe)); | |
| assert(tbe.is_repl_tbe); | |
| if (unify_repl_TBEs) { | |
| storTBEs.removeEntryFromSlot(tbe.storSlot); | |
| } else { | |
| storReplTBEs.removeEntryFromSlot(tbe.storSlot); | |
| } | |
| replTBEs.deallocate(tbe.addr); | |
| } | |
| void deallocateDvmTBE(TBE tbe) { | |
| assert(is_valid(tbe)); | |
| assert(tbe.is_dvm_tbe); | |
| storDvmTBEs.removeEntryFromSlot(tbe.storSlot); | |
| dvmTBEs.deallocate(tbe.addr); | |
| } | |
| void deallocateDvmSnoopTBE(TBE tbe) { | |
| assert(is_valid(tbe)); | |
| assert(tbe.is_dvm_snp_tbe); | |
| storDvmSnpTBEs.removeEntryFromSlot(tbe.storSlot); | |
| dvmSnpTBEs.deallocate(tbe.addr); | |
| } | |
| void setDataToBeStates(TBE tbe) { | |
| assert(is_valid(tbe)); | |
| if (tbe.dataToBeInvalid) { | |
| tbe.dataValid := false; | |
| tbe.dataBlkValid.clear(); | |
| } | |
| if (tbe.dataToBeSharedClean) { | |
| tbe.dataUnique := false; | |
| tbe.dataDirty := false; | |
| assert(tbe.dataMaybeDirtyUpstream == false); | |
| } | |
| tbe.dataToBeInvalid := false; | |
| tbe.dataToBeSharedClean := false; | |
| } | |
| void setExpectedForInvSnoop(TBE tbe, bool expectCleanWB) { | |
| assert(tbe.expected_snp_resp.hasExpected() == false); | |
| assert(tbe.dir_sharers.count() > 0); | |
| clearExpectedSnpResp(tbe); | |
| if (expectCleanWB) { | |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_I); | |
| } | |
| if (tbe.dataMaybeDirtyUpstream) { | |
| assert(tbe.dir_ownerExists); | |
| tbe.expected_snp_resp.addExpectedDataType(CHIDataType:SnpRespData_I_PD); | |
| if ((expectCleanWB == false) || (tbe.dir_sharers.count() > 1)) { | |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_I); | |
| } | |
| } else { | |
| tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_I); | |
| } | |
| tbe.expected_snp_resp.setExpectedCount(tbe.dir_sharers.count()); | |
| } | |
| State makeFinalStateHelper(State cs, State ds) { | |
| if (ds == State:RSC) { | |
| if (cs == State:UD) { | |
| return State:UD_RSC; | |
| } else if (cs == State:SD) { | |
| return State:SD_RSC; | |
| } else if (cs == State:UC) { | |
| return State:UC_RSC; | |
| } else if (cs == State:SC) { | |
| return State:SC_RSC; | |
| } else { | |
| return State:RSC; | |
| } | |
| } else if (ds == State:RU) { | |
| if (cs == State:UD) { | |
| return State:UD_RU; | |
| } else if (cs == State:UC) { | |
| return State:UC_RU; | |
| } else { | |
| assert(cs != State:SC); | |
| assert(cs != State:SD); | |
| return State:RU; | |
| } | |
| } else if (ds == State:RSD) { | |
| if (cs == State:UD) { | |
| return State:UD_RSD; | |
| } else if (cs == State:SD) { | |
| return State:SD_RSD; | |
| } else { | |
| assert(cs == State:I); | |
| return State:RSD; | |
| } | |
| } else if (ds == State:RUSD) { | |
| if (cs == State:UD) { | |
| return State:UD_RSD; | |
| } else { | |
| assert(cs == State:I); | |
| return State:RUSD; | |
| } | |
| } else if (ds == State:RUSC) { | |
| if (cs == State:UC) { | |
| return State:UC_RSC; | |
| } else if (cs == State:UD) { | |
| return State:UD_RSC; | |
| } else { | |
| assert(cs == State:I); | |
| return State:RUSC; | |
| } | |
| } else { | |
| assert(ds == State:I); | |
| return cs; | |
| } | |
| } | |
| State makeFinalState(TBE tbe, CacheEntry cache_entry, DirEntry dir_entry) { | |
| setDataToBeStates(tbe); | |
| printTBEState(tbe); | |
| State cache_state := State:I; | |
| State dir_state := State:I; | |
| if (tbe.dir_ownerExists) { | |
| assert(is_valid(dir_entry)); | |
| assert(tbe.dataMaybeDirtyUpstream); | |
| if (tbe.dir_ownerIsExcl) { | |
| assert(tbe.dir_sharers.count() == 1); | |
| dir_state := State:RU; | |
| } else { | |
| assert(tbe.dir_sharers.count() >= 1); | |
| if (tbe.dataUnique) { | |
| dir_state := State:RUSD; | |
| } else { | |
| dir_state := State:RSD; | |
| } | |
| } | |
| } else if (tbe.dir_sharers.count() > 0) { | |
| assert(is_valid(dir_entry)); | |
| assert(tbe.dataMaybeDirtyUpstream == false); | |
| if (tbe.dataUnique) { | |
| dir_state := State:RUSC; | |
| } else { | |
| dir_state := State:RSC; | |
| } | |
| } | |
| if (tbe.dataValid && is_valid(cache_entry)) { | |
| if (tbe.dataUnique && tbe.dataDirty) { | |
| if (tbe.hasUseTimeout) { | |
| cache_state := State:UD_T; | |
| } else { | |
| cache_state := State:UD; | |
| } | |
| } else if (tbe.dataUnique && (tbe.dataDirty == false)) { | |
| cache_state := State:UC; | |
| } else if ((tbe.dataUnique == false) && tbe.dataDirty) { | |
| assert(allow_SD); | |
| cache_state := State:SD; | |
| } else { | |
| cache_state := State:SC; | |
| } | |
| } | |
| return makeFinalStateHelper(cache_state, dir_state); | |
| } | |
| // This is used only with the finalization transitions | |
| State getNextState(Addr address) { | |
| TBE tbe := getCurrentActiveTBE(address); | |
| assert(is_valid(tbe)); | |
| assert(tbe.pendAction == Event:Final); | |
| tbe.finalState := makeFinalState(tbe, getCacheEntry(address), getDirEntry(address)); | |
| assert(tbe.finalState != State:null); | |
| return tbe.finalState; | |
| } | |
| int scLockLatency() { | |
| return sc_lock_multiplier * sc_lock_base_latency_cy; | |
| } | |
| void scLockIncLatency() | |
| { | |
| sc_lock_multiplier := sc_lock_multiplier + sc_lock_multiplier_inc; | |
| if (sc_lock_multiplier > sc_lock_multiplier_max) { | |
| sc_lock_multiplier := sc_lock_multiplier_max; | |
| } | |
| DPRINTF(LLSC, "SC lock latency increased to %d cy\n", scLockLatency()); | |
| } | |
| void scLockDecayLatency() | |
| { | |
| sc_lock_multiplier := sc_lock_multiplier - sc_lock_multiplier_decay; | |
| if (sc_lock_multiplier < 0) { | |
| sc_lock_multiplier := 0; | |
| } | |
| DPRINTF(LLSC, "SC lock latency decayed to %d cy\n", scLockLatency()); | |
| } | |
| void clearPendingAction(TBE tbe) { | |
| // only clear pendAction if snd_pendEv not set | |
| if (tbe.snd_pendEv) { | |
| assert(tbe.pendAction == Event:TX_Data); | |
| } else { | |
| tbe.pendAction := Event:null; | |
| } | |
| } | |
| bool isReadReqType(CHIRequestType type) { | |
| if (type == CHIRequestType:Load || | |
| type == CHIRequestType:ReadShared || | |
| type == CHIRequestType:ReadNotSharedDirty || | |
| type == CHIRequestType:ReadOnce) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool isWriteReqType(CHIRequestType type) { | |
| if (type == CHIRequestType:Store || | |
| type == CHIRequestType:StoreLine || | |
| type == CHIRequestType:WriteUniquePtl || | |
| type == CHIRequestType:WriteUniqueFull || | |
| type == CHIRequestType:ReadUnique) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| //////////////////////////////////////////////////////////////////////////// | |
| // State->Event converters | |
| Event reqToEvent(CHIRequestType type, bool is_prefetch) { | |
| if (type == CHIRequestType:Load) { | |
| if (is_prefetch == false) { | |
| return Event:Load; | |
| } else { | |
| return Event:Prefetch; | |
| } | |
| } else if (type == CHIRequestType:Store) { | |
| return Event:Store; | |
| } else if (type == CHIRequestType:StoreLine) { | |
| return Event:Store; | |
| } else if (type == CHIRequestType:ReadShared) { | |
| return Event:ReadShared; | |
| } else if (type == CHIRequestType:ReadNotSharedDirty) { | |
| return Event:ReadNotSharedDirty; | |
| } else if (type == CHIRequestType:ReadUnique) { | |
| if (is_HN) { | |
| return Event:ReadUnique_PoC; | |
| } else { | |
| return Event:ReadUnique; | |
| } | |
| } else if (type == CHIRequestType:CleanUnique) { | |
| return Event:CleanUnique; | |
| } else if (type == CHIRequestType:ReadOnce) { | |
| return Event:ReadOnce; | |
| } else if (type == CHIRequestType:Evict) { | |
| return Event:Evict; | |
| } else if (type == CHIRequestType:WriteBackFull) { | |
| return Event:WriteBackFull; | |
| } else if (type == CHIRequestType:WriteEvictFull) { | |
| return Event:WriteEvictFull; | |
| } else if (type == CHIRequestType:WriteCleanFull) { | |
| return Event:WriteCleanFull; | |
| } else if (type == CHIRequestType:WriteUniquePtl) { | |
| if (is_HN) { | |
| return Event:WriteUniquePtl_PoC; | |
| } else { | |
| return Event:WriteUnique; // all WriteUnique handled the same when ~PoC | |
| } | |
| } else if (type == CHIRequestType:WriteUniqueFull) { | |
| if (is_HN && alloc_on_writeback) { | |
| return Event:WriteUniqueFull_PoC_Alloc; | |
| } else if (is_HN) { | |
| return Event:WriteUniqueFull_PoC; | |
| } else { | |
| return Event:WriteUnique; // all WriteUnique handled the same when ~PoC | |
| } | |
| } else if (type == CHIRequestType:DvmTlbi_Initiate) { | |
| return Event:DvmTlbi_Initiate; | |
| } else if (type == CHIRequestType:DvmSync_Initiate) { | |
| return Event:DvmSync_Initiate; | |
| } else { | |
| error("Invalid CHIRequestType"); | |
| } | |
| } | |
| Event respToEvent (CHIResponseType type, TBE tbe) { | |
| bool on_hazard := is_valid(tbe) && (tbe.is_req_hazard || tbe.is_repl_hazard); | |
| if (type == CHIResponseType:Comp_I) { | |
| return Event:Comp_I; | |
| } else if (type == CHIResponseType:Comp_UC) { | |
| return Event:Comp_UC; | |
| } else if (type == CHIResponseType:Comp_SC) { | |
| return Event:Comp_SC; | |
| } else if (type == CHIResponseType:CompDBIDResp) { | |
| return Event:CompDBIDResp; | |
| } else if (type == CHIResponseType:DBIDResp) { | |
| return Event:DBIDResp; | |
| } else if (type == CHIResponseType:Comp) { | |
| return Event:Comp; | |
| } else if (type == CHIResponseType:CompAck) { | |
| return Event:CompAck; | |
| } else if (type == CHIResponseType:ReadReceipt) { | |
| return Event:ReadReceipt; | |
| } else if (type == CHIResponseType:RespSepData) { | |
| return Event:RespSepData; | |
| } else if (type == CHIResponseType:SnpResp_I) { | |
| return Event:SnpResp_I; | |
| } else if (type == CHIResponseType:SnpResp_I_Fwded_UC) { | |
| return Event:SnpResp_I_Fwded_UC; | |
| } else if (type == CHIResponseType:SnpResp_I_Fwded_UD_PD) { | |
| return Event:SnpResp_I_Fwded_UD_PD; | |
| } else if (type == CHIResponseType:SnpResp_SC) { | |
| return Event:SnpResp_SC; | |
| } else if (type == CHIResponseType:SnpResp_SC_Fwded_SC) { | |
| return Event:SnpResp_SC_Fwded_SC; | |
| } else if (type == CHIResponseType:SnpResp_SC_Fwded_SD_PD) { | |
| return Event:SnpResp_SC_Fwded_SD_PD; | |
| } else if (type == CHIResponseType:SnpResp_SD_Fwded_I) { | |
| return Event:SnpResp_SD_Fwded_I; | |
| } else if (type == CHIResponseType:SnpResp_SC_Fwded_I) { | |
| return Event:SnpResp_SC_Fwded_I; | |
| } else if (type == CHIResponseType:SnpResp_UD_Fwded_I) { | |
| return Event:SnpResp_UD_Fwded_I; | |
| } else if (type == CHIResponseType:SnpResp_UC_Fwded_I) { | |
| return Event:SnpResp_UC_Fwded_I; | |
| } else if (type == CHIResponseType:RetryAck) { | |
| if (is_HN) { | |
| if (on_hazard) { | |
| return Event:RetryAck_PoC_Hazard; | |
| } else { | |
| return Event:RetryAck_PoC; | |
| } | |
| } else { | |
| if (on_hazard) { | |
| return Event:RetryAck_Hazard; | |
| } else { | |
| return Event:RetryAck; | |
| } | |
| } | |
| } else if (type == CHIResponseType:PCrdGrant) { | |
| if (is_HN) { | |
| if (on_hazard) { | |
| return Event:PCrdGrant_PoC_Hazard; | |
| } else { | |
| return Event:PCrdGrant_PoC; | |
| } | |
| } else { | |
| if (on_hazard) { | |
| return Event:PCrdGrant_Hazard; | |
| } else { | |
| return Event:PCrdGrant; | |
| } | |
| } | |
| } else { | |
| error("Invalid CHIResponseType"); | |
| } | |
| } | |
| Event dataToEvent (CHIDataType type) { | |
| if (type == CHIDataType:CompData_I) { | |
| return Event:CompData_I; | |
| } else if (type == CHIDataType:CompData_UC) { | |
| return Event:CompData_UC; | |
| } else if (type == CHIDataType:CompData_SC) { | |
| return Event:CompData_SC; | |
| } else if (type == CHIDataType:CompData_UD_PD) { | |
| return Event:CompData_UD_PD; | |
| } else if (type == CHIDataType:CompData_SD_PD) { | |
| return Event:CompData_SD_PD; | |
| } else if (type == CHIDataType:DataSepResp_UC) { | |
| return Event:DataSepResp_UC; | |
| } else if (type == CHIDataType:CBWrData_I) { | |
| return Event:CBWrData_I; | |
| } else if (type == CHIDataType:CBWrData_UC) { | |
| return Event:CBWrData_UC; | |
| } else if (type == CHIDataType:CBWrData_SC) { | |
| return Event:CBWrData_SC; | |
| } else if (type == CHIDataType:CBWrData_UD_PD) { | |
| return Event:CBWrData_UD_PD; | |
| } else if (type == CHIDataType:CBWrData_SD_PD) { | |
| return Event:CBWrData_SD_PD; | |
| } else if (type == CHIDataType:NCBWrData) { | |
| return Event:NCBWrData; | |
| } else if (type == CHIDataType:SnpRespData_I_PD) { | |
| return Event:SnpRespData_I_PD; | |
| } else if (type == CHIDataType:SnpRespData_I) { | |
| return Event:SnpRespData_I; | |
| } else if (type == CHIDataType:SnpRespData_SC_PD) { | |
| return Event:SnpRespData_SC_PD; | |
| } else if (type == CHIDataType:SnpRespData_SC) { | |
| return Event:SnpRespData_SC; | |
| } else if (type == CHIDataType:SnpRespData_SD) { | |
| return Event:SnpRespData_SD; | |
| } else if (type == CHIDataType:SnpRespData_UC) { | |
| return Event:SnpRespData_UC; | |
| } else if (type == CHIDataType:SnpRespData_UD) { | |
| return Event:SnpRespData_UD; | |
| } else if (type == CHIDataType:SnpRespData_SC_Fwded_SC) { | |
| return Event:SnpRespData_SC_Fwded_SC; | |
| } else if (type == CHIDataType:SnpRespData_SC_Fwded_SD_PD) { | |
| return Event:SnpRespData_SC_Fwded_SD_PD; | |
| } else if (type == CHIDataType:SnpRespData_SC_PD_Fwded_SC) { | |
| return Event:SnpRespData_SC_PD_Fwded_SC; | |
| } else if (type == CHIDataType:SnpRespData_I_Fwded_SD_PD) { | |
| return Event:SnpRespData_I_Fwded_SD_PD; | |
| } else if (type == CHIDataType:SnpRespData_I_PD_Fwded_SC) { | |
| return Event:SnpRespData_I_PD_Fwded_SC; | |
| } else if (type == CHIDataType:SnpRespData_I_Fwded_SC) { | |
| return Event:SnpRespData_I_Fwded_SC; | |
| } else { | |
| error("Invalid CHIDataType"); | |
| } | |
| } | |
| Event snpToEvent (CHIRequestType type) { | |
| if (type == CHIRequestType:SnpCleanInvalid) { | |
| return Event:SnpCleanInvalid; | |
| } else if (type == CHIRequestType:SnpShared) { | |
| return Event:SnpShared; | |
| } else if (type == CHIRequestType:SnpUnique) { | |
| return Event:SnpUnique; | |
| } else if (type == CHIRequestType:SnpSharedFwd) { | |
| return Event:SnpSharedFwd; | |
| } else if (type == CHIRequestType:SnpNotSharedDirtyFwd) { | |
| return Event:SnpNotSharedDirtyFwd; | |
| } else if (type == CHIRequestType:SnpUniqueFwd) { | |
| return Event:SnpUniqueFwd; | |
| } else if (type == CHIRequestType:SnpOnce) { | |
| return Event:SnpOnce; | |
| } else if (type == CHIRequestType:SnpOnceFwd) { | |
| return Event:SnpOnceFwd; | |
| } else if (type == CHIRequestType:SnpDvmOpSync_P1) { | |
| return Event:SnpDvmOpSync_P1; | |
| } else if (type == CHIRequestType:SnpDvmOpSync_P2) { | |
| return Event:SnpDvmOpSync_P2; | |
| } else if (type == CHIRequestType:SnpDvmOpNonSync_P1) { | |
| return Event:SnpDvmOpNonSync_P1; | |
| } else if (type == CHIRequestType:SnpDvmOpNonSync_P2) { | |
| return Event:SnpDvmOpNonSync_P2; | |
| } else { | |
| error("Invalid CHIRequestType"); | |
| } | |
| } | |
| ////////////////////////////////////////// | |
| // Cache bank utilization tracking | |
| enumeration(RequestType, desc="To communicate stats from transitions to recordStats") { | |
| TagArrayRead, desc="Read or write the dir/cache tag/data array"; | |
| TagArrayWrite, desc="Read or write the dir/cache tag/data array"; | |
| DataArrayRead, desc="Read or write the dir/cache tag/data array"; | |
| DataArrayWrite, desc="Read or write the dir/cache tag/data array"; | |
| DestinationAvailable, desc="Check if there is a pending retry from the destination"; | |
| ReplTBEAvailable, desc="Check if a replacement TBE is available"; | |
| } | |
| void recordRequestType(RequestType request_type, Addr addr) { | |
| if (request_type == RequestType:DataArrayRead) { | |
| cache.recordRequestType(CacheRequestType:DataArrayRead, addr); | |
| } else if (request_type == RequestType:DataArrayWrite) { | |
| cache.recordRequestType(CacheRequestType:DataArrayWrite, addr); | |
| } else if (request_type == RequestType:TagArrayRead) { | |
| cache.recordRequestType(CacheRequestType:TagArrayRead, addr); | |
| } else if (request_type == RequestType:TagArrayWrite) { | |
| cache.recordRequestType(CacheRequestType:TagArrayWrite, addr); | |
| } | |
| } | |
| bool _checkResourceAvailable(RequestType request_type, Addr addr) { | |
| if (request_type == RequestType:DataArrayRead) { | |
| return cache.checkResourceAvailable(CacheResourceType:DataArray, addr); | |
| } else if (request_type == RequestType:DataArrayWrite) { | |
| return cache.checkResourceAvailable(CacheResourceType:DataArray, addr); | |
| } else if (request_type == RequestType:TagArrayRead) { | |
| return cache.checkResourceAvailable(CacheResourceType:TagArray, addr); | |
| } else if (request_type == RequestType:TagArrayWrite) { | |
| return cache.checkResourceAvailable(CacheResourceType:TagArray, addr); | |
| } else if (request_type == RequestType:DestinationAvailable) { | |
| if (throttle_req_on_retry) { | |
| MachineID dest := mapAddressToDownstreamMachine(addr); | |
| DPRINTF(RubySlicc, "Checking %s for addr %#x dest %s\n", request_type, addr, dest); | |
| return destsWaitingRetry.isElement(dest) == false; | |
| } else { | |
| return true; | |
| } | |
| } else if (request_type == RequestType:ReplTBEAvailable) { | |
| // if unify_repl_TBEs the replacement uses the same slot as the request | |
| // that initiated it, so the resource is always available | |
| return unify_repl_TBEs || storReplTBEs.areNSlotsAvailable(1); | |
| } else { | |
| error("Invalid RequestType type in checkResourceAvailable"); | |
| return true; | |
| } | |
| } | |
| bool checkResourceAvailable(RequestType request_type, Addr addr) { | |
| bool avail := _checkResourceAvailable(request_type, addr); | |
| if (avail == false) { | |
| DPRINTF(RubySlicc, "Resource %s not available for addr: %#x\n", request_type, addr); | |
| } | |
| return avail; | |
| } |