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"
52 RubyPrefetcherParams::create()
54 return new RubyPrefetcher(this);
57 RubyPrefetcher::RubyPrefetcher(const Params
*p
)
58 : SimObject(p
), m_num_streams(p
->num_streams
),
59 m_array(p
->num_streams
), m_train_misses(p
->train_misses
),
60 m_num_startup_pfs(p
->num_startup_pfs
),
61 unitFilter(p
->unit_filter
),
62 negativeFilter(p
->unit_filter
),
63 nonUnitFilter(p
->nonunit_filter
),
64 m_prefetch_cross_pages(p
->cross_page
),
65 m_page_shift(p
->sys
->getPageShift())
67 assert(m_num_streams
> 0);
68 assert(m_num_startup_pfs
<= MAX_PF_INFLIGHT
);
72 RubyPrefetcher::regStats()
74 SimObject::regStats();
77 .name(name() + ".miss_observed")
78 .desc("number of misses observed")
82 .name(name() + ".allocated_streams")
83 .desc("number of streams allocated for prefetching")
87 .name(name() + ".prefetches_requested")
88 .desc("number of prefetch requests made")
92 .name(name() + ".hits")
93 .desc("number of prefetched blocks accessed (for the first time)")
97 .name(name() + ".partial_hits")
98 .desc("number of misses observed for a block being prefetched")
102 .name(name() + ".pages_crossed")
103 .desc("number of prefetches across pages")
106 numMissedPrefetchedBlocks
107 .name(name() + ".misses_on_prefetched_blocks")
108 .desc("number of misses for blocks that were prefetched, yet missed")
113 RubyPrefetcher::observeMiss(Addr address
, const RubyRequestType
& type
)
115 DPRINTF(RubyPrefetcher
, "Observed miss for %#x\n", address
);
116 Addr line_addr
= makeLineAddress(address
);
119 // check to see if we have already issued a prefetch for this block
121 PrefetchEntry
*pfEntry
= getPrefetchEntry(line_addr
, index
);
122 if (pfEntry
!= NULL
) {
123 if (pfEntry
->requestIssued
[index
]) {
124 if (pfEntry
->requestCompleted
[index
]) {
125 // We prefetched too early and now the prefetch block no
126 // longer exists in the cache
127 numMissedPrefetchedBlocks
++;
130 // The controller has issued the prefetch request,
131 // but the request for the block arrived earlier.
133 observePfMiss(line_addr
);
137 // The request is still in the prefetch queue of the controller.
138 // Or was evicted because of other requests.
143 // Check if address is in any of the stride filters
144 if (accessUnitFilter(&unitFilter
, line_addr
, 1, type
)) {
145 DPRINTF(RubyPrefetcher
, " *** hit in unit stride buffer\n");
148 if (accessUnitFilter(&negativeFilter
, line_addr
, -1, type
)) {
149 DPRINTF(RubyPrefetcher
, " *** hit in unit negative unit buffer\n");
152 if (accessNonunitFilter(line_addr
, type
)) {
153 DPRINTF(RubyPrefetcher
, " *** hit in non-unit stride buffer\n");
159 RubyPrefetcher::observePfMiss(Addr address
)
162 DPRINTF(RubyPrefetcher
, "Observed partial hit for %#x\n", address
);
163 issueNextPrefetch(address
, NULL
);
167 RubyPrefetcher::observePfHit(Addr address
)
170 DPRINTF(RubyPrefetcher
, "Observed hit for %#x\n", address
);
171 issueNextPrefetch(address
, NULL
);
175 RubyPrefetcher::issueNextPrefetch(Addr address
, PrefetchEntry
*stream
)
177 // get our corresponding stream fetcher
178 if (stream
== NULL
) {
180 stream
= getPrefetchEntry(address
, index
);
183 // if (for some reason), this stream is unallocated, return.
184 if (stream
== NULL
) {
185 DPRINTF(RubyPrefetcher
, "Unallocated stream, returning\n");
189 // extend this prefetching stream by 1 (or more)
190 Addr page_addr
= pageAddress(stream
->m_address
);
191 Addr line_addr
= makeNextStrideAddress(stream
->m_address
,
194 // possibly stop prefetching at page boundaries
195 if (page_addr
!= pageAddress(line_addr
)) {
196 if (!m_prefetch_cross_pages
) {
197 // Deallocate the stream since we are not prefetching
198 // across page boundries
199 stream
->m_is_valid
= false;
205 // launch next prefetch
206 numPrefetchRequested
++;
207 stream
->m_address
= line_addr
;
208 stream
->m_use_time
= m_controller
->curCycle();
209 DPRINTF(RubyPrefetcher
, "Requesting prefetch for %#x\n", line_addr
);
210 m_controller
->enqueuePrefetch(line_addr
, stream
->m_type
);
214 RubyPrefetcher::getLRUindex(void)
216 uint32_t lru_index
= 0;
217 Cycles lru_access
= m_array
[lru_index
].m_use_time
;
219 for (uint32_t i
= 0; i
< m_num_streams
; i
++) {
220 if (!m_array
[i
].m_is_valid
) {
223 if (m_array
[i
].m_use_time
< lru_access
) {
224 lru_access
= m_array
[i
].m_use_time
;
233 RubyPrefetcher::initializeStream(Addr address
, int stride
,
234 uint32_t index
, const RubyRequestType
& type
)
236 numAllocatedStreams
++;
238 // initialize the stream prefetcher
239 PrefetchEntry
*mystream
= &(m_array
[index
]);
240 mystream
->m_address
= makeLineAddress(address
);
241 mystream
->m_stride
= stride
;
242 mystream
->m_use_time
= m_controller
->curCycle();
243 mystream
->m_is_valid
= true;
244 mystream
->m_type
= type
;
246 // create a number of initial prefetches for this stream
247 Addr page_addr
= pageAddress(mystream
->m_address
);
248 Addr line_addr
= makeLineAddress(mystream
->m_address
);
250 // insert a number of prefetches into the prefetch table
251 for (int k
= 0; k
< m_num_startup_pfs
; k
++) {
252 line_addr
= makeNextStrideAddress(line_addr
, stride
);
253 // possibly stop prefetching at page boundaries
254 if (page_addr
!= pageAddress(line_addr
)) {
255 if (!m_prefetch_cross_pages
) {
256 // deallocate this stream prefetcher
257 mystream
->m_is_valid
= false;
264 numPrefetchRequested
++;
265 DPRINTF(RubyPrefetcher
, "Requesting prefetch for %#x\n", line_addr
);
266 m_controller
->enqueuePrefetch(line_addr
, m_array
[index
].m_type
);
269 // update the address to be the last address prefetched
270 mystream
->m_address
= line_addr
;
274 RubyPrefetcher::getPrefetchEntry(Addr address
, uint32_t &index
)
276 // search all streams for a match
277 for (int i
= 0; i
< m_num_streams
; i
++) {
278 // search all the outstanding prefetches for this stream
279 if (m_array
[i
].m_is_valid
) {
280 for (int j
= 0; j
< m_num_startup_pfs
; j
++) {
281 if (makeNextStrideAddress(m_array
[i
].m_address
,
282 -(m_array
[i
].m_stride
*j
)) == address
) {
283 return &(m_array
[i
]);
292 RubyPrefetcher::accessUnitFilter(CircularQueue
<UnitFilterEntry
>* const filter
,
293 Addr line_addr
, int stride
, const RubyRequestType
& type
)
295 for (auto& entry
: *filter
) {
296 if (entry
.addr
== line_addr
) {
297 entry
.addr
= makeNextStrideAddress(entry
.addr
, stride
);
299 if (entry
.hits
>= m_train_misses
) {
300 // Allocate a new prefetch stream
301 initializeStream(line_addr
, stride
, getLRUindex(), type
);
307 // Enter this address in the filter
308 filter
->push_back(UnitFilterEntry(
309 makeNextStrideAddress(line_addr
, stride
)));
315 RubyPrefetcher::accessNonunitFilter(Addr line_addr
,
316 const RubyRequestType
& type
)
318 /// look for non-unit strides based on a (user-defined) page size
319 Addr page_addr
= pageAddress(line_addr
);
321 for (auto& entry
: nonUnitFilter
) {
322 if (pageAddress(entry
.addr
) == page_addr
) {
323 // hit in the non-unit filter
324 // compute the actual stride (for this reference)
325 int delta
= line_addr
- entry
.addr
;
328 // no zero stride prefetches
329 // check that the stride matches (for the last N times)
330 if (delta
== entry
.stride
) {
332 // increment count (if > 2) allocate stream
334 if (entry
.hits
> m_train_misses
) {
335 // This stride HAS to be the multiplicative constant of
336 // dataBlockBytes (bc makeNextStrideAddress is
337 // calculated based on this multiplicative constant!)
338 const int stride
= entry
.stride
/
339 RubySystem::getBlockSizeBytes();
341 // clear this filter entry
344 initializeStream(line_addr
, stride
, getLRUindex(),
348 // If delta didn't match reset entry's hit count
352 // update the last address seen & the stride
353 entry
.addr
= line_addr
;
354 entry
.stride
= delta
;
362 // not found: enter this address in the table
363 nonUnitFilter
.push_back(NonUnitFilterEntry(line_addr
));
369 RubyPrefetcher::print(std::ostream
& out
) const
371 out
<< name() << " Prefetcher State\n";
372 // print out unit filter
373 out
<< "unit table:\n";
374 for (const auto& entry
: unitFilter
) {
375 out
<< entry
.addr
<< std::endl
;
378 out
<< "negative table:\n";
379 for (const auto& entry
: negativeFilter
) {
380 out
<< entry
.addr
<< std::endl
;
383 // print out non-unit stride filter
384 out
<< "non-unit table:\n";
385 for (const auto& entry
: nonUnitFilter
) {
386 out
<< entry
.addr
<< " "
387 << entry
.stride
<< " "
388 << entry
.hits
<< std::endl
;
391 // print out allocated stream buffers
393 for (int i
= 0; i
< m_num_streams
; i
++) {
394 out
<< m_array
[i
].m_address
<< " "
395 << m_array
[i
].m_stride
<< " "
396 << m_array
[i
].m_is_valid
<< " "
397 << m_array
[i
].m_use_time
<< std::endl
;
402 RubyPrefetcher::pageAddress(Addr addr
) const
404 return mbits
<Addr
>(addr
, 63, m_page_shift
);