Cycles response_latency := 2;
bool send_evictions;
+ Prefetcher * prefetcher;
+ bool enable_prefetch := "False";
+
// From this node's L0 cache to the network
MessageBuffer * bufferToL1, network="To";
// Message queue between this controller and the processor
MessageBuffer * mandatoryQueue;
+
+ // Request Buffer for prefetches
+ MessageBuffer * prefetchQueue;
{
// STATES
state_declaration(State, desc="Cache states", default="L0Cache_State_I") {
// processor needs to write to it. So, the controller has requested for
// write permission.
SM, AccessPermission:Read_Only, desc="Issued GETX, have not seen response yet";
+
+ // Transient states in which block is being prefetched
+ PF_Inst_IS, AccessPermission:Busy, desc="Issued GETS, have not seen response yet";
+ PF_IS, AccessPermission:Busy, desc="Issued GETS, have not seen response yet";
+ PF_IE, AccessPermission:Busy, desc="Issued GETX, have not seen response yet";
}
// EVENTS
WB_Ack, desc="Ack for replacement";
Failed_SC, desc="Store conditional request that will fail";
+
+ // Prefetch events (generated by prefetcher)
+ PF_L0_Replacement, desc="L0 Replacement caused by pretcher", format="!pr";
+ PF_Load, desc="Load request from prefetcher";
+ PF_Ifetch, desc="Instruction fetch request from prefetcher";
+ PF_Store, desc="Exclusive load request from prefetcher";
}
// TYPES
State CacheState, desc="cache state";
DataBlock DataBlk, desc="data for the block";
bool Dirty, default="false", desc="data is dirty";
+ bool isPrefetched, default="false", desc="Set if this block was prefetched";
}
// TBE fields
void allocate(Addr);
void deallocate(Addr);
bool isPresent(Addr);
+ TBE getNullEntry();
}
TBETable TBEs, template="<L0Cache_TBE>", constructor="m_number_of_TBEs";
void wakeUpBuffers(Addr a);
void wakeUpAllBuffers(Addr a);
void profileMsgDelay(int virtualNetworkType, Cycles c);
+ MachineID mapAddressToMachine(Addr addr, MachineType mtype);
// inclusive cache returns L0 entries only
Entry getCacheEntry(Addr addr), return_by_pointer="yes" {
}
}
+ Event prefetch_request_type_to_event(RubyRequestType type) {
+ if (type == RubyRequestType:LD) {
+ return Event:PF_Load;
+ } else if (type == RubyRequestType:IFETCH) {
+ return Event:PF_Ifetch;
+ } else if (type == RubyRequestType:ST) {
+ return Event:PF_Store;
+ } else {
+ error("Invalid RubyRequestType");
+ }
+ }
+
int getPendingAcks(TBE tbe) {
return tbe.pendingAcks;
}
out_port(requestNetwork_out, CoherenceMsg, bufferToL1);
+ out_port(optionalQueue_out, RubyRequest, prefetchQueue);
+
+ void enqueuePrefetch(Addr address, RubyRequestType type) {
+ enqueue(optionalQueue_out, RubyRequest, 1) {
+ out_msg.LineAddress := address;
+ out_msg.Type := type;
+ out_msg.Prefetch := PrefetchBit:Yes;
+ out_msg.AccessMode := RubyAccessMode:Supervisor;
+ }
+ }
+
+ // Prefetch queue between the controller and the prefetcher
+ // As per Spracklen et al. (HPCA 2005), the prefetch queue should be
+ // implemented as a LIFO structure. The structure would allow for fast
+ // searches of all entries in the queue, not just the head msg. All
+ // msgs in the structure can be invalidated if a demand miss matches.
+ in_port(optionalQueue_in, RubyRequest, prefetchQueue, desc="...", rank = 2) {
+ if (optionalQueue_in.isReady(clockEdge())) {
+ peek(optionalQueue_in, RubyRequest) {
+ if (in_msg.Type == RubyRequestType:IFETCH) {
+ // Instruction Prefetch
+ Entry icache_entry := getICacheEntry(in_msg.LineAddress);
+ if (is_valid(icache_entry)) {
+ // The block to be prefetched is already present in the
+ // cache. This request will be made benign and cause the
+ // prefetch queue to be popped.
+ trigger(prefetch_request_type_to_event(in_msg.Type),
+ in_msg.LineAddress,
+ icache_entry, TBEs[in_msg.LineAddress]);
+ }
+
+ // Check to see if it is in the L0-D
+ Entry cache_entry := getDCacheEntry(in_msg.LineAddress);
+ if (is_valid(cache_entry)) {
+ // The block is in the wrong L0 cache. We should drop
+ // this request.
+ trigger(prefetch_request_type_to_event(in_msg.Type),
+ in_msg.LineAddress,
+ cache_entry, TBEs[in_msg.LineAddress]);
+ }
+
+ if (Icache.cacheAvail(in_msg.LineAddress)) {
+ // L0-I does't have the line, but we have space for it
+ // in the L0-I so let's see if the L1 has it
+ trigger(prefetch_request_type_to_event(in_msg.Type),
+ in_msg.LineAddress,
+ icache_entry, TBEs[in_msg.LineAddress]);
+ } else {
+ // No room in the L0-I, so we need to make room in the L0-I
+ Addr addr := Icache.cacheProbe(in_msg.LineAddress);
+ check_on_cache_probe(optionalQueue_in, addr);
+
+ trigger(Event:PF_L0_Replacement, addr,
+ getICacheEntry(addr),
+ TBEs[addr]);
+ }
+ } else {
+ // Data prefetch
+ Entry cache_entry := getDCacheEntry(in_msg.LineAddress);
+ if (is_valid(cache_entry)) {
+ // The block to be prefetched is already present in the
+ // cache. This request will be made benign and cause the
+ // prefetch queue to be popped.
+ trigger(prefetch_request_type_to_event(in_msg.Type),
+ in_msg.LineAddress,
+ cache_entry, TBEs[in_msg.LineAddress]);
+ }
+
+ // Check to see if it is in the L0-I
+ Entry icache_entry := getICacheEntry(in_msg.LineAddress);
+ if (is_valid(icache_entry)) {
+ // The block is in the wrong L0. Just drop the prefetch
+ // request.
+ trigger(prefetch_request_type_to_event(in_msg.Type),
+ in_msg.LineAddress,
+ icache_entry, TBEs[in_msg.LineAddress]);
+ }
+
+ if (Dcache.cacheAvail(in_msg.LineAddress)) {
+ // L0-D does't have the line, but we have space for it in
+ // the L0-D let's see if the L1 has it
+ trigger(prefetch_request_type_to_event(in_msg.Type),
+ in_msg.LineAddress,
+ cache_entry, TBEs[in_msg.LineAddress]);
+ } else {
+ // No room in the L0-D, so we need to make room in the L0-D
+ Addr addr := Dcache.cacheProbe(in_msg.LineAddress);
+ check_on_cache_probe(optionalQueue_in, addr);
+
+ trigger(Event:PF_L0_Replacement, addr,
+ getDCacheEntry(addr),
+ TBEs[addr]);
+ }
+ }
+ }
+ }
+ }
// Messages for this L0 cache from the L1 cache
in_port(messgeBuffer_in, CoherenceMsg, bufferFromL1, rank = 1) {
sequencer.writeCallbackScFail(address, cache_entry.DataBlk);
}
+ // prefetching
+
+ action(pa_issuePfGETS, "pa", desc="Issue prefetch GETS") {
+ peek(optionalQueue_in, RubyRequest) {
+ enqueue(requestNetwork_out, CoherenceMsg, request_latency) {
+ out_msg.addr := address;
+ out_msg.Class := CoherenceClass:GETS;
+ out_msg.Sender := machineID;
+ out_msg.Dest := createMachineID(MachineType:L1Cache, version);
+ DPRINTF(RubySlicc, "address: %#x, destination: %s\n",
+ address, out_msg.Dest);
+ out_msg.MessageSize := MessageSizeType:Control;
+ out_msg.Prefetch := in_msg.Prefetch;
+ out_msg.AccessMode := in_msg.AccessMode;
+ }
+ }
+ }
+
+ action(pb_issuePfGETX, "pb", desc="Issue prefetch GETX") {
+ peek(optionalQueue_in, RubyRequest) {
+ enqueue(requestNetwork_out, CoherenceMsg, request_latency) {
+ out_msg.addr := address;
+ out_msg.Class := CoherenceClass:GETX;
+ out_msg.Sender := machineID;
+ DPRINTF(RubySlicc, "%s\n", machineID);
+ out_msg.Dest := createMachineID(MachineType:L1Cache, version);
+
+ DPRINTF(RubySlicc, "address: %#x, destination: %s\n",
+ address, out_msg.Dest);
+ out_msg.MessageSize := MessageSizeType:Control;
+ out_msg.Prefetch := in_msg.Prefetch;
+ out_msg.AccessMode := in_msg.AccessMode;
+ }
+ }
+ }
+
+ action(pq_popPrefetchQueue, "\pq", desc="Pop the prefetch request queue") {
+ optionalQueue_in.dequeue(clockEdge());
+ }
+
+ action(mp_markPrefetched, "mp", desc="Write data from response queue to cache") {
+ assert(is_valid(cache_entry));
+ cache_entry.isPrefetched := true;
+ }
+
+ action(po_observeMiss, "\po", desc="Inform the prefetcher about a cache miss") {
+ peek(mandatoryQueue_in, RubyRequest) {
+ if (enable_prefetch) {
+ prefetcher.observeMiss(in_msg.LineAddress, in_msg.Type);
+ }
+ }
+ }
+
+ action(ppm_observePfMiss, "\ppm",
+ desc="Inform the prefetcher about a cache miss with in-flight prefetch") {
+ peek(mandatoryQueue_in, RubyRequest) {
+ prefetcher.observePfMiss(in_msg.LineAddress);
+ }
+ }
+
+ action(pph_observePfHit, "\pph",
+ desc="Inform the prefetcher if a cache hit was the result of a prefetch") {
+ peek(mandatoryQueue_in, RubyRequest) {
+ if (cache_entry.isPrefetched) {
+ prefetcher.observePfHit(in_msg.LineAddress);
+ cache_entry.isPrefetched := false;
+ }
+ }
+ }
+
+ action(z_stallAndWaitOptionalQueue, "\pz", desc="recycle prefetch request queue") {
+ stall_and_wait(optionalQueue_in, address);
+ }
+
//*****************************************************
// TRANSITIONS
//*****************************************************
i_allocateTBE;
a_issueGETS;
uu_profileDataMiss;
+ po_observeMiss;
k_popMandatoryQueue;
}
i_allocateTBE;
a_issueGETS;
uu_profileInstMiss;
+ po_observeMiss;
k_popMandatoryQueue;
}
i_allocateTBE;
b_issueGETX;
uu_profileDataMiss;
+ po_observeMiss;
k_popMandatoryQueue;
}
transition({S,E,M}, Load) {
h_load_hit;
uu_profileDataHit;
+ pph_observePfHit;
k_popMandatoryQueue;
}
transition({S,E,M}, Ifetch) {
h_ifetch_hit;
uu_profileInstHit;
+ pph_observePfHit;
k_popMandatoryQueue;
}
k_popMandatoryQueue;
}
- transition(S, L0_Replacement, I) {
+ transition(S, {L0_Replacement,PF_L0_Replacement}, I) {
forward_eviction_to_cpu;
ff_deallocateCacheBlock;
}
transition({E,M}, Store, M) {
hh_store_hit;
uu_profileDataHit;
+ pph_observePfHit;
k_popMandatoryQueue;
}
- transition(E, L0_Replacement, I) {
+ transition(E, {L0_Replacement,PF_L0_Replacement}, I) {
forward_eviction_to_cpu;
g_issuePUTX;
ff_deallocateCacheBlock;
}
// Transitions from Modified
- transition(M, L0_Replacement, I) {
+ transition(M, {L0_Replacement,PF_L0_Replacement}, I) {
forward_eviction_to_cpu;
g_issuePUTX;
ff_deallocateCacheBlock;
hhc_storec_fail;
k_popMandatoryQueue;
}
+
+ // prefetcher
+
+ transition({Inst_IS, IS, IM, SM, PF_Inst_IS, PF_IS, PF_IE}, PF_L0_Replacement) {
+ z_stallAndWaitOptionalQueue;
+ }
+
+ transition({PF_Inst_IS, PF_IS}, {Store, L0_Replacement}) {
+ z_stallAndWaitMandatoryQueue;
+ }
+
+ transition({PF_IE}, {Load, Ifetch, L0_Replacement}) {
+ z_stallAndWaitMandatoryQueue;
+ }
+
+ transition({S,E,M,Inst_IS,IS,IM,SM,PF_Inst_IS,PF_IS,PF_IE},
+ {PF_Load, PF_Store, PF_Ifetch}) {
+ pq_popPrefetchQueue;
+ }
+
+ transition(I, PF_Load, PF_IS) {
+ oo_allocateDCacheBlock;
+ i_allocateTBE;
+ pa_issuePfGETS;
+ pq_popPrefetchQueue;
+ }
+
+ transition(PF_IS, Load, IS) {
+ uu_profileDataMiss;
+ ppm_observePfMiss;
+ k_popMandatoryQueue;
+ }
+
+ transition(I, PF_Ifetch, PF_Inst_IS) {
+ pp_allocateICacheBlock;
+ i_allocateTBE;
+ pa_issuePfGETS;
+ pq_popPrefetchQueue;
+ }
+
+ transition(PF_Inst_IS, Ifetch, Inst_IS) {
+ uu_profileInstMiss;
+ ppm_observePfMiss;
+ k_popMandatoryQueue;
+ }
+
+ transition(I, PF_Store, PF_IE) {
+ oo_allocateDCacheBlock;
+ i_allocateTBE;
+ pb_issuePfGETX;
+ pq_popPrefetchQueue;
+ }
+
+ transition(PF_IE, Store, IM) {
+ uu_profileDataMiss;
+ ppm_observePfMiss;
+ k_popMandatoryQueue;
+ }
+
+ transition({PF_Inst_IS, PF_IS, PF_IE}, {InvOwn, InvElse}) {
+ fi_sendInvAck;
+ l_popRequestQueue;
+ }
+
+ transition(PF_IS, Data, S) {
+ u_writeDataToCache;
+ s_deallocateTBE;
+ mp_markPrefetched;
+ o_popIncomingResponseQueue;
+ kd_wakeUpDependents;
+ }
+
+ transition(PF_IS, Data_Exclusive, E) {
+ u_writeDataToCache;
+ s_deallocateTBE;
+ mp_markPrefetched;
+ o_popIncomingResponseQueue;
+ kd_wakeUpDependents;
+ }
+
+ transition(PF_IS, Data_Stale, I) {
+ u_writeDataToCache;
+ s_deallocateTBE;
+ mp_markPrefetched;
+ ff_deallocateCacheBlock;
+ o_popIncomingResponseQueue;
+ kd_wakeUpDependents;
+ }
+
+ transition(PF_Inst_IS, Data, S) {
+ u_writeInstToCache;
+ s_deallocateTBE;
+ mp_markPrefetched;
+ o_popIncomingResponseQueue;
+ kd_wakeUpDependents;
+ }
+
+ transition(PF_Inst_IS, Data_Exclusive, E) {
+ u_writeInstToCache;
+ s_deallocateTBE;
+ mp_markPrefetched;
+ o_popIncomingResponseQueue;
+ kd_wakeUpDependents;
+ }
+
+ transition(PF_IE, Data_Exclusive, E) {
+ u_writeDataToCache;
+ s_deallocateTBE;
+ mp_markPrefetched;
+ o_popIncomingResponseQueue;
+ kd_wakeUpDependents;
+ }
}