Files
gem5/src/mem/ruby/protocol/chi/CHI-cache-funcs.sm
Tiago Mück dc33a16993 mem-ruby: fix functionalRead on pending CU
Normally we don't check the TBE data if there are outstanding response
messages for the transaction because that means the latest valid data is
either in another cache or within an inflight message.
However this is not the case when we have either a pending CleanUnique
or we are handling CleanUnique. So bypass the pending message check in
this case.

Change-Id: I5f31039ca2a01a6a68fee8e0f3cf02c7e437b43e
Signed-off-by: Tiago Mück <tiago.muck@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/57395
Reviewed-by: Daecheol You <daecheol.you@samsung.com>
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
Maintainer: Jason Lowe-Power <power.jg@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
2022-06-01 15:23:47 +00:00

1423 lines
48 KiB
Plaintext

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