| /** |
| * Copyright (c) 2019 Metempsy Technology Consulting |
| * All rights reserved. |
| * |
| * 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. |
| */ |
| |
| #include "mem/cache/prefetch/spatio_temporal_memory_streaming.hh" |
| |
| #include "debug/HWPrefetch.hh" |
| #include "mem/cache/prefetch/associative_set_impl.hh" |
| #include "params/STeMSPrefetcher.hh" |
| |
| namespace Prefetcher { |
| |
| STeMS::STeMS(const STeMSPrefetcherParams *p) |
| : Queued(p), spatialRegionSize(p->spatial_region_size), |
| spatialRegionSizeBits(floorLog2(p->spatial_region_size)), |
| reconstructionEntries(p->reconstruction_entries), |
| activeGenerationTable(p->active_generation_table_assoc, |
| p->active_generation_table_entries, |
| p->active_generation_table_indexing_policy, |
| p->active_generation_table_replacement_policy, |
| ActiveGenerationTableEntry( |
| spatialRegionSize / blkSize)), |
| patternSequenceTable(p->pattern_sequence_table_assoc, |
| p->pattern_sequence_table_entries, |
| p->pattern_sequence_table_indexing_policy, |
| p->pattern_sequence_table_replacement_policy, |
| ActiveGenerationTableEntry( |
| spatialRegionSize / blkSize)), |
| rmob(p->region_miss_order_buffer_entries) |
| { |
| fatal_if(!isPowerOf2(spatialRegionSize), |
| "The spatial region size must be a power of 2."); |
| } |
| |
| void |
| STeMS::checkForActiveGenerationsEnd() |
| { |
| // This prefetcher operates attached to the L1 and it observes all |
| // accesses, this guarantees that no evictions are missed |
| |
| // Iterate over all entries, if any recorded cacheline has been evicted, |
| // the generation finishes, move the entry to the PST |
| for (auto &agt_entry : activeGenerationTable) { |
| if (agt_entry.isValid()) { |
| bool generation_ended = false; |
| bool sr_is_secure = agt_entry.isSecure(); |
| for (auto &seq_entry : agt_entry.sequence) { |
| if (seq_entry.counter > 0) { |
| Addr cache_addr = |
| agt_entry.paddress + seq_entry.offset * blkSize; |
| if (!inCache(cache_addr, sr_is_secure) && |
| !inMissQueue(cache_addr, sr_is_secure)) { |
| generation_ended = true; |
| break; |
| } |
| } |
| } |
| if (generation_ended) { |
| // PST is indexed using the PC (secure bit is unused) |
| ActiveGenerationTableEntry *pst_entry = |
| patternSequenceTable.findEntry(agt_entry.pc, |
| false /*unused*/); |
| if (pst_entry == nullptr) { |
| // Tipically an entry will not exist |
| pst_entry = patternSequenceTable.findVictim(agt_entry.pc); |
| assert(pst_entry != nullptr); |
| patternSequenceTable.insertEntry(agt_entry.pc, |
| false /*unused*/, pst_entry); |
| } else { |
| patternSequenceTable.accessEntry(pst_entry); |
| } |
| // If the entry existed, this will update the values, if not, |
| // this also sets the values of the entry |
| pst_entry->update(agt_entry); |
| // Free the AGT entry |
| activeGenerationTable.invalidate(&agt_entry); |
| } |
| } |
| } |
| } |
| |
| void |
| STeMS::addToRMOB(Addr sr_addr, Addr pst_addr, unsigned int delta) |
| { |
| RegionMissOrderBufferEntry rmob_entry; |
| rmob_entry.srAddress = sr_addr; |
| rmob_entry.pstAddress = pst_addr; |
| rmob_entry.delta = delta; |
| |
| rmob.push_back(rmob_entry); |
| } |
| |
| void |
| STeMS::calculatePrefetch(const PrefetchInfo &pfi, |
| std::vector<AddrPriority> &addresses) |
| { |
| if (!pfi.hasPC()) { |
| DPRINTF(HWPrefetch, "Ignoring request with no PC.\n"); |
| return; |
| } |
| |
| Addr pc = pfi.getPC(); |
| bool is_secure = pfi.isSecure(); |
| // Spatial region address |
| Addr sr_addr = pfi.getAddr() / spatialRegionSize; |
| Addr paddr = pfi.getPaddr(); |
| |
| // Offset in cachelines within the spatial region |
| Addr sr_offset = (pfi.getAddr() % spatialRegionSize) / blkSize; |
| |
| // Check if any active generation has ended |
| checkForActiveGenerationsEnd(); |
| |
| ActiveGenerationTableEntry *agt_entry = |
| activeGenerationTable.findEntry(sr_addr, is_secure); |
| if (agt_entry != nullptr) { |
| // found an entry in the AGT, entry is currently being recorded, |
| // add the offset |
| activeGenerationTable.accessEntry(agt_entry); |
| agt_entry->addOffset(sr_offset); |
| lastTriggerCounter += 1; |
| } else { |
| // Not found, this is the first access (Trigger access) |
| |
| // Add entry to RMOB |
| Addr pst_addr = (pc << spatialRegionSizeBits) + sr_offset; |
| addToRMOB(sr_addr, pst_addr, lastTriggerCounter); |
| // Reset last trigger counter |
| lastTriggerCounter = 0; |
| |
| // allocate a new AGT entry |
| agt_entry = activeGenerationTable.findVictim(sr_addr); |
| assert(agt_entry != nullptr); |
| activeGenerationTable.insertEntry(sr_addr, is_secure, agt_entry); |
| agt_entry->pc = pc; |
| agt_entry->paddress = paddr; |
| agt_entry->addOffset(sr_offset); |
| } |
| // increase the seq Counter for other entries |
| for (auto &agt_e : activeGenerationTable) { |
| if (agt_e.isValid() && agt_entry != &agt_e) { |
| agt_e.seqCounter += 1; |
| } |
| } |
| |
| // Prefetch generation: if this is a miss, search for the most recent |
| // entry in the RMOB, and reconstruct the registered access sequence |
| if (pfi.isCacheMiss()) { |
| auto it = rmob.end(); |
| while (it != rmob.begin()) { |
| --it; |
| if (it->srAddress == sr_addr) { |
| // reconstruct the access sequence |
| reconstructSequence(it, addresses); |
| break; |
| } |
| } |
| } |
| } |
| |
| void |
| STeMS::reconstructSequence( |
| CircularQueue<RegionMissOrderBufferEntry>::iterator rmob_it, |
| std::vector<AddrPriority> &addresses) |
| { |
| std::vector<Addr> reconstruction(reconstructionEntries, MaxAddr); |
| unsigned int idx = 0; |
| |
| // Process rmob entries from rmob_it (most recent with address = sr_addr) |
| // to the latest one |
| for (auto it = rmob_it; it != rmob.end() && (idx < reconstructionEntries); |
| it++) { |
| reconstruction[idx] = it->srAddress * spatialRegionSize; |
| idx += (it+1)->delta + 1; |
| } |
| |
| // Now query the PST with the PC of each RMOB entry |
| idx = 0; |
| for (auto it = rmob_it; it != rmob.end() && (idx < reconstructionEntries); |
| it++) { |
| ActiveGenerationTableEntry *pst_entry = |
| patternSequenceTable.findEntry(it->pstAddress, false /* unused */); |
| if (pst_entry != nullptr) { |
| patternSequenceTable.accessEntry(pst_entry); |
| for (auto &seq_entry : pst_entry->sequence) { |
| if (seq_entry.counter > 1) { |
| // 2-bit counter: high enough confidence with a |
| // value greater than 1 |
| Addr rec_addr = it->srAddress * spatialRegionSize + |
| seq_entry.offset; |
| unsigned ridx = idx + seq_entry.delta; |
| // Try to use the corresponding position, if it has been |
| // already used, look the surrounding positions |
| if (ridx < reconstructionEntries && |
| reconstruction[ridx] == MaxAddr) { |
| reconstruction[ridx] = rec_addr; |
| } else if ((ridx + 1) < reconstructionEntries && |
| reconstruction[ridx + 1] == MaxAddr) { |
| reconstruction[ridx + 1] = rec_addr; |
| } else if ((ridx + 2) < reconstructionEntries && |
| reconstruction[ridx + 2] == MaxAddr) { |
| reconstruction[ridx + 2] = rec_addr; |
| } else if ((ridx > 0) && |
| ((ridx - 1) < reconstructionEntries) && |
| reconstruction[ridx - 1] == MaxAddr) { |
| reconstruction[ridx - 1] = rec_addr; |
| } else if ((ridx > 1) && |
| ((ridx - 2) < reconstructionEntries) && |
| reconstruction[ridx - 2] == MaxAddr) { |
| reconstruction[ridx - 2] = rec_addr; |
| } |
| } |
| } |
| } |
| idx += (it+1)->delta + 1; |
| } |
| |
| for (Addr pf_addr : reconstruction) { |
| if (pf_addr != MaxAddr) { |
| addresses.push_back(AddrPriority(pf_addr, 0)); |
| } |
| } |
| } |
| |
| } // namespace Prefetcher |
| |
| Prefetcher::STeMS* |
| STeMSPrefetcherParams::create() |
| { |
| return new Prefetcher::STeMS(this); |
| } |