2 * Copyright (c) 2020 Inria
3 * Copyright (c) 2020 ARM Limited
6 * The license below extends only to copyright in the software and shall
7 * not be construed as granting a license to any other intellectual
8 * property including but not limited to intellectual property relating
9 * to a hardware implementation of the functionality of the software
10 * licensed hereunder. You may use the software subject to the license
11 * terms below provided that you ensure that this notice is replicated
12 * unmodified and in its entirety in all distributions of the software,
13 * modified or unmodified, in source code or in binary form.
15 * Copyright (c) 1999-2012 Mark D. Hill and David A. Wood
16 * All rights reserved.
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions are
20 * met: redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer;
22 * redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution;
25 * neither the name of the copyright holders nor the names of its
26 * contributors may be used to endorse or promote products derived from
27 * this software without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 #include "mem/ruby/structures/RubyPrefetcher.hh"
46 #include "base/bitfield.hh"
47 #include "debug/RubyPrefetcher.hh"
48 #include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh"
49 #include "mem/ruby/system/RubySystem.hh"
51 RubyPrefetcher::RubyPrefetcher(const Params
&p
)
52 : SimObject(p
), m_num_streams(p
.num_streams
),
53 m_array(p
.num_streams
), m_train_misses(p
.train_misses
),
54 m_num_startup_pfs(p
.num_startup_pfs
),
55 unitFilter(p
.unit_filter
),
56 negativeFilter(p
.unit_filter
),
57 nonUnitFilter(p
.nonunit_filter
),
58 m_prefetch_cross_pages(p
.cross_page
),
59 m_page_shift(p
.sys
->getPageShift()),
60 rubyPrefetcherStats(this)
62 assert(m_num_streams
> 0);
63 assert(m_num_startup_pfs
<= MAX_PF_INFLIGHT
);
67 RubyPrefetcherStats::RubyPrefetcherStats(Stats::Group
*parent
)
68 : Stats::Group(parent
, "RubyPrefetcher"),
69 ADD_STAT(numMissObserved
, "Number of misses observed"),
70 ADD_STAT(numAllocatedStreams
, "Number of streams allocated for "
72 ADD_STAT(numPrefetchRequested
, "Number of prefetch requests made"),
73 ADD_STAT(numHits
, "Number of prefetched blocks accessed "
74 "(for the first time)"),
75 ADD_STAT(numPartialHits
, "Number of misses observed for a block being "
77 ADD_STAT(numPagesCrossed
, "Number of prefetches across pages"),
78 ADD_STAT(numMissedPrefetchedBlocks
, "Number of misses for blocks that "
79 "were prefetched, yet missed")
84 RubyPrefetcher::observeMiss(Addr address
, const RubyRequestType
& type
)
86 DPRINTF(RubyPrefetcher
, "Observed miss for %#x\n", address
);
87 Addr line_addr
= makeLineAddress(address
);
88 rubyPrefetcherStats
.numMissObserved
++;
90 // check to see if we have already issued a prefetch for this block
92 PrefetchEntry
*pfEntry
= getPrefetchEntry(line_addr
, index
);
93 if (pfEntry
!= NULL
) {
94 if (pfEntry
->requestIssued
[index
]) {
95 if (pfEntry
->requestCompleted
[index
]) {
96 // We prefetched too early and now the prefetch block no
97 // longer exists in the cache
98 rubyPrefetcherStats
.numMissedPrefetchedBlocks
++;
101 // The controller has issued the prefetch request,
102 // but the request for the block arrived earlier.
103 rubyPrefetcherStats
.numPartialHits
++;
104 observePfMiss(line_addr
);
108 // The request is still in the prefetch queue of the controller.
109 // Or was evicted because of other requests.
114 // Check if address is in any of the stride filters
115 if (accessUnitFilter(&unitFilter
, line_addr
, 1, type
)) {
116 DPRINTF(RubyPrefetcher
, " *** hit in unit stride buffer\n");
119 if (accessUnitFilter(&negativeFilter
, line_addr
, -1, type
)) {
120 DPRINTF(RubyPrefetcher
, " *** hit in unit negative unit buffer\n");
123 if (accessNonunitFilter(line_addr
, type
)) {
124 DPRINTF(RubyPrefetcher
, " *** hit in non-unit stride buffer\n");
130 RubyPrefetcher::observePfMiss(Addr address
)
132 rubyPrefetcherStats
.numPartialHits
++;
133 DPRINTF(RubyPrefetcher
, "Observed partial hit for %#x\n", address
);
134 issueNextPrefetch(address
, NULL
);
138 RubyPrefetcher::observePfHit(Addr address
)
140 rubyPrefetcherStats
.numHits
++;
141 DPRINTF(RubyPrefetcher
, "Observed hit for %#x\n", address
);
142 issueNextPrefetch(address
, NULL
);
146 RubyPrefetcher::issueNextPrefetch(Addr address
, PrefetchEntry
*stream
)
148 // get our corresponding stream fetcher
149 if (stream
== NULL
) {
151 stream
= getPrefetchEntry(address
, index
);
154 // if (for some reason), this stream is unallocated, return.
155 if (stream
== NULL
) {
156 DPRINTF(RubyPrefetcher
, "Unallocated stream, returning\n");
160 // extend this prefetching stream by 1 (or more)
161 Addr page_addr
= pageAddress(stream
->m_address
);
162 Addr line_addr
= makeNextStrideAddress(stream
->m_address
,
165 // possibly stop prefetching at page boundaries
166 if (page_addr
!= pageAddress(line_addr
)) {
167 if (!m_prefetch_cross_pages
) {
168 // Deallocate the stream since we are not prefetching
169 // across page boundries
170 stream
->m_is_valid
= false;
173 rubyPrefetcherStats
.numPagesCrossed
++;
176 // launch next prefetch
177 rubyPrefetcherStats
.numPrefetchRequested
++;
178 stream
->m_address
= line_addr
;
179 stream
->m_use_time
= m_controller
->curCycle();
180 DPRINTF(RubyPrefetcher
, "Requesting prefetch for %#x\n", line_addr
);
181 m_controller
->enqueuePrefetch(line_addr
, stream
->m_type
);
185 RubyPrefetcher::getLRUindex(void)
187 uint32_t lru_index
= 0;
188 Cycles lru_access
= m_array
[lru_index
].m_use_time
;
190 for (uint32_t i
= 0; i
< m_num_streams
; i
++) {
191 if (!m_array
[i
].m_is_valid
) {
194 if (m_array
[i
].m_use_time
< lru_access
) {
195 lru_access
= m_array
[i
].m_use_time
;
204 RubyPrefetcher::initializeStream(Addr address
, int stride
,
205 uint32_t index
, const RubyRequestType
& type
)
207 rubyPrefetcherStats
.numAllocatedStreams
++;
209 // initialize the stream prefetcher
210 PrefetchEntry
*mystream
= &(m_array
[index
]);
211 mystream
->m_address
= makeLineAddress(address
);
212 mystream
->m_stride
= stride
;
213 mystream
->m_use_time
= m_controller
->curCycle();
214 mystream
->m_is_valid
= true;
215 mystream
->m_type
= type
;
217 // create a number of initial prefetches for this stream
218 Addr page_addr
= pageAddress(mystream
->m_address
);
219 Addr line_addr
= makeLineAddress(mystream
->m_address
);
221 // insert a number of prefetches into the prefetch table
222 for (int k
= 0; k
< m_num_startup_pfs
; k
++) {
223 line_addr
= makeNextStrideAddress(line_addr
, stride
);
224 // possibly stop prefetching at page boundaries
225 if (page_addr
!= pageAddress(line_addr
)) {
226 if (!m_prefetch_cross_pages
) {
227 // deallocate this stream prefetcher
228 mystream
->m_is_valid
= false;
231 rubyPrefetcherStats
.numPagesCrossed
++;
235 rubyPrefetcherStats
.numPrefetchRequested
++;
236 DPRINTF(RubyPrefetcher
, "Requesting prefetch for %#x\n", line_addr
);
237 m_controller
->enqueuePrefetch(line_addr
, m_array
[index
].m_type
);
240 // update the address to be the last address prefetched
241 mystream
->m_address
= line_addr
;
245 RubyPrefetcher::getPrefetchEntry(Addr address
, uint32_t &index
)
247 // search all streams for a match
248 for (int i
= 0; i
< m_num_streams
; i
++) {
249 // search all the outstanding prefetches for this stream
250 if (m_array
[i
].m_is_valid
) {
251 for (int j
= 0; j
< m_num_startup_pfs
; j
++) {
252 if (makeNextStrideAddress(m_array
[i
].m_address
,
253 -(m_array
[i
].m_stride
*j
)) == address
) {
254 return &(m_array
[i
]);
263 RubyPrefetcher::accessUnitFilter(CircularQueue
<UnitFilterEntry
>* const filter
,
264 Addr line_addr
, int stride
, const RubyRequestType
& type
)
266 for (auto& entry
: *filter
) {
267 if (entry
.addr
== line_addr
) {
268 entry
.addr
= makeNextStrideAddress(entry
.addr
, stride
);
270 if (entry
.hits
>= m_train_misses
) {
271 // Allocate a new prefetch stream
272 initializeStream(line_addr
, stride
, getLRUindex(), type
);
278 // Enter this address in the filter
279 filter
->push_back(UnitFilterEntry(
280 makeNextStrideAddress(line_addr
, stride
)));
286 RubyPrefetcher::accessNonunitFilter(Addr line_addr
,
287 const RubyRequestType
& type
)
289 /// look for non-unit strides based on a (user-defined) page size
290 Addr page_addr
= pageAddress(line_addr
);
292 for (auto& entry
: nonUnitFilter
) {
293 if (pageAddress(entry
.addr
) == page_addr
) {
294 // hit in the non-unit filter
295 // compute the actual stride (for this reference)
296 int delta
= line_addr
- entry
.addr
;
299 // no zero stride prefetches
300 // check that the stride matches (for the last N times)
301 if (delta
== entry
.stride
) {
303 // increment count (if > 2) allocate stream
305 if (entry
.hits
> m_train_misses
) {
306 // This stride HAS to be the multiplicative constant of
307 // dataBlockBytes (bc makeNextStrideAddress is
308 // calculated based on this multiplicative constant!)
309 const int stride
= entry
.stride
/
310 RubySystem::getBlockSizeBytes();
312 // clear this filter entry
315 initializeStream(line_addr
, stride
, getLRUindex(),
319 // If delta didn't match reset entry's hit count
323 // update the last address seen & the stride
324 entry
.addr
= line_addr
;
325 entry
.stride
= delta
;
333 // not found: enter this address in the table
334 nonUnitFilter
.push_back(NonUnitFilterEntry(line_addr
));
340 RubyPrefetcher::print(std::ostream
& out
) const
342 out
<< name() << " Prefetcher State\n";
343 // print out unit filter
344 out
<< "unit table:\n";
345 for (const auto& entry
: unitFilter
) {
346 out
<< entry
.addr
<< std::endl
;
349 out
<< "negative table:\n";
350 for (const auto& entry
: negativeFilter
) {
351 out
<< entry
.addr
<< std::endl
;
354 // print out non-unit stride filter
355 out
<< "non-unit table:\n";
356 for (const auto& entry
: nonUnitFilter
) {
357 out
<< entry
.addr
<< " "
358 << entry
.stride
<< " "
359 << entry
.hits
<< std::endl
;
362 // print out allocated stream buffers
364 for (int i
= 0; i
< m_num_streams
; i
++) {
365 out
<< m_array
[i
].m_address
<< " "
366 << m_array
[i
].m_stride
<< " "
367 << m_array
[i
].m_is_valid
<< " "
368 << m_array
[i
].m_use_time
<< std::endl
;
373 RubyPrefetcher::pageAddress(Addr addr
) const
375 return mbits
<Addr
>(addr
, 63, m_page_shift
);