| /* | |
| * 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. | |
| */ | |
| 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 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; | |
| // 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; | |
| // Use separate Comp/DBIDResp responses for WriteUnique | |
| bool comp_wu := "False"; | |
| // additional latency for the WU Comp response | |
| Cycles comp_wu_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; | |
| // 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=""; | |
| Store, desc=""; | |
| Prefetch, desc=""; | |
| ReadShared, desc=""; | |
| ReadNotSharedDirty, desc=""; | |
| ReadUnique, desc=""; | |
| ReadUnique_PoC, desc=""; | |
| ReadOnce, desc=""; | |
| CleanUnique, desc=""; | |
| Evict, desc=""; | |
| WriteBackFull, desc=""; | |
| WriteEvictFull, desc=""; | |
| WriteCleanFull, desc=""; | |
| WriteUnique, desc=""; | |
| WriteUniquePtl_PoC, desc=""; | |
| WriteUniqueFull_PoC, desc=""; | |
| WriteUniqueFull_PoC_Alloc, desc=""; | |
| SnpCleanInvalid, desc=""; | |
| SnpShared, desc=""; | |
| SnpSharedFwd, desc=""; | |
| SnpNotSharedDirtyFwd, desc=""; | |
| SnpUnique, desc=""; | |
| SnpUniqueFwd, desc=""; | |
| SnpOnce, desc=""; | |
| SnpOnceFwd, desc=""; | |
| SnpStalled, desc=""; // A snoop stall triggered from the inport | |
| // DVM sequencer requests | |
| DvmTlbi_Initiate, desc=""; // 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=""; // 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=""; | |
| SnpDvmOpSync_P2, desc=""; | |
| SnpDvmOpNonSync_P1, desc=""; | |
| SnpDvmOpNonSync_P2, desc=""; | |
| // 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=""; | |
| WriteBackFull_Stale, desc=""; | |
| WriteEvictFull_Stale, desc=""; | |
| WriteCleanFull_Stale, desc=""; | |
| CleanUnique_Stale, desc=""; | |
| // 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, desc="Evicts/WB the local copy of the line"; | |
| LocalHN_Eviction, desc="Local_Eviction triggered when is HN"; | |
| Global_Eviction, 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"; | |
| 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, desc="Send a ReadShared or ReadNotSharedDirty is allow_SD is false"; | |
| SendReadOnce, desc="Send a ReadOnce"; | |
| SendReadNoSnp, desc="Send a SendReadNoSnp"; | |
| SendReadNoSnpDMT, desc="Send a SendReadNoSnp using DMT"; | |
| SendReadUnique, 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, desc="Send a WriteBackFull (if line is UD or SD) or WriteEvictFull (if UC)"; | |
| SendWriteClean, desc="Send a WriteCleanFull"; | |
| SendWriteNoSnp, desc="Send a WriteNoSnp for a full line"; | |
| SendWriteNoSnpPartial, desc="Send a WriteNoSnpPtl"; | |
| SendWriteUnique, 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"; | |
| // Dataless requests | |
| SendEvict, desc="Send a Evict"; | |
| SendCompIResp, desc="Ack Evict with Comp_I"; | |
| SendCleanUnique,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"; | |
| 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"; | |
| 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"; | |
| // 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"; | |
| WriteMask dataBlkValid, desc="Marks which bytes in the DataBlock are valid"; | |
| bool dataValid, desc="Local copy is valid"; | |
| 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; | |
| 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"; | |
| } |