smmu_v3_caches.cc revision 14221:2954f631ee64
112855Sgabeblack@google.com/*
212855Sgabeblack@google.com * Copyright (c) 2014, 2018-2019 ARM Limited
312855Sgabeblack@google.com * All rights reserved
412855Sgabeblack@google.com *
512855Sgabeblack@google.com * The license below extends only to copyright in the software and shall
612855Sgabeblack@google.com * not be construed as granting a license to any other intellectual
712855Sgabeblack@google.com * property including but not limited to intellectual property relating
812855Sgabeblack@google.com * to a hardware implementation of the functionality of the software
912855Sgabeblack@google.com * licensed hereunder.  You may use the software subject to the license
1012855Sgabeblack@google.com * terms below provided that you ensure that this notice is replicated
1112855Sgabeblack@google.com * unmodified and in its entirety in all distributions of the software,
1212855Sgabeblack@google.com * modified or unmodified, in source code or in binary form.
1312855Sgabeblack@google.com *
1412855Sgabeblack@google.com * Redistribution and use in source and binary forms, with or without
1512855Sgabeblack@google.com * modification, are permitted provided that the following conditions are
1612855Sgabeblack@google.com * met: redistributions of source code must retain the above copyright
1712855Sgabeblack@google.com * notice, this list of conditions and the following disclaimer;
1812855Sgabeblack@google.com * redistributions in binary form must reproduce the above copyright
1912855Sgabeblack@google.com * notice, this list of conditions and the following disclaimer in the
2012855Sgabeblack@google.com * documentation and/or other materials provided with the distribution;
2112855Sgabeblack@google.com * neither the name of the copyright holders nor the names of its
2212855Sgabeblack@google.com * contributors may be used to endorse or promote products derived from
2312855Sgabeblack@google.com * this software without specific prior written permission.
2412855Sgabeblack@google.com *
2512855Sgabeblack@google.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2612855Sgabeblack@google.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2712855Sgabeblack@google.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2812855Sgabeblack@google.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2912855Sgabeblack@google.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3012855Sgabeblack@google.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3112855Sgabeblack@google.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3212855Sgabeblack@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3312855Sgabeblack@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3412855Sgabeblack@google.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3512855Sgabeblack@google.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3612855Sgabeblack@google.com *
3712855Sgabeblack@google.com * Authors: Stan Czerniawski
3812855Sgabeblack@google.com *          Damian Richardson
3912855Sgabeblack@google.com */
4012855Sgabeblack@google.com
4112855Sgabeblack@google.com#include "dev/arm/smmu_v3_caches.hh"
4212855Sgabeblack@google.com
4312855Sgabeblack@google.com#include <numeric>
4412855Sgabeblack@google.com
4512855Sgabeblack@google.com#include "base/bitfield.hh"
4612855Sgabeblack@google.com#include "base/intmath.hh"
4712855Sgabeblack@google.com#include "base/logging.hh"
4812855Sgabeblack@google.com#include "sim/stats.hh"
4912855Sgabeblack@google.com
5012855Sgabeblack@google.com
5112855Sgabeblack@google.com// taken from hex expansion of pi
5212855Sgabeblack@google.com#define SMMUTLB_SEED     0xEA752DFE
5312855Sgabeblack@google.com#define ARMARCHTLB_SEED  0x8B021FA1
5412855Sgabeblack@google.com#define IPACACHE_SEED    0xE5A0CC0F
5512855Sgabeblack@google.com#define CONFIGCACHE_SEED 0xB56F74E8
5612855Sgabeblack@google.com#define WALKCACHE_SEED   0x18ACF3D6
5712855Sgabeblack@google.com
5812855Sgabeblack@google.com/*
5912855Sgabeblack@google.com * BaseCache
6012855Sgabeblack@google.com *
6112855Sgabeblack@google.com * TODO: move more code into this base class to reduce duplication.
6212855Sgabeblack@google.com */
6312855Sgabeblack@google.com
6412855Sgabeblack@google.comSMMUv3BaseCache::SMMUv3BaseCache(const std::string &policy_name, uint32_t seed) :
6512855Sgabeblack@google.com    replacementPolicy(decodePolicyName(policy_name)),
6612855Sgabeblack@google.com    nextToReplace(0),
6712855Sgabeblack@google.com    random(seed),
6812855Sgabeblack@google.com    useStamp(0)
6912855Sgabeblack@google.com{}
7012855Sgabeblack@google.com
7112855Sgabeblack@google.comint
7212855Sgabeblack@google.comSMMUv3BaseCache::decodePolicyName(const std::string &policy_name)
7312855Sgabeblack@google.com{
7412855Sgabeblack@google.com    if (policy_name == "rr") {
7512855Sgabeblack@google.com        return SMMU_CACHE_REPL_ROUND_ROBIN;
7612855Sgabeblack@google.com    } else if (policy_name == "rand") {
7712855Sgabeblack@google.com        return SMMU_CACHE_REPL_RANDOM;
7812855Sgabeblack@google.com    } else if (policy_name == "lru") {
7912855Sgabeblack@google.com        return SMMU_CACHE_REPL_LRU;
8012855Sgabeblack@google.com    } else {
8112855Sgabeblack@google.com        panic("Unknown cache replacement policy '%s'\n", policy_name);
8212855Sgabeblack@google.com    }
8312855Sgabeblack@google.com}
8412855Sgabeblack@google.com
8512855Sgabeblack@google.comvoid
8612855Sgabeblack@google.comSMMUv3BaseCache::regStats(const std::string &name)
8712855Sgabeblack@google.com{
8812855Sgabeblack@google.com    using namespace Stats;
8912855Sgabeblack@google.com
9012855Sgabeblack@google.com
9112855Sgabeblack@google.com    averageLookups
9212855Sgabeblack@google.com        .name(name + ".averageLookups")
9312855Sgabeblack@google.com        .desc("Average number lookups per second")
9412855Sgabeblack@google.com        .flags(pdf);
9512855Sgabeblack@google.com
9612855Sgabeblack@google.com    totalLookups
97        .name(name + ".totalLookups")
98        .desc("Total number of lookups")
99        .flags(pdf);
100
101    averageLookups = totalLookups / simSeconds;
102
103
104    averageMisses
105        .name(name + ".averageMisses")
106        .desc("Average number misses per second")
107        .flags(pdf);
108
109    totalMisses
110        .name(name + ".totalMisses")
111        .desc("Total number of misses")
112        .flags(pdf);
113
114    averageMisses = totalMisses / simSeconds;
115
116
117    averageUpdates
118        .name(name + ".averageUpdates")
119        .desc("Average number updates per second")
120        .flags(pdf);
121
122    totalUpdates
123        .name(name + ".totalUpdates")
124        .desc("Total number of updates")
125        .flags(pdf);
126
127    averageUpdates = totalUpdates / simSeconds;
128
129
130    averageHitRate
131        .name(name + ".averageHitRate")
132        .desc("Average hit rate")
133        .flags(pdf);
134
135    averageHitRate = (totalLookups - totalMisses) / totalLookups;
136
137    insertions
138        .name(name + ".insertions")
139        .desc("Number of insertions (not replacements)")
140        .flags(pdf);
141}
142
143
144
145/*
146 * SMMUTLB
147 */
148
149SMMUTLB::SMMUTLB(unsigned numEntries, unsigned _associativity,
150                 const std::string &policy)
151:
152    SMMUv3BaseCache(policy, SMMUTLB_SEED),
153    associativity(_associativity)
154{
155    if (associativity == 0)
156        associativity = numEntries; // fully associative
157
158    if (numEntries == 0)
159        fatal("SMMUTLB must have at least one entry\n");
160
161    if (associativity > numEntries)
162        fatal("SMMUTLB associativity cannot be higher than "
163              "its number of entries\n");
164
165    unsigned num_sets = numEntries / associativity;
166
167    if (num_sets*associativity != numEntries)
168        fatal("Number of SMMUTLB entries must be divisible "
169              "by its associativity\n");
170
171    Entry e;
172    e.valid = false;
173
174    Set set(associativity, e);
175    sets.resize(num_sets, set);
176}
177
178const SMMUTLB::Entry*
179SMMUTLB::lookup(uint32_t sid, uint32_t ssid,
180                Addr va, bool updStats)
181{
182    const Entry *result = NULL;
183
184    Set &set = sets[pickSetIdx(va)];
185
186    for (size_t i = 0; i < set.size(); i++) {
187        const Entry &e = set[i];
188
189        if (e.valid && (e.va & e.vaMask) == (va & e.vaMask) &&
190            e.sid==sid && e.ssid==ssid)
191        {
192            if (result != NULL)
193                panic("SMMUTLB: duplicate entry found!\n");
194
195            result = &e;
196            break;
197        }
198    }
199
200    if (updStats) {
201        if (result)
202            result->lastUsed = useStamp++;
203
204        totalLookups++;
205        if (result == NULL)
206            totalMisses++;
207    }
208
209    return result;
210}
211
212const SMMUTLB::Entry*
213SMMUTLB::lookupAnyVA(uint32_t sid, uint32_t ssid, bool updStats)
214{
215    const Entry *result = NULL;
216
217    for (size_t s = 0; s < sets.size(); s++) {
218        Set &set = sets[s];
219
220        for (size_t i = 0; i < set.size(); i++) {
221            const Entry &e = set[i];
222
223            if (e.valid && e.sid==sid && e.ssid==ssid) {
224                result = &e;
225                break;
226            }
227        }
228    }
229
230    if (updStats) {
231        totalLookups++;
232        if (result == NULL)
233            totalMisses++;
234    }
235
236    return result;
237}
238
239void
240SMMUTLB::store(const Entry &incoming, AllocPolicy alloc)
241{
242    if (!incoming.valid)
243        panic("Tried to store an invalid entry\n");
244
245    incoming.lastUsed = 0;
246
247    const Entry *existing =
248        lookup(incoming.sid, incoming.ssid, incoming.va, false);
249
250    if (existing) {
251        *const_cast<Entry *> (existing) = incoming;
252    } else {
253        Set &set = sets[pickSetIdx(incoming.va)];
254        set[pickEntryIdxToReplace(set, alloc)] = incoming;
255    }
256
257    totalUpdates++;
258}
259
260void
261SMMUTLB::invalidateSSID(uint32_t sid, uint32_t ssid)
262{
263    Set &set = sets[pickSetIdx(sid, ssid)];
264
265    for (size_t i = 0; i < set.size(); i++) {
266        Entry &e = set[i];
267
268        if (e.sid == sid && e.ssid == ssid)
269            e.valid = false;
270    }
271}
272
273void
274SMMUTLB::invalidateSID(uint32_t sid)
275{
276    for (size_t s = 0; s < sets.size(); s++) {
277        Set &set = sets[s];
278
279        for (size_t i = 0; i < set.size(); i++) {
280            Entry &e = set[i];
281
282            if (e.sid == sid)
283                e.valid = false;
284        }
285    }
286}
287
288void
289SMMUTLB::invalidateVA(Addr va, uint16_t asid, uint16_t vmid)
290{
291    Set &set = sets[pickSetIdx(va)];
292
293    for (size_t i = 0; i < set.size(); i++) {
294        Entry &e = set[i];
295
296        if ((e.va & e.vaMask) == (va & e.vaMask) &&
297            e.asid==asid && e.vmid==vmid)
298        {
299            e.valid = false;
300        }
301    }
302}
303
304void
305SMMUTLB::invalidateVAA(Addr va, uint16_t vmid)
306{
307    Set &set = sets[pickSetIdx(va)];
308
309    for (size_t i = 0; i < set.size(); i++) {
310        Entry &e = set[i];
311
312        if ((e.va & e.vaMask) == (va & e.vaMask) && e.vmid==vmid)
313            e.valid = false;
314    }
315}
316
317void
318SMMUTLB::invalidateASID(uint16_t asid, uint16_t vmid)
319{
320    for (size_t s = 0; s < sets.size(); s++) {
321        Set &set = sets[s];
322
323        for (size_t i = 0; i < set.size(); i++) {
324            Entry &e = set[i];
325
326            if (e.asid==asid && e.vmid==vmid)
327                e.valid = false;
328        }
329    }
330}
331
332void
333SMMUTLB::invalidateVMID(uint16_t vmid)
334{
335    for (size_t s = 0; s < sets.size(); s++) {
336        Set &set = sets[s];
337
338        for (size_t i = 0; i < set.size(); i++) {
339            Entry &e = set[i];
340
341            if (e.vmid == vmid)
342                e.valid = false;
343        }
344    }
345}
346
347void
348SMMUTLB::invalidateAll()
349{
350    for (size_t s = 0; s < sets.size(); s++) {
351        Set &set = sets[s];
352
353        for (size_t i = 0; i < set.size(); i++)
354            set[i].valid = false;
355    }
356}
357
358size_t
359SMMUTLB::pickSetIdx(Addr va) const
360{
361    return (va >> 12) % sets.size();
362}
363
364size_t
365SMMUTLB::pickSetIdx(uint32_t sid, uint32_t ssid) const
366{
367    return (sid^ssid) % sets.size();
368}
369
370size_t
371SMMUTLB::pickEntryIdxToReplace(const Set &set, AllocPolicy alloc)
372{
373    if (alloc == ALLOC_LAST_WAY)
374        return associativity - 1;
375
376    uint32_t lru_tick = UINT32_MAX;
377    size_t lru_idx = 0;
378    size_t max_idx =
379        alloc==ALLOC_ANY_BUT_LAST_WAY ?
380            set.size()-1 : set.size();
381
382    for (size_t i = 0; i < max_idx; i++) {
383        if (!set[i].valid) {
384            insertions++;
385            return i;
386        }
387
388        if (set[i].lastUsed < lru_tick) {
389            lru_idx = i;
390            lru_tick = set[i].lastUsed;
391        }
392    }
393
394    switch (replacementPolicy) {
395    case SMMU_CACHE_REPL_ROUND_ROBIN:
396        switch (alloc) {
397        case ALLOC_ANY_WAY:
398            return nextToReplace = ((nextToReplace+1) % associativity);
399        case ALLOC_ANY_BUT_LAST_WAY:
400            return nextToReplace = ((nextToReplace+1) % (associativity-1));
401        default:
402            panic("Unknown allocation mode %d\n", alloc);
403        }
404
405    case SMMU_CACHE_REPL_RANDOM:
406        switch (alloc) {
407        case ALLOC_ANY_WAY:
408            return random.random<size_t>(0, associativity-1);
409        case ALLOC_ANY_BUT_LAST_WAY:
410            return random.random<size_t>(0, associativity-2);
411        default:
412            panic("Unknown allocation mode %d\n", alloc);
413        }
414
415    case SMMU_CACHE_REPL_LRU:
416        return lru_idx;
417
418    default:
419        panic("Unknown replacement policy %d\n", replacementPolicy);
420    }
421}
422
423
424
425/*
426 * ARMArchTLB
427 */
428
429ARMArchTLB::ARMArchTLB(unsigned numEntries, unsigned _associativity,
430                       const std::string &policy)
431:
432    SMMUv3BaseCache(policy, ARMARCHTLB_SEED),
433    associativity(_associativity)
434{
435    if (associativity == 0)
436        associativity = numEntries; // fully associative
437
438    if (numEntries == 0)
439        fatal("ARMArchTLB must have at least one entry\n");
440
441    if (associativity > numEntries)
442        fatal("ARMArchTLB associativity cannot be higher than "
443              "its number of entries\n");
444
445    unsigned num_sets = numEntries / associativity;
446
447    if (num_sets*associativity != numEntries)
448        fatal("Number of ARMArchTLB entries must be divisible "
449              "by its associativity\n");
450
451    Entry e;
452    e.valid = false;
453
454    Set set(associativity, e);
455    sets.resize(num_sets, set);
456}
457
458const ARMArchTLB::Entry *
459ARMArchTLB::lookup(Addr va, uint16_t asid, uint16_t vmid, bool updStats)
460{
461    const Entry *result = NULL;
462
463    Set &set = sets[pickSetIdx(va, asid, vmid)];
464
465    for (size_t i = 0; i < set.size(); i++) {
466        const Entry &e = set[i];
467
468        if (e.valid && (e.va & e.vaMask) == (va & e.vaMask) &&
469            e.asid==asid && e.vmid==vmid)
470        {
471            if (result != NULL)
472                panic("ARMArchTLB: duplicate entry found!\n");
473
474            result = &e;
475            break;
476        }
477    }
478
479    if (updStats) {
480        if (result)
481            result->lastUsed = useStamp++;
482
483        totalLookups++;
484        if (result == NULL)
485            totalMisses++;
486    }
487
488    return result;
489}
490
491void
492ARMArchTLB::store(const Entry &incoming)
493{
494    if (!incoming.valid)
495        panic("Tried to store an invalid entry\n");
496
497    incoming.lastUsed = 0;
498
499    const Entry *existing =
500        lookup(incoming.va, incoming.asid, incoming.vmid, false);
501
502    if (existing) {
503        *const_cast<Entry *> (existing) = incoming;
504    } else {
505        Set &set = sets[pickSetIdx(incoming.va, incoming.asid, incoming.vmid)];
506        set[pickEntryIdxToReplace(set)] = incoming;
507    }
508
509    totalUpdates++;
510}
511
512void
513ARMArchTLB::invalidateVA(Addr va, uint16_t asid, uint16_t vmid)
514{
515    Set &set = sets[pickSetIdx(va, asid, vmid)];
516
517    for (size_t i = 0; i < set.size(); i++) {
518        Entry &e = set[i];
519
520        if ((e.va & e.vaMask) == (va & e.vaMask) &&
521            e.asid==asid && e.vmid==vmid)
522        {
523            e.valid = false;
524        }
525    }
526}
527
528void
529ARMArchTLB::invalidateVAA(Addr va, uint16_t vmid)
530{
531    for (size_t s = 0; s < sets.size(); s++) {
532        Set &set = sets[s];
533
534        for (size_t i = 0; i < set.size(); i++) {
535            Entry &e = set[i];
536
537            if ((e.va & e.vaMask) == (va & e.vaMask) && e.vmid==vmid)
538                e.valid = false;
539        }
540    }
541}
542
543void
544ARMArchTLB::invalidateASID(uint16_t asid, uint16_t vmid)
545{
546    for (size_t s = 0; s < sets.size(); s++) {
547        Set &set = sets[s];
548
549        for (size_t i = 0; i < set.size(); i++) {
550            Entry &e = set[i];
551
552            if (e.asid==asid && e.vmid==vmid)
553                e.valid = false;
554        }
555    }
556}
557
558void
559ARMArchTLB::invalidateVMID(uint16_t vmid)
560{
561    for (size_t s = 0; s < sets.size(); s++) {
562        Set &set = sets[s];
563
564        for (size_t i = 0; i < set.size(); i++) {
565            Entry &e = set[i];
566
567            if (e.vmid == vmid)
568                e.valid = false;
569        }
570    }
571}
572
573void
574ARMArchTLB::invalidateAll()
575{
576    for (size_t s = 0; s < sets.size(); s++) {
577        Set &set = sets[s];
578
579        for (size_t i = 0; i < set.size(); i++)
580            set[i].valid = false;
581    }
582}
583
584size_t
585ARMArchTLB::pickSetIdx(Addr va, uint16_t asid, uint16_t vmid) const
586{
587    return ((va >> 12) ^ asid ^ vmid) % sets.size();
588}
589
590size_t
591ARMArchTLB::pickEntryIdxToReplace(const Set &set)
592{
593    size_t lru_idx = 0;
594    uint32_t lru_tick = UINT32_MAX;
595
596    for (size_t i = 0; i < set.size(); i++) {
597        if (!set[i].valid) {
598            insertions++;
599            return i;
600        }
601
602        if (set[i].lastUsed < lru_tick) {
603            lru_idx = i;
604            lru_tick = set[i].lastUsed;
605        }
606    }
607
608    switch (replacementPolicy) {
609    case SMMU_CACHE_REPL_ROUND_ROBIN:
610        return nextToReplace = ((nextToReplace+1) % associativity);
611
612    case SMMU_CACHE_REPL_RANDOM:
613        return random.random<size_t>(0, associativity-1);
614
615    case SMMU_CACHE_REPL_LRU:
616        return lru_idx;
617
618    default:
619        panic("Unknown replacement policy %d\n", replacementPolicy);
620    }
621
622}
623
624/*
625 * IPACache
626 */
627
628IPACache::IPACache(unsigned numEntries, unsigned _associativity,
629                   const std::string &policy)
630:
631    SMMUv3BaseCache(policy, IPACACHE_SEED),
632    associativity(_associativity)
633{
634    if (associativity == 0)
635        associativity = numEntries; // fully associative
636
637    if (numEntries == 0)
638        fatal("IPACache must have at least one entry\n");
639
640    if (associativity > numEntries)
641        fatal("IPACache associativity cannot be higher than "
642              "its number of entries\n");
643
644    unsigned num_sets = numEntries / associativity;
645
646    if (num_sets*associativity != numEntries)
647        fatal("Number of IPACache entries must be divisible "
648              "by its associativity\n");
649
650    Entry e;
651    e.valid = false;
652
653    Set set(associativity, e);
654    sets.resize(num_sets, set);
655}
656
657const IPACache::Entry*
658IPACache::lookup(Addr ipa, uint16_t vmid, bool updStats)
659{
660    const Entry *result = NULL;
661
662    Set &set = sets[pickSetIdx(ipa, vmid)];
663
664    for (size_t i = 0; i < set.size(); i++) {
665        const Entry &e = set[i];
666
667        if (e.valid && (e.ipa & e.ipaMask) == (ipa & e.ipaMask) &&
668            e.vmid==vmid)
669        {
670            if (result != NULL)
671                panic("IPACache: duplicate entry found!\n");
672
673            result = &e;
674            break;
675        }
676    }
677
678    if (updStats) {
679        if (result)
680            result->lastUsed = useStamp++;
681
682        totalLookups++;
683        if (result == NULL)
684            totalMisses++;
685    }
686
687    return result;
688}
689
690void
691IPACache::store(const Entry &incoming)
692{
693    if (!incoming.valid)
694        panic("Tried to store an invalid entry\n");
695
696    incoming.lastUsed = 0;
697
698    const Entry *existing = lookup(incoming.ipa, incoming.vmid, false);
699
700    if (existing) {
701        *const_cast<Entry *> (existing) = incoming;
702    } else {
703        Set &set = sets[pickSetIdx(incoming.ipa, incoming.vmid)];
704        set[pickEntryIdxToReplace(set)] = incoming;
705    }
706
707    totalUpdates++;
708}
709
710void
711IPACache::invalidateIPA(Addr ipa, uint16_t vmid)
712{
713    Set &set = sets[pickSetIdx(ipa, vmid)];
714
715    for (size_t i = 0; i < set.size(); i++) {
716        Entry &e = set[i];
717
718        if ((e.ipa & e.ipaMask) == (ipa & e.ipaMask) && e.vmid==vmid)
719            e.valid = false;
720    }
721}
722
723void
724IPACache::invalidateIPAA(Addr ipa)
725{
726    for (size_t s = 0; s < sets.size(); s++) {
727        Set &set = sets[s];
728
729        for (size_t i = 0; i < set.size(); i++) {
730            Entry &e = set[i];
731
732            if ((e.ipa & e.ipaMask) == (ipa & e.ipaMask))
733                e.valid = false;
734        }
735    }
736}
737
738void
739IPACache::invalidateVMID(uint16_t vmid)
740{
741    for (size_t s = 0; s < sets.size(); s++) {
742        Set &set = sets[s];
743
744        for (size_t i = 0; i < set.size(); i++) {
745            Entry &e = set[i];
746
747            if (e.vmid == vmid)
748                e.valid = false;
749        }
750    }
751}
752
753void
754IPACache::invalidateAll()
755{
756    for (size_t s = 0; s < sets.size(); s++) {
757        Set &set = sets[s];
758
759        for (size_t i = 0; i < set.size(); i++)
760            set[i].valid = false;
761    }
762}
763
764size_t
765IPACache::pickSetIdx(Addr va, uint16_t vmid) const
766{
767    return ((va >> 12) ^ vmid) % sets.size();
768}
769
770size_t
771IPACache::pickEntryIdxToReplace(const Set &set)
772{
773    size_t lru_idx = 0;
774    uint32_t lru_tick = UINT32_MAX;
775
776    for (size_t i = 0; i < set.size(); i++) {
777        if (!set[i].valid) {
778            insertions++;
779            return i;
780        }
781
782        if (set[i].lastUsed < lru_tick) {
783            lru_idx = i;
784            lru_tick = set[i].lastUsed;
785        }
786    }
787
788    switch (replacementPolicy) {
789    case SMMU_CACHE_REPL_ROUND_ROBIN:
790        return nextToReplace = ((nextToReplace+1) % associativity);
791
792    case SMMU_CACHE_REPL_RANDOM:
793        return random.random<size_t>(0, associativity-1);
794
795    case SMMU_CACHE_REPL_LRU:
796        return lru_idx;
797
798    default:
799        panic("Unknown replacement policy %d\n", replacementPolicy);
800    }
801
802}
803
804/*
805 * ConfigCache
806 */
807
808ConfigCache::ConfigCache(unsigned numEntries, unsigned _associativity,
809                         const std::string &policy)
810:
811    SMMUv3BaseCache(policy, CONFIGCACHE_SEED),
812    associativity(_associativity)
813{
814    if (associativity == 0)
815        associativity = numEntries; // fully associative
816
817    if (numEntries == 0)
818        fatal("ConfigCache must have at least one entry\n");
819
820    if (associativity > numEntries)
821        fatal("ConfigCache associativity cannot be higher than "
822              "its number of entries\n");
823
824    unsigned num_sets = numEntries / associativity;
825
826    if (num_sets*associativity != numEntries)
827        fatal("Number of ConfigCache entries must be divisible "
828              "by its associativity\n");
829
830    Entry e;
831    e.valid = false;
832
833    Set set(associativity, e);
834    sets.resize(num_sets, set);
835}
836
837const ConfigCache::Entry *
838ConfigCache::lookup(uint32_t sid, uint32_t ssid, bool updStats)
839{
840    const Entry *result = NULL;
841
842    Set &set = sets[pickSetIdx(sid, ssid)];
843
844    for (size_t i = 0; i < set.size(); i++) {
845        const Entry &e = set[i];
846
847        if (e.valid && e.sid==sid && e.ssid==ssid)
848        {
849            if (result != NULL)
850                panic("ConfigCache: duplicate entry found!\n");
851
852            result = &e;
853            break;
854        }
855    }
856
857    if (updStats) {
858        if (result)
859            result->lastUsed = useStamp++;
860
861        totalLookups++;
862        if (result == NULL)
863            totalMisses++;
864    }
865
866    return result;
867}
868
869void
870ConfigCache::store(const Entry &incoming)
871{
872    if (!incoming.valid)
873        panic("Tried to store an invalid entry\n");
874
875    incoming.lastUsed = 0;
876
877    const Entry *existing = lookup(incoming.sid, incoming.ssid, false);
878
879    if (existing) {
880        *const_cast<Entry *> (existing) = incoming;
881    } else {
882        Set &set = sets[pickSetIdx(incoming.sid, incoming.ssid)];
883        set[pickEntryIdxToReplace(set)] = incoming;
884    }
885
886    totalUpdates++;
887}
888
889void
890ConfigCache::invalidateSSID(uint32_t sid, uint32_t ssid)
891{
892    Set &set = sets[pickSetIdx(sid, ssid)];
893
894    for (size_t i = 0; i < set.size(); i++) {
895        Entry &e = set[i];
896
897        if (e.sid==sid && e.ssid==ssid)
898            e.valid = false;
899    }
900}
901
902void
903ConfigCache::invalidateSID(uint32_t sid)
904{
905    for (size_t s = 0; s < sets.size(); s++) {
906        Set &set = sets[s];
907
908        for (size_t i = 0; i < set.size(); i++) {
909            Entry &e = set[i];
910
911            if (e.sid == sid)
912                e.valid = false;
913        }
914    }
915}
916
917void
918ConfigCache::invalidateAll()
919{
920    for (size_t s = 0; s < sets.size(); s++) {
921        Set &set = sets[s];
922
923        for (size_t i = 0; i < set.size(); i++)
924            set[i].valid = false;
925    }
926}
927
928size_t
929ConfigCache::pickSetIdx(uint32_t sid, uint32_t ssid) const
930{
931    return (sid^ssid) % sets.size();
932}
933
934size_t
935ConfigCache::pickEntryIdxToReplace(const Set &set)
936{
937    size_t lru_idx = 0;
938    uint32_t lru_tick = UINT32_MAX;
939
940    for (size_t i = 0; i < set.size(); i++) {
941        if (!set[i].valid) {
942            insertions++;
943            return i;
944        }
945
946        if (set[i].lastUsed < lru_tick) {
947            lru_idx = i;
948            lru_tick = set[i].lastUsed;
949        }
950    }
951
952    switch (replacementPolicy) {
953    case SMMU_CACHE_REPL_ROUND_ROBIN:
954        return nextToReplace = ((nextToReplace+1) % associativity);
955
956    case SMMU_CACHE_REPL_RANDOM:
957        return random.random<size_t>(0, associativity-1);
958
959    case SMMU_CACHE_REPL_LRU:
960        return lru_idx;
961
962    default:
963        panic("Unknown replacement policy %d\n", replacementPolicy);
964    }
965
966}
967
968/*
969 * WalkCache
970 */
971
972WalkCache::WalkCache(const std::array<unsigned, 2*WALK_CACHE_LEVELS> &_sizes,
973                     unsigned _associativity, const std::string &policy) :
974    SMMUv3BaseCache(policy, WALKCACHE_SEED),
975    associativity(_associativity),
976    sizes()
977{
978    unsigned numEntries = std::accumulate(&_sizes[0],
979                                          &_sizes[2*WALK_CACHE_LEVELS], 0);
980
981    if (associativity == 0)
982        associativity = numEntries; // fully associative
983
984    if (numEntries == 0)
985        fatal("WalkCache must have at least one entry\n");
986
987    for (size_t i = 0; i < 2*WALK_CACHE_LEVELS; i++){
988        if (_sizes[i] % associativity != 0)
989              fatal("Number of WalkCache entries at each level must be "
990                    "divisible by WalkCache associativity\n");
991
992        sizes[i] = _sizes[i] /  associativity;
993        offsets[i] = i==0 ? 0 : offsets[i-1] + sizes[i-1];
994    }
995
996    if (associativity > numEntries)
997        fatal("WalkCache associativity cannot be higher than "
998              "its number of entries\n");
999
1000    unsigned num_sets = numEntries / associativity;
1001
1002    if (num_sets*associativity != numEntries)
1003        fatal("Number of WalkCache entries must be divisible "
1004              "by its associativity\n");
1005
1006    Entry e;
1007    e.valid = false;
1008
1009    Set set(associativity, e);
1010    sets.resize(num_sets, set);
1011}
1012
1013const WalkCache::Entry*
1014WalkCache::lookup(Addr va, Addr vaMask,
1015                  uint16_t asid, uint16_t vmid,
1016                  unsigned stage, unsigned level,
1017                  bool updStats)
1018{
1019    const Entry *result = NULL;
1020
1021    Set &set = sets[pickSetIdx(va, vaMask, stage, level)];
1022
1023    for (size_t i = 0; i < set.size(); i++) {
1024        const Entry &e = set[i];
1025
1026        if (e.valid && (e.va & e.vaMask) == (va & e.vaMask) &&
1027            e.asid==asid && e.vmid==vmid && e.stage==stage && e.level==level)
1028        {
1029            if (result != NULL)
1030                panic("WalkCache: duplicate entry found!\n");
1031
1032            result = &e;
1033            break;
1034        }
1035    }
1036
1037    if (updStats) {
1038        if (result)
1039            result->lastUsed = useStamp++;
1040
1041        totalLookups++;
1042        if (result == NULL)
1043            totalMisses++;
1044
1045        lookupsByStageLevel[stage-1][level]++;
1046        totalLookupsByStageLevel[stage-1][level]++;
1047        if (result == NULL) {
1048            missesByStageLevel[stage-1][level]++;
1049            totalMissesByStageLevel[stage-1][level]++;
1050        }
1051    }
1052
1053    return result;
1054}
1055
1056void
1057WalkCache::store(const Entry &incoming)
1058{
1059    if (!incoming.valid)
1060        panic("Tried to store an invalid entry\n");
1061
1062    assert(incoming.stage==1 || incoming.stage==2);
1063    assert(incoming.level<=WALK_CACHE_LEVELS);
1064
1065    incoming.lastUsed = 0;
1066
1067    const Entry *existing = lookup(incoming.va, incoming.vaMask,
1068                                   incoming.asid, incoming.vmid,
1069                                   incoming.stage, incoming.level, false);
1070
1071    if (existing) {
1072        *const_cast<Entry *> (existing) = incoming;
1073    } else {
1074        Set &set = sets[pickSetIdx(incoming.va, incoming.vaMask,
1075                                   incoming.stage, incoming.level)];
1076        set[pickEntryIdxToReplace(set, incoming.stage, incoming.level)] =
1077            incoming;
1078    }
1079
1080    totalUpdates++;
1081    updatesByStageLevel[incoming.stage-1][incoming.level]++;
1082    totalUpdatesByStageLevel[incoming.stage-1][incoming.level]++;
1083}
1084
1085void
1086WalkCache::invalidateVA(Addr va, uint16_t asid, uint16_t vmid,
1087                        const bool leaf_only)
1088{
1089    for (size_t s = 0; s < sets.size(); s++) {
1090        Set &set = sets[s];
1091
1092        for (size_t i = 0; i < set.size(); i++) {
1093            Entry &e = set[i];
1094
1095            if ((!leaf_only || e.leaf) && (e.va & e.vaMask) == (va & e.vaMask)
1096                && e.asid == asid && e.vmid == vmid)
1097            {
1098                e.valid = false;
1099            }
1100        }
1101    }
1102}
1103
1104void
1105WalkCache::invalidateVAA(Addr va, uint16_t vmid, const bool leaf_only)
1106{
1107    for (size_t s = 0; s < sets.size(); s++) {
1108        Set &set = sets[s];
1109
1110        for (size_t i = 0; i < set.size(); i++) {
1111            Entry &e = set[i];
1112
1113            if ((!leaf_only || e.leaf) && (e.va & e.vaMask) == (va & e.vaMask)
1114                && e.vmid == vmid)
1115            {
1116                e.valid = false;
1117            }
1118        }
1119    }
1120}
1121
1122void
1123WalkCache::invalidateASID(uint16_t asid, uint16_t vmid)
1124{
1125    panic("%s unimplemented\n", __func__);
1126}
1127
1128void
1129WalkCache::invalidateVMID(uint16_t vmid)
1130{
1131    for (size_t s = 0; s < sets.size(); s++) {
1132        Set &set = sets[s];
1133
1134        for (size_t i = 0; i < set.size(); i++) {
1135            Entry &e = set[i];
1136
1137            if (e.vmid == vmid)
1138                e.valid = false;
1139        }
1140    }
1141}
1142
1143void
1144WalkCache::invalidateAll()
1145{
1146    for (size_t s = 0; s < sets.size(); s++) {
1147        Set &set = sets[s];
1148
1149        for (size_t i = 0; i < set.size(); i++)
1150            set[i].valid = false;
1151    }
1152}
1153
1154size_t
1155WalkCache::pickSetIdx(Addr va, Addr vaMask,
1156                      unsigned stage, unsigned level) const
1157{
1158    (void) stage;
1159
1160    int size, offset;
1161
1162    switch (stage) {
1163        case 1:
1164            assert (level<=3);
1165            size = sizes[0*WALK_CACHE_LEVELS + level];
1166            offset = offsets[0*WALK_CACHE_LEVELS + level];
1167            break;
1168
1169        case 2:
1170            assert (level<=3);
1171            size = sizes[1*WALK_CACHE_LEVELS + level];
1172            offset = offsets[1*WALK_CACHE_LEVELS + level];
1173            break;
1174
1175        default:
1176            panic("bad stage");
1177    }
1178
1179    return ((va >> findLsbSet(vaMask)) % size) + offset;
1180}
1181
1182size_t
1183WalkCache::pickEntryIdxToReplace(const Set &set,
1184                                 unsigned stage, unsigned level)
1185{
1186    size_t lru_idx = 0;
1187    uint32_t lru_tick = UINT32_MAX;
1188
1189    for (size_t i = 0; i < set.size(); i++) {
1190        if (!set[i].valid) {
1191            insertions++;
1192            insertionsByStageLevel[stage-1][level]++;
1193            return i;
1194        }
1195
1196        if (set[i].lastUsed < lru_tick) {
1197            lru_idx = i;
1198            lru_tick = set[i].lastUsed;
1199        }
1200    }
1201
1202    switch (replacementPolicy) {
1203    case SMMU_CACHE_REPL_ROUND_ROBIN:
1204        return nextToReplace = ((nextToReplace+1) % associativity);
1205
1206    case SMMU_CACHE_REPL_RANDOM:
1207        return random.random<size_t>(0, associativity-1);
1208
1209    case SMMU_CACHE_REPL_LRU:
1210        return lru_idx;
1211
1212    default:
1213        panic("Unknown replacement policy %d\n", replacementPolicy);
1214    }
1215
1216}
1217
1218void
1219WalkCache::regStats(const std::string &name)
1220{
1221    using namespace Stats;
1222
1223    SMMUv3BaseCache::regStats(name);
1224
1225    for (int s = 0; s < 2; s++) {
1226        for (int l = 0; l < WALK_CACHE_LEVELS; l++) {
1227            averageLookupsByStageLevel[s][l]
1228                .name(csprintf("%s.averageLookupsS%dL%d", name, s+1, l))
1229                .desc("Average number lookups per second")
1230                .flags(pdf);
1231
1232            totalLookupsByStageLevel[s][l]
1233                .name(csprintf("%s.totalLookupsS%dL%d", name, s+1, l))
1234                .desc("Total number of lookups")
1235                .flags(pdf);
1236
1237            averageLookupsByStageLevel[s][l] =
1238                totalLookupsByStageLevel[s][l] / simSeconds;
1239
1240
1241            averageMissesByStageLevel[s][l]
1242                .name(csprintf("%s.averageMissesS%dL%d", name, s+1, l))
1243                .desc("Average number misses per second")
1244                .flags(pdf);
1245
1246            totalMissesByStageLevel[s][l]
1247                .name(csprintf("%s.totalMissesS%dL%d", name, s+1, l))
1248                .desc("Total number of misses")
1249                .flags(pdf);
1250
1251            averageMissesByStageLevel[s][l] =
1252                totalMissesByStageLevel[s][l] / simSeconds;
1253
1254
1255            averageUpdatesByStageLevel[s][l]
1256                .name(csprintf("%s.averageUpdatesS%dL%d", name, s+1, l))
1257                .desc("Average number updates per second")
1258                .flags(pdf);
1259
1260            totalUpdatesByStageLevel[s][l]
1261                .name(csprintf("%s.totalUpdatesS%dL%d", name, s+1, l))
1262                .desc("Total number of updates")
1263                .flags(pdf);
1264
1265            averageUpdatesByStageLevel[s][l] =
1266                totalUpdatesByStageLevel[s][l] / simSeconds;
1267
1268
1269            averageHitRateByStageLevel[s][l]
1270                .name(csprintf("%s.averageHitRateS%dL%d", name, s+1, l))
1271                .desc("Average hit rate")
1272                .flags(pdf);
1273
1274            averageHitRateByStageLevel[s][l] =
1275                (totalLookupsByStageLevel[s][l] -
1276                 totalMissesByStageLevel[s][l])
1277                / totalLookupsByStageLevel[s][l];
1278
1279            insertionsByStageLevel[s][l]
1280                .name(csprintf("%s.insertionsS%dL%d", name, s+1, l))
1281                .desc("Number of insertions (not replacements)")
1282                .flags(pdf);
1283        }
1284    }
1285}
1286