Introduce far atomic operations in CHI protocol.
Three configuration parameters have been used to tune this behavior:
policy_type: sets the atomic policy to one of the described in our paper
atomic_op_latency: simulates the AMO ALU operation latency
comp_anr: configures the Atomic No return transaction to split
CompDBIDResp into two different messages DBIDResp and Comp
Change-Id: I087afad9ad9fcb9df42d72893c9e32ad5a5eb478
882 lines
41 KiB
Plaintext
882 lines
41 KiB
Plaintext
/*
|
|
* Copyright (c) 2021-2023 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.
|
|
*/
|
|
|
|
|
|
machine(MachineType:Cache, "Cache coherency protocol") :
|
|
// Sequencer to insert Load/Store requests.
|
|
// May be null if this is not a L1 cache
|
|
Sequencer * sequencer;
|
|
|
|
// Cache for storing local lines.
|
|
// NOTE: it is assumed that a cache tag and directory lookups and updates
|
|
// happen in parallel. The cache tag latency is used for both cases.
|
|
CacheMemory * cache;
|
|
|
|
// Additional pipeline latency modeling for the different request types
|
|
// When defined, these are applied after the initial tag array read and
|
|
// sending necessary snoops.
|
|
Cycles read_hit_latency := 0;
|
|
Cycles read_miss_latency := 0;
|
|
Cycles atomic_op_latency := 0;
|
|
Cycles write_fe_latency := 0; // Front-end: Rcv req -> Snd req
|
|
Cycles write_be_latency := 0; // Back-end: Rcv ack -> Snd data
|
|
Cycles fill_latency := 0; // Fill latency
|
|
Cycles snp_latency := 0; // Applied before handling any snoop
|
|
Cycles snp_inv_latency := 0; // Additional latency for invalidating snoops
|
|
|
|
// Waits for cache data array write to complete before executing next action
|
|
// Note a new write will always block if bank stalls are enabled in the cache
|
|
bool wait_for_cache_wr := "False";
|
|
|
|
// Request TBE allocation latency
|
|
Cycles allocation_latency := 0;
|
|
|
|
// Enqueue latencies for outgoing messages
|
|
// NOTE: should remove this and only use parameters above?
|
|
Cycles request_latency := 1;
|
|
Cycles response_latency := 1;
|
|
Cycles snoop_latency := 1;
|
|
Cycles data_latency := 1;
|
|
Cycles dvm_ext_tlbi_latency := 6;
|
|
|
|
// When an SC fails, unique lines are locked to this controller for a period
|
|
// proportional to the number of consecutive failed SC requests. See
|
|
// the usage of sc_lock_multiplier and llscCheckMonitor for details
|
|
int sc_lock_base_latency_cy := 4;
|
|
int sc_lock_multiplier_inc := 4;
|
|
int sc_lock_multiplier_decay := 1;
|
|
int sc_lock_multiplier_max := 256;
|
|
bool sc_lock_enabled;
|
|
|
|
// Maximum number of outstanding transactions from a single requester
|
|
Addr max_outstanding_transactions := 1024;
|
|
|
|
// Recycle latency on resource stalls
|
|
Cycles stall_recycle_lat := 1;
|
|
|
|
// Notify the sequencer when a line is evicted. This should be set is the
|
|
// sequencer is not null and handled LL/SC request types.
|
|
bool send_evictions;
|
|
|
|
// Number of entries in the snoop, replacement, and DVM TBE tables
|
|
// notice the "number_of_TBEs" parameter is defined by AbstractController
|
|
int number_of_snoop_TBEs;
|
|
int number_of_repl_TBEs;
|
|
int number_of_DVM_TBEs;
|
|
int number_of_DVM_snoop_TBEs;
|
|
|
|
// replacements use the same TBE slot as the request that triggered it
|
|
// in this case the number_of_repl_TBEs parameter is ignored
|
|
bool unify_repl_TBEs;
|
|
|
|
// wait for the final tag update to complete before deallocating TBE and
|
|
// going to final stable state
|
|
bool dealloc_wait_for_tag := "False";
|
|
|
|
// Width of the data channel. Data transfer are split in multiple messages
|
|
// at the protocol level when this is less than the cache line size.
|
|
int data_channel_size;
|
|
|
|
// Set when this is used as the home node and point of coherency of the
|
|
// system. Must be false for every other cache level.
|
|
bool is_HN;
|
|
|
|
// Enables direct memory transfers between SNs and RNs when the data is
|
|
// not cache in the HN.
|
|
bool enable_DMT;
|
|
|
|
// Use ReadNoSnpSep instead of ReadNoSnp for DMT requests, which allows
|
|
// the TBE to be deallocated at HNFs before the requester receives the data
|
|
bool enable_DMT_early_dealloc := "False";
|
|
|
|
// Enables direct cache transfers, i.e., use forwarding snoops whenever
|
|
// possible.
|
|
bool enable_DCT;
|
|
|
|
// Atomic Operation Policy
|
|
// All Near executes all Atomics at L1 (variable set to 0; default)
|
|
// Unique Near executes Atomics at HNF for states I, SC, SD (set to 1)
|
|
// Present Near execites all Atomics at L1 except when state is I (set to 2)
|
|
int policy_type := 1;
|
|
|
|
|
|
// Use separate Comp/DBIDResp responses for WriteUnique
|
|
bool comp_wu := "False";
|
|
// additional latency for the WU Comp response
|
|
Cycles comp_wu_latency := 0;
|
|
|
|
|
|
// Use separate Comp/DBIDResp responses for AtomicNoResponse
|
|
bool comp_anr := "False";
|
|
// additional latency for the ANR Comp response
|
|
Cycles comp_anr_latency := 0;
|
|
|
|
// Controls cache clusivity for different request types.
|
|
// set all alloc_on* to false to completelly disable caching
|
|
bool alloc_on_readshared;
|
|
bool alloc_on_readunique;
|
|
bool alloc_on_readonce;
|
|
bool alloc_on_writeback;
|
|
bool alloc_on_seq_acc;
|
|
bool alloc_on_seq_line_write;
|
|
bool alloc_on_atomic;
|
|
// Controls if the clusivity is strict.
|
|
bool dealloc_on_unique;
|
|
bool dealloc_on_shared;
|
|
bool dealloc_backinv_unique;
|
|
bool dealloc_backinv_shared;
|
|
|
|
// If the responder has the line in UC or UD state, propagate this state
|
|
// on a ReadShared. Notice data won't be deallocated if dealloc_on_unique is
|
|
// set
|
|
bool fwd_unique_on_readshared := "False";
|
|
|
|
// Allow receiving data in SD state.
|
|
bool allow_SD;
|
|
|
|
// stall new requests to destinations with a pending retry
|
|
bool throttle_req_on_retry := "True";
|
|
|
|
// Use prefetcher
|
|
bool use_prefetcher, default="false";
|
|
|
|
// Message Queues
|
|
|
|
// Interface to the network
|
|
// Note vnet_type is used by Garnet only. "response" type is assumed to
|
|
// have data, so use it for data channels and "none" for the rest.
|
|
// network="To" for outbound queue; network="From" for inbound
|
|
// virtual networks: 0=request, 1=snoop, 2=response, 3=data
|
|
|
|
MessageBuffer * reqOut, network="To", virtual_network="0", vnet_type="none";
|
|
MessageBuffer * snpOut, network="To", virtual_network="1", vnet_type="none";
|
|
MessageBuffer * rspOut, network="To", virtual_network="2", vnet_type="none";
|
|
MessageBuffer * datOut, network="To", virtual_network="3", vnet_type="response";
|
|
|
|
MessageBuffer * reqIn, network="From", virtual_network="0", vnet_type="none";
|
|
MessageBuffer * snpIn, network="From", virtual_network="1", vnet_type="none";
|
|
MessageBuffer * rspIn, network="From", virtual_network="2", vnet_type="none";
|
|
MessageBuffer * datIn, network="From", virtual_network="3", vnet_type="response";
|
|
|
|
// Mandatory queue for receiving requests from the sequencer
|
|
MessageBuffer * mandatoryQueue;
|
|
|
|
// Internal queue for trigger events
|
|
MessageBuffer * triggerQueue;
|
|
|
|
// Internal queue for retry trigger events
|
|
MessageBuffer * retryTriggerQueue;
|
|
|
|
// Internal queue for accepted requests
|
|
MessageBuffer * reqRdy;
|
|
|
|
// Internal queue for accepted snoops
|
|
MessageBuffer * snpRdy;
|
|
|
|
// Internal queue for eviction requests
|
|
MessageBuffer * replTriggerQueue;
|
|
|
|
// Prefetch queue for receiving prefetch requests from prefetcher
|
|
MessageBuffer * prefetchQueue;
|
|
|
|
// Requests that originated from a prefetch in a upstream cache are treated
|
|
// as demand access in this cache. Notice the demand access stats are still
|
|
// updated only on true demand requests.
|
|
bool upstream_prefetch_trains_prefetcher := "False";
|
|
|
|
{
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// States
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
state_declaration(State, default="Cache_State_null") {
|
|
// Stable states
|
|
|
|
I, AccessPermission:Invalid, desc="Invalid / not present locally or upstream";
|
|
|
|
// States when block is present in local cache only
|
|
SC, AccessPermission:Read_Only, desc="Shared Clean";
|
|
UC, AccessPermission:Read_Write, desc="Unique Clean";
|
|
SD, AccessPermission:Read_Only, desc="Shared Dirty";
|
|
UD, AccessPermission:Read_Write, desc="Unique Dirty";
|
|
UD_T, AccessPermission:Read_Write, desc="UD with use timeout";
|
|
|
|
// Invalid in local cache but present in upstream caches
|
|
RU, AccessPermission:Invalid, desc="Upstream requester has line in UD/UC";
|
|
RSC, AccessPermission:Invalid, desc="Upstream requester has line in SC";
|
|
RSD, AccessPermission:Invalid, desc="Upstream requester has line in SD and maybe SC";
|
|
RUSC, AccessPermission:Invalid, desc="RSC + this node stills has exclusive access";
|
|
RUSD, AccessPermission:Invalid, desc="RSD + this node stills has exclusive access";
|
|
|
|
// Both in local and upstream caches. In some cases local maybe stale
|
|
SC_RSC, AccessPermission:Read_Only, desc="SC + RSC";
|
|
SD_RSC, AccessPermission:Read_Only, desc="SD + RSC";
|
|
SD_RSD, AccessPermission:Read_Only, desc="SD + RSD";
|
|
UC_RSC, AccessPermission:Read_Write, desc="UC + RSC";
|
|
UC_RU, AccessPermission:Invalid, desc="UC + RU";
|
|
UD_RU, AccessPermission:Invalid, desc="UD + RU";
|
|
UD_RSD, AccessPermission:Read_Write, desc="UD + RSD";
|
|
UD_RSC, AccessPermission:Read_Write, desc="UD + RSC";
|
|
|
|
// DVM states - use AccessPermission:Invalid because this prevents "functional reads"
|
|
DvmTlbi_Unconfirmed, AccessPermission:Invalid, desc="DVM TLBI waiting for confirmation from MN";
|
|
DvmSync_Unsent, AccessPermission:Invalid, desc="DVM Sync waiting for previous TLBIs to complete";
|
|
DvmSync_Unconfirmed, AccessPermission:Invalid, desc="DVM Sync waiting for confirmation from MN";
|
|
DvmTlbi_Waiting, AccessPermission:Invalid, desc="DVM TLBI confirmed by MN, waiting for completion";
|
|
DvmSync_Waiting, AccessPermission:Invalid, desc="DVM Sync confirmed by MN, waiting for completion";
|
|
DvmOp_Finished, AccessPermission:Invalid, desc="DVM operation that has completed, about to be deallocated";
|
|
|
|
DvmExtTlbi_Partial, AccessPermission:Invalid, desc="External DVM TLBI waiting for second packet from MN";
|
|
DvmExtTlbi_Executing, AccessPermission:Invalid, desc="External DVM TLBI being executed by this machine";
|
|
DvmExtSync_Partial, AccessPermission:Invalid, desc="External DVM Sync waiting for second packet from MN";
|
|
DvmExtSync_Executing, AccessPermission:Invalid, desc="External DVM Sync being executed by this machine";
|
|
DvmExtOp_Finished, AccessPermission:Invalid, desc="External DVM operation that has completed, about to be deallocated";
|
|
|
|
// Generic transient state
|
|
// There is only a transient "BUSY" state. The actions taken at this state
|
|
// and the final stable state are defined by information in the TBE.
|
|
// While on BUSY_INTR, we will reply to incoming snoops and the
|
|
// state of the cache line may change. While on BUSY_BLKD snoops
|
|
// are blocked
|
|
BUSY_INTR, AccessPermission:Busy, desc="Waiting for data and/or ack";
|
|
BUSY_BLKD, AccessPermission:Busy, desc="Waiting for data and/or ack; blocks snoops";
|
|
|
|
// Null state for debugging
|
|
null, AccessPermission:Invalid, desc="Null state";
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Events
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
enumeration(Event) {
|
|
// Events triggered by incoming requests. Allocate TBE and move
|
|
// request or snoop to the ready queue
|
|
AllocRequest, desc="Allocates a TBE for a request. Triggers a retry if table is full";
|
|
AllocRequestWithCredit, desc="Allocates a TBE for a request. Always succeeds.";
|
|
AllocSeqRequest, desc="Allocates a TBE for a sequencer request. Stalls requests if table is full";
|
|
AllocSeqDvmRequest, desc="Allocates a TBE for a sequencer DVM request. Stalls requests if table is full";
|
|
AllocPfRequest, desc="Allocates a TBE for a prefetch request. Stalls requests if table is full";
|
|
AllocSnoop, desc="Allocates a TBE for a snoop. Stalls snoop if table is full";
|
|
AllocDvmSnoop, desc="Allocated a TBE for a DVM snoop. Stalls snoop if table is full";
|
|
|
|
// Events triggered by sequencer requests or snoops in the rdy queue
|
|
// See CHIRequestType in CHi-msg.sm for descriptions
|
|
Load, desc="", in_trans="yes";
|
|
Store, desc="", in_trans="yes";
|
|
AtomicLoad, desc="", in_trans="yes";
|
|
AtomicStore, desc="", in_trans="yes";
|
|
Prefetch, desc="", in_trans="yes";
|
|
ReadShared, desc="", in_trans="yes";
|
|
ReadNotSharedDirty, desc="", in_trans="yes";
|
|
ReadUnique, desc="", in_trans="yes";
|
|
ReadUnique_PoC, desc="", in_trans="yes";
|
|
ReadOnce, desc="", in_trans="yes";
|
|
CleanUnique, desc="", in_trans="yes";
|
|
Evict, desc="", in_trans="yes";
|
|
WriteBackFull, desc="", in_trans="yes";
|
|
WriteEvictFull, desc="", in_trans="yes";
|
|
WriteCleanFull, desc="", in_trans="yes";
|
|
WriteUnique, desc="", in_trans="yes";
|
|
WriteUniquePtl_PoC, desc="", in_trans="yes";
|
|
WriteUniqueFull_PoC, desc="", in_trans="yes";
|
|
WriteUniqueFull_PoC_Alloc, desc="", in_trans="yes";
|
|
AtomicReturn, desc="", in_trans="yes";
|
|
AtomicNoReturn, desc="", in_trans="yes";
|
|
AtomicReturn_PoC, desc="", in_trans="yes";
|
|
AtomicNoReturn_PoC, desc="", in_trans="yes";
|
|
SnpCleanInvalid, desc="", in_trans="yes";
|
|
SnpShared, desc="", in_trans="yes";
|
|
SnpSharedFwd, desc="", in_trans="yes";
|
|
SnpNotSharedDirtyFwd, desc="", in_trans="yes";
|
|
SnpUnique, desc="", in_trans="yes";
|
|
SnpUniqueFwd, desc="", in_trans="yes";
|
|
SnpOnce, desc="", in_trans="yes";
|
|
SnpOnceFwd, desc="", in_trans="yes";
|
|
SnpStalled, desc="", in_trans="yes"; // A snoop stall triggered from the inport
|
|
|
|
// DVM sequencer requests
|
|
DvmTlbi_Initiate, desc="", out_trans="yes", in_trans="yes"; // triggered when a CPU core wants to send a TLBI
|
|
// TLBIs are handled entirely within Ruby, so there's no ExternCompleted message
|
|
DvmSync_Initiate, desc="", out_trans="yes", in_trans="yes"; // triggered when a CPU core wants to send a sync
|
|
DvmSync_ExternCompleted, desc=""; // triggered when an externally requested Sync is completed
|
|
|
|
// Events triggered by incoming response messages
|
|
// See CHIResponseType in CHi-msg.sm for descriptions
|
|
CompAck, desc="";
|
|
Comp_I, desc="";
|
|
Comp_UC, desc="";
|
|
Comp_SC, desc="";
|
|
CompDBIDResp, desc="";
|
|
DBIDResp, desc="";
|
|
Comp, desc="";
|
|
ReadReceipt, desc="";
|
|
RespSepData, desc="";
|
|
SnpResp_I, desc="";
|
|
SnpResp_I_Fwded_UC, desc="";
|
|
SnpResp_I_Fwded_UD_PD, desc="";
|
|
SnpResp_SC, desc="";
|
|
SnpResp_SC_Fwded_SC, desc="";
|
|
SnpResp_SC_Fwded_SD_PD, desc="";
|
|
SnpResp_UC_Fwded_I, desc="";
|
|
SnpResp_UD_Fwded_I, desc="";
|
|
SnpResp_SC_Fwded_I, desc="";
|
|
SnpResp_SD_Fwded_I, desc="";
|
|
RetryAck, desc="";
|
|
RetryAck_PoC, desc="";
|
|
PCrdGrant, desc="";
|
|
PCrdGrant_PoC, desc="";
|
|
RetryAck_Hazard, desc="";
|
|
RetryAck_PoC_Hazard, desc="";
|
|
PCrdGrant_Hazard, desc="";
|
|
PCrdGrant_PoC_Hazard, desc="";
|
|
|
|
// Events triggered by incoming DVM messages
|
|
SnpDvmOpSync_P1, desc="", in_trans="yes";
|
|
SnpDvmOpSync_P2, desc="", in_trans="yes";
|
|
SnpDvmOpNonSync_P1, desc="", in_trans="yes";
|
|
SnpDvmOpNonSync_P2, desc="", in_trans="yes";
|
|
|
|
// Events triggered by incoming data response messages
|
|
// See CHIDataType in CHi-msg.sm for descriptions
|
|
CompData_I, desc="";
|
|
CompData_UC, desc="";
|
|
CompData_SC, desc="";
|
|
CompData_UD_PD, desc="";
|
|
CompData_SD_PD, desc="";
|
|
DataSepResp_UC, desc="";
|
|
CBWrData_I, desc="";
|
|
CBWrData_UC, desc="";
|
|
CBWrData_SC, desc="";
|
|
CBWrData_UD_PD, desc="";
|
|
CBWrData_SD_PD, desc="";
|
|
NCBWrData, desc="";
|
|
SnpRespData_I, desc="";
|
|
SnpRespData_I_PD, desc="";
|
|
SnpRespData_SC, desc="";
|
|
SnpRespData_SC_PD, desc="";
|
|
SnpRespData_SD, desc="";
|
|
SnpRespData_UC, desc="";
|
|
SnpRespData_UD, desc="";
|
|
SnpRespData_SC_Fwded_SC, desc="";
|
|
SnpRespData_SC_Fwded_SD_PD, desc="";
|
|
SnpRespData_SC_PD_Fwded_SC, desc="";
|
|
SnpRespData_I_Fwded_SD_PD, desc="";
|
|
SnpRespData_I_PD_Fwded_SC, desc="";
|
|
SnpRespData_I_Fwded_SC, desc="";
|
|
|
|
// We use special events for requests that we detect to be stale. This is
|
|
// done for debugging only. We sent a stale response so the requester can
|
|
// confirm the request is indeed stale and this is not a protocol bug.
|
|
// A Write or Evict becomes stale when the requester receives a snoop that
|
|
// changes the state of the data while the request was pending.
|
|
// Actual CHI implementations don't have this check.
|
|
Evict_Stale, desc="", in_trans="yes";
|
|
WriteBackFull_Stale, desc="", in_trans="yes";
|
|
WriteEvictFull_Stale, desc="", in_trans="yes";
|
|
WriteCleanFull_Stale, desc="", in_trans="yes";
|
|
CleanUnique_Stale, desc="", in_trans="yes";
|
|
|
|
// Cache fill handling
|
|
CheckCacheFill, desc="Check if need to write or update the cache and trigger any necessary allocation and evictions";
|
|
|
|
// Internal requests generated to evict or writeback a local copy
|
|
// to free-up cache space
|
|
Local_Eviction, in_trans="yes", desc="Evicts/WB the local copy of the line";
|
|
LocalHN_Eviction, in_trans="yes", desc="Local_Eviction triggered when is HN";
|
|
Global_Eviction, in_trans="yes", desc="Local_Eviction + back-invalidate line in all upstream requesters";
|
|
|
|
// Events triggered from tbe.actions
|
|
// In general, for each event we define a single transition from
|
|
// BUSY_BLKD and/or BUSY_INTR.
|
|
// See processNextState functions and Initiate_* actions.
|
|
// All triggered transitions execute in the same cycle until it has to wait
|
|
// for pending pending responses or data (set by expected_req_resp and
|
|
// expected_snp_resp). Triggers queued with pushNB are executed even if
|
|
// there are pending messages.
|
|
|
|
// Cache/directory access events. Notice these only model the latency.
|
|
TagArrayRead, desc="Read the cache and directory tag array";
|
|
TagArrayWrite, desc="Write the cache and directory tag array";
|
|
DataArrayRead, desc="Read the cache data array";
|
|
DataArrayWrite, desc="Write the cache data array";
|
|
DataArrayWriteOnFill, desc="Write the cache data array (cache fill)";
|
|
|
|
// Events for modeling the pipeline latency
|
|
ReadHitPipe, desc="Latency of reads served from local cache";
|
|
ReadMissPipe, desc="Latency of reads not served from local cache";
|
|
WriteFEPipe, desc="Front-end latency of write requests";
|
|
WriteBEPipe, desc="Back-end latency of write requests";
|
|
FillPipe, desc="Cache fill latency";
|
|
DelayAtomic, desc="Atomic operation latency";
|
|
SnpSharedPipe, desc="Latency for SnpShared requests";
|
|
SnpInvPipe, desc="Latency for SnpUnique and SnpCleanInv requests";
|
|
SnpOncePipe, desc="Latency for SnpOnce requests";
|
|
|
|
// Send a read request downstream.
|
|
SendReadShared, out_trans="yes", desc="Send a ReadShared or ReadNotSharedDirty is allow_SD is false";
|
|
SendReadOnce, out_trans="yes", desc="Send a ReadOnce";
|
|
SendReadNoSnp, out_trans="yes", desc="Send a SendReadNoSnp";
|
|
SendReadNoSnpDMT, out_trans="yes", desc="Send a SendReadNoSnp using DMT";
|
|
SendReadUnique, out_trans="yes", desc="Send a ReadUnique";
|
|
SendCompAck, desc="Send CompAck";
|
|
// Read handling at the completer
|
|
SendCompData, desc="Send CompData";
|
|
WaitCompAck, desc="Expect to receive CompAck";
|
|
SendRespSepData, desc="Send RespSepData for a DMT request";
|
|
|
|
// Send a write request downstream.
|
|
SendWriteBackOrWriteEvict, out_trans="yes", desc="Send a WriteBackFull (if line is UD or SD) or WriteEvictFull (if UC)";
|
|
SendWriteClean, out_trans="yes", desc="Send a WriteCleanFull";
|
|
SendWriteNoSnp, out_trans="yes", desc="Send a WriteNoSnp for a full line";
|
|
SendWriteNoSnpPartial, out_trans="yes", desc="Send a WriteNoSnpPtl";
|
|
SendWriteUnique, out_trans="yes", desc="Send a WriteUniquePtl";
|
|
SendWBData, desc="Send writeback data";
|
|
SendWUData, desc="Send write unique data";
|
|
SendWUDataCB, desc="Send write unique data from a sequencer callback";
|
|
// Write handling at the completer
|
|
SendCompDBIDResp, desc="Ack WB with CompDBIDResp";
|
|
SendCompDBIDRespStale, desc="Ack stale WB with CompDBIDResp";
|
|
SendCompDBIDResp_WU, desc="Ack WU with CompDBIDResp and set expected data";
|
|
SendDBIDResp_WU, desc="Ack WU with DBIDResp and set expected data";
|
|
SendComp_WU, desc="Ack WU completion";
|
|
|
|
// Send an atomic request downstream.
|
|
SendAtomicReturn, out_trans="yes", desc="Send atomic request with return";
|
|
SendAtomicReturn_NoWait, out_trans="yes", desc="Send atomic request with return, but no DBID";
|
|
SendAtomicNoReturn, out_trans="yes", desc="Send atomic request without return";
|
|
SendARData, desc="Send atomic return request data";
|
|
SendANRData, desc="Send atomic no return request data";
|
|
// Atomic handling at the completer
|
|
SendDBIDResp_AR, desc="Ack AR with DBIDResp and set expected data";
|
|
SendCompData_AR, desc="Ack AR completion";
|
|
SendCompDBIDResp_ANR, desc="Ack ANR with CompDBIDResp and set expected data";
|
|
SendDBIDResp_ANR, desc="Ack ANR with DBIDResp and set expected data";
|
|
SendComp_ANR, desc="Ack ANR completion";
|
|
|
|
|
|
// Dataless requests
|
|
SendEvict, out_trans="yes", desc="Send a Evict";
|
|
SendCompIResp, desc="Ack Evict with Comp_I";
|
|
SendCleanUnique,out_trans="yes", desc="Send a CleanUnique";
|
|
SendCompUCResp, desc="Ack CleanUnique with Comp_UC";
|
|
SendCompUCRespStale, desc="Ack stale CleanUnique with Comp_UC";
|
|
|
|
// Checks if an upgrade using a CleanUnique was sucessfull
|
|
CheckUpgrade_FromStore, desc="Upgrade needed by a Store";
|
|
CheckUpgrade_FromCU, desc="Upgrade needed by an upstream CleanUnique";
|
|
CheckUpgrade_FromRU, desc="Upgrade needed by an upstream ReadUnique";
|
|
|
|
// Snoop requests
|
|
// SnpNotSharedDirty are sent instead of SnpShared for ReadNotSharedDirty
|
|
SendSnpShared, desc="Send a SnpShared/SnpNotSharedDirty to sharer in UC,UD, or SD state";
|
|
SendSnpSharedFwdToOwner, desc="Send a SnpSharedFwd/SnpNotSharedDirtyFwd to sharer in UC,UD, or SD state";
|
|
SendSnpSharedFwdToSharer, desc="Send a SnpSharedFwd/SnpNotSharedDirtyFwd to a sharer in SC state";
|
|
SendSnpOnce, desc="Send a SnpOnce to a sharer";
|
|
SendSnpOnceFwd, desc="Send a SnpOnceFwd to a sharer";
|
|
SendSnpUnique, desc="Send a SnpUnique to all sharers";
|
|
SendSnpUniqueRetToSrc, desc="Send a SnpUnique to all sharers. Sets RetToSrc for only one sharer.";
|
|
SendSnpUniqueFwd, desc="Send a SnpUniqueFwd to a single sharer";
|
|
SendSnpCleanInvalid, desc="Send a SnpCleanInvalid to all sharers";
|
|
SendSnpCleanInvalidNoReq, desc="Send a SnpCleanInvalid to all sharers except requestor";
|
|
// Snoop responses
|
|
SendSnpData, desc="Send SnpRespData as snoop reply";
|
|
SendSnpIResp, desc="Send SnpResp_I as snoop reply";
|
|
SendInvSnpResp, desc="Check data state and queue either SendSnpIResp or SendSnpData";
|
|
SendSnpUniqueFwdCompData, desc="Send CompData to SnpUniqueFwd target and queue either SendSnpFwdedData or SendSnpFwdedResp";
|
|
SendSnpSharedFwdCompData, desc="Send CompData to SnpUniqueFwd target and queue either SendSnpFwdedData or SendSnpFwdedResp";
|
|
SendSnpNotSharedDirtyFwdCompData, desc="Send CompData to SnpNotSharedDirtyFwd target and queue either SendSnpFwdedData or SendSnpFwdedResp";
|
|
SendSnpOnceFwdCompData, desc="Send CompData to SnpOnceFwd target and queue either SendSnpFwdedData or SendSnpFwdedResp";
|
|
SendSnpFwdedData, desc="Send SnpResp for a forwarding snoop";
|
|
SendSnpFwdedResp, desc="Send SnpRespData for a forwarding snoop";
|
|
|
|
// DVM sends
|
|
DvmSync_Send, desc="Send an unstarted DVM Sync";
|
|
|
|
// Retry handling
|
|
SendRetryAck, desc="Send RetryAck";
|
|
SendPCrdGrant, desc="Send PCrdGrant";
|
|
DoRetry, desc="Resend the current pending request";
|
|
DoRetry_Hazard, desc="DoRetry during a hazard";
|
|
|
|
// Misc triggers
|
|
LoadHit, desc="Complete a load hit";
|
|
StoreHit, desc="Complete a store hit";
|
|
AtomicHit, desc="Complete an atomic hit";
|
|
UseTimeout, desc="Transition from UD_T -> UD";
|
|
RestoreFromHazard, desc="Restore from a snoop hazard";
|
|
TX_Data, desc="Transmit pending data messages";
|
|
MaintainCoherence, desc="Queues a WriteBack or Evict before droping the only valid copy of the block";
|
|
FinishCleanUnique, desc="Sends acks and perform any writeback after a CleanUnique";
|
|
FinishCopyBack_Stale, desc="Check if a Evict needs to be sent";
|
|
ActionStalledOnHazard, desc="Stall a trigger action because until finish handling snoop hazard";
|
|
|
|
// This is triggered once a transaction doesn't have
|
|
// any queued action and is not expecting responses/data. The transaction
|
|
// is finalized and the next stable state is stored in the cache/directory
|
|
// See the processNextState and makeFinalState functions
|
|
Final, desc="";
|
|
|
|
null, desc="";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Data structures
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Cache block size
|
|
int blockSize, default="RubySystem::getBlockSizeBytes()";
|
|
|
|
// CacheEntry
|
|
structure(CacheEntry, interface="AbstractCacheEntry") {
|
|
State state, desc="SLICC line state";
|
|
DataBlock DataBlk, desc="data for the block";
|
|
bool HWPrefetched, default="false", desc="Set if this cache entry was prefetched";
|
|
}
|
|
|
|
// Directory entry
|
|
structure(DirEntry, interface="AbstractCacheEntry", main="false") {
|
|
NetDest sharers, desc="All upstream controllers that have this line (includes ownwer)";
|
|
MachineID owner, desc="Controller that has the line in UD,UC, or SD state";
|
|
bool ownerExists, default="false", desc="true if owner exists";
|
|
bool ownerIsExcl, default="false", desc="true if owner is UD or UC";
|
|
State state, desc="SLICC line state";
|
|
}
|
|
|
|
// Helper class for tracking expected response and data messages
|
|
structure(ExpectedMap, external ="yes") {
|
|
void clear(int dataChunks);
|
|
void addExpectedRespType(CHIResponseType);
|
|
void addExpectedDataType(CHIDataType);
|
|
void setExpectedCount(int val);
|
|
void addExpectedCount(int val);
|
|
bool hasExpected();
|
|
bool hasReceivedResp();
|
|
bool hasReceivedData();
|
|
int expected();
|
|
int received();
|
|
bool receiveResp(CHIResponseType);
|
|
bool receiveData(CHIDataType);
|
|
bool receivedDataType(CHIDataType);
|
|
bool receivedRespType(CHIResponseType);
|
|
}
|
|
|
|
// Tracks a pending retry
|
|
structure(RetryQueueEntry) {
|
|
Addr addr, desc="Line address";
|
|
bool usesTxnId, desc="Uses a transaction ID instead of a memory address";
|
|
MachineID retryDest, desc="Retry destination";
|
|
}
|
|
|
|
// Queue for event triggers. Used to specify a list of actions that need
|
|
// to be performed across multiple transitions.
|
|
// This class is also used to track pending retries
|
|
structure(TriggerQueue, external ="yes") {
|
|
Event front();
|
|
Event back();
|
|
bool frontNB();
|
|
bool backNB();
|
|
bool empty();
|
|
void push(Event);
|
|
void pushNB(Event);
|
|
void pushFront(Event);
|
|
void pushFrontNB(Event);
|
|
void pop();
|
|
// For the retry queue
|
|
void emplace(Addr,bool,MachineID);
|
|
RetryQueueEntry next(); //SLICC won't allow to reuse front()
|
|
}
|
|
|
|
// TBE fields
|
|
structure(TBE, desc="Transaction buffer entry definition") {
|
|
// in which table was this allocated
|
|
bool is_req_tbe, desc="Allocated in the request table";
|
|
bool is_snp_tbe, desc="Allocated in the snoop table";
|
|
bool is_repl_tbe, desc="Allocated in the replacements table";
|
|
bool is_dvm_tbe, desc="Allocated in the DVM table";
|
|
bool is_dvm_snp_tbe, desc="Allocated in the DVM snoop table";
|
|
|
|
int storSlot, desc="Slot in the storage tracker occupied by this entry";
|
|
|
|
// Transaction info mostly extracted from the request message
|
|
Addr addr, desc="Line address for this TBE";
|
|
Addr accAddr, desc="Access address for Load/Store/WriteUniquePtl; otherwisse == addr";
|
|
int accSize, desc="Access size for Load/Store/WriteUniquePtl; otherwisse == blockSize";
|
|
CHIRequestType reqType, desc="Request type that initiated this transaction";
|
|
Addr txnId, desc="Transaction ID. We default to -1 for debug purposes", default="-1";
|
|
MachineID requestor, desc="Requestor ID";
|
|
MachineID fwdRequestor, desc="Requestor to receive data on fwding snoops";
|
|
bool use_DMT, desc="Use DMT for this transaction";
|
|
bool use_DCT, desc="Use DCT for this transaction";
|
|
|
|
// if either is set prefetchers are not notified on miss/hit/fill and
|
|
// demand hit/miss stats are not incremented
|
|
bool is_local_pf, desc="Request generated by a local prefetcher";
|
|
bool is_remote_pf, desc="Request generated a prefetcher in another cache";
|
|
|
|
// Atomic info associated with the transaction
|
|
WriteMask atomic_op, desc="Atomic Operation Wrapper";
|
|
bool atomic_to_be_done, desc="We have yet to perform the atomic";
|
|
|
|
// NOTE: seqReq is a smart pointer pointing to original CPU request object
|
|
// that triggers transactions associated with this TBE. seqReq carries some
|
|
// information (e.g., PC of requesting instruction, virtual address of this
|
|
// request, etc.). Not all transactions have this field set if they are not
|
|
// triggered directly by a demand request from CPU.
|
|
RequestPtr seqReq, default="nullptr", desc="Pointer to original request from CPU/sequencer";
|
|
bool isSeqReqValid, default="false", desc="Set if seqReq is valid (not nullptr)";
|
|
|
|
// Transaction state information
|
|
State state, desc="SLICC line state";
|
|
|
|
// Transient state information. These are set at the beggining of a
|
|
// transactions and updated as data and responses are received. After
|
|
// finalizing the transactions these are used to create the next SLICC
|
|
// stable state.
|
|
bool hasUseTimeout, desc="Line is locked under store/use timeout";
|
|
DataBlock dataBlk, desc="Local copy of the line";
|
|
DataBlock oldDataBlk, desc="Local copy of the line before executing atomic";
|
|
WriteMask dataBlkValid, desc="Marks which bytes in the DataBlock are valid";
|
|
bool dataValid, desc="Local copy is valid";
|
|
bool dataAMOValid, desc="Local copy is valid for AMO";
|
|
bool dataDirty, desc="Local copy is dirtry";
|
|
bool dataMaybeDirtyUpstream, desc="Line maybe dirty upstream";
|
|
bool dataUnique, desc="Line is unique either locally or upsatream";
|
|
bool dataToBeInvalid, desc="Local copy will be invalidated at the end of transaction";
|
|
bool dataToBeSharedClean, desc="Local copy will become SC at the end of transaction";
|
|
NetDest dir_sharers, desc="Upstream controllers that have the line (includes owner)";
|
|
MachineID dir_owner, desc="Owner ID";
|
|
bool dir_ownerExists, desc="Owner ID is valid";
|
|
bool dir_ownerIsExcl, desc="Owner is UD or UC; SD otherwise";
|
|
bool doCacheFill, desc="Write valid data to the cache when completing transaction";
|
|
// NOTE: dataMaybeDirtyUpstream and dir_ownerExists are the same except
|
|
// when we had just sent dirty data upstream and are waiting for ack to set
|
|
// dir_ownerExists
|
|
|
|
// Helper structures to track expected events and additional transient
|
|
// state info
|
|
|
|
// List of actions to be performed while on a transient state
|
|
// See the processNextState function for details
|
|
TriggerQueue actions, template="<Cache_Event>", desc="List of actions";
|
|
Event pendAction, desc="Current pending action";
|
|
Tick delayNextAction, desc="Delay next action until given tick";
|
|
State finalState, desc="Final state; set when pendAction==Final";
|
|
|
|
// List of expected responses and data. Checks the type of data against the
|
|
// expected ones for debugging purposes
|
|
// See the processNextState function for details
|
|
ExpectedMap expected_req_resp, template="<CHIResponseType,CHIDataType>";
|
|
ExpectedMap expected_snp_resp, template="<CHIResponseType,CHIDataType>";
|
|
bool defer_expected_comp; // expect to receive Comp before the end of transaction
|
|
CHIResponseType slicchack1; // fix compiler not including headers
|
|
CHIDataType slicchack2; // fix compiler not including headers
|
|
|
|
// Tracks pending data messages that need to be generated when sending
|
|
// a line
|
|
bool snd_pendEv, desc="Is there a pending tx event ?";
|
|
WriteMask snd_pendBytes, desc="Which bytes are pending transmission";
|
|
CHIDataType snd_msgType, desc="Type of message being sent";
|
|
MachineID snd_destination, desc="Data destination";
|
|
|
|
// Tracks how to update the directory when receiving a CompAck
|
|
bool updateDirOnCompAck, desc="Update directory on CompAck";
|
|
bool requestorToBeOwner, desc="Sets dir_ownerExists";
|
|
bool requestorToBeExclusiveOwner, desc="Sets dir_ownerIsExcl";
|
|
// NOTE: requestor always added to dir_sharers if updateDirOnCompAck is set
|
|
|
|
// Set for incoming snoop requests
|
|
bool snpNeedsData, desc="Set if snoop requires data as response";
|
|
State fwdedState, desc="State of CompData sent due to a forwarding snoop";
|
|
bool is_req_hazard, desc="Snoop hazard with an outstanding request";
|
|
bool is_repl_hazard, desc="Snoop hazard with an outstanding writeback request";
|
|
bool is_stale, desc="Request is now stale because of a snoop hazard";
|
|
|
|
// Tracks requests sent downstream
|
|
CHIRequestType pendReqType, desc="Sent request type";
|
|
bool pendReqAllowRetry, desc="Sent request can be retried";
|
|
bool rcvdRetryAck, desc="Received a RetryAck";
|
|
bool rcvdRetryCredit, desc="Received a PCrdGrant";
|
|
// NOTE: the message is retried only after receiving both RetryAck and
|
|
// PCrdGrant. A request can be retried only once.
|
|
// These are a copy of the retry msg fields in case we need to retry
|
|
Addr pendReqAccAddr;
|
|
int pendReqAccSize;
|
|
NetDest pendReqDest;
|
|
bool pendReqD2OrigReq;
|
|
bool pendReqRetToSrc;
|
|
|
|
// This TBE stalled a message and thus we need to call wakeUpBuffers
|
|
// at some point
|
|
bool wakeup_pending_req;
|
|
bool wakeup_pending_snp;
|
|
bool wakeup_pending_tgr;
|
|
}
|
|
|
|
// TBE table definition
|
|
structure(TBETable, external ="yes") {
|
|
TBE lookup(Addr);
|
|
void allocate(Addr);
|
|
void deallocate(Addr);
|
|
bool isPresent(Addr);
|
|
}
|
|
|
|
structure(TBEStorage, external ="yes") {
|
|
int size();
|
|
int capacity();
|
|
int reserved();
|
|
int slotsAvailable();
|
|
bool areNSlotsAvailable(int n);
|
|
void incrementReserved();
|
|
void decrementReserved();
|
|
int addEntryToNewSlot();
|
|
void addEntryToSlot(int slot);
|
|
void removeEntryFromSlot(int slot);
|
|
}
|
|
|
|
// Directory memory definition
|
|
structure(PerfectCacheMemory, external = "yes") {
|
|
void allocate(Addr);
|
|
void deallocate(Addr);
|
|
DirEntry lookup(Addr);
|
|
bool isTagPresent(Addr);
|
|
}
|
|
|
|
// Directory
|
|
PerfectCacheMemory directory, template="<Cache_DirEntry>";
|
|
|
|
// Tracks unique lines locked after a store miss
|
|
TimerTable useTimerTable;
|
|
|
|
// Multiplies sc_lock_base_latency to obtain the lock timeout.
|
|
// This is incremented at Profile_Eviction and decays on
|
|
// store miss completion
|
|
int sc_lock_multiplier, default="0";
|
|
|
|
// Definitions of the TBE tables
|
|
|
|
// Main TBE table used for incoming requests
|
|
TBETable TBEs, template="<Cache_TBE>", constructor="m_number_of_TBEs";
|
|
TBEStorage storTBEs, constructor="this, m_number_of_TBEs";
|
|
|
|
// TBE table for WriteBack/Evict requests generated by a replacement
|
|
// Notice storTBEs will be used when unify_repl_TBEs is set
|
|
TBETable replTBEs, template="<Cache_TBE>", constructor="m_unify_repl_TBEs ? m_number_of_TBEs : m_number_of_repl_TBEs";
|
|
TBEStorage storReplTBEs, constructor="this, m_number_of_repl_TBEs";
|
|
|
|
// TBE table for incoming snoops
|
|
TBETable snpTBEs, template="<Cache_TBE>", constructor="m_number_of_snoop_TBEs";
|
|
TBEStorage storSnpTBEs, constructor="this, m_number_of_snoop_TBEs";
|
|
|
|
// TBE table for outgoing DVM requests
|
|
TBETable dvmTBEs, template="<Cache_TBE>", constructor="m_number_of_DVM_TBEs";
|
|
TBEStorage storDvmTBEs, constructor="this, m_number_of_DVM_TBEs";
|
|
|
|
// TBE table for incoming DVM snoops
|
|
TBETable dvmSnpTBEs, template="<Cache_TBE>", constructor="m_number_of_DVM_snoop_TBEs";
|
|
TBEStorage storDvmSnpTBEs, constructor="this, m_number_of_DVM_snoop_TBEs";
|
|
|
|
// DVM data
|
|
// Queue of non-sync operations that haven't been Comp-d yet.
|
|
// Before a Sync operation can start, this queue must be emptied
|
|
TriggerQueue dvmPendingNonSyncsBlockingSync, template="<Cache_Event>";
|
|
// Used to record if a Sync op is pending
|
|
bool dvmHasPendingSyncOp, default="false";
|
|
Addr dvmPendingSyncOp, default="0";
|
|
|
|
// Retry handling
|
|
|
|
// Destinations that will be sent PCrdGrant when a TBE becomes available
|
|
TriggerQueue retryQueue, template="<Cache_RetryQueueEntry>";
|
|
|
|
|
|
// Pending RetryAck/PCrdGrant/DoRetry
|
|
structure(RetryTriggerMsg, interface="Message") {
|
|
Addr addr;
|
|
Event event;
|
|
MachineID retryDest;
|
|
bool usesTxnId;
|
|
Addr txnId;
|
|
|
|
bool functionalRead(Packet *pkt) { return false; }
|
|
bool functionalRead(Packet *pkt, WriteMask &mask) { return false; }
|
|
bool functionalWrite(Packet *pkt) { return false; }
|
|
}
|
|
|
|
// Destinations from we received a RetryAck. Sending new requests to these
|
|
// destinations will be blocked until a PCrdGrant is received if
|
|
// throttle_req_on_retry is set
|
|
NetDest destsWaitingRetry;
|
|
|
|
// Pending transaction actions (generated by TBE:actions)
|
|
structure(TriggerMsg, interface="Message") {
|
|
Addr addr;
|
|
bool usesTxnId;
|
|
bool from_hazard; // this actions was generate during a snoop hazard
|
|
bool functionalRead(Packet *pkt) { return false; }
|
|
bool functionalRead(Packet *pkt, WriteMask &mask) { return false; }
|
|
bool functionalWrite(Packet *pkt) { return false; }
|
|
}
|
|
|
|
// Internal replacement request
|
|
structure(ReplacementMsg, interface="Message") {
|
|
Addr addr;
|
|
Addr from_addr;
|
|
int slot; // set only when unify_repl_TBEs is set
|
|
bool functionalRead(Packet *pkt) { return false; }
|
|
bool functionalRead(Packet *pkt, WriteMask &mask) { return false; }
|
|
bool functionalWrite(Packet *pkt) { return false; }
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Input/output port definitions
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
include "CHI-cache-ports.sm";
|
|
// CHI-cache-ports.sm also includes CHI-cache-funcs.sm
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Actions and transitions
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
include "CHI-cache-actions.sm";
|
|
include "CHI-cache-transitions.sm";
|
|
}
|