1/*
2 * Copyright (c) 2014, 2018-2019 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Stan Czerniawski
38 *          Damian Richardson
39 */
40
41#include "dev/arm/smmu_v3_caches.hh"
42
43#include <numeric>
44
45#include "base/bitfield.hh"
46#include "base/intmath.hh"
47#include "base/logging.hh"
48#include "sim/stats.hh"
49
50
51// taken from hex expansion of pi
52#define SMMUTLB_SEED     0xEA752DFE
53#define ARMARCHTLB_SEED  0x8B021FA1
54#define IPACACHE_SEED    0xE5A0CC0F
55#define CONFIGCACHE_SEED 0xB56F74E8
56#define WALKCACHE_SEED   0x18ACF3D6
57
58/*
59 * BaseCache
60 *
61 * TODO: move more code into this base class to reduce duplication.
62 */
63
64SMMUv3BaseCache::SMMUv3BaseCache(const std::string &policy_name, uint32_t seed) :
65    replacementPolicy(decodePolicyName(policy_name)),
66    nextToReplace(0),
67    random(seed),
68    useStamp(0)
69{}
70
71int
72SMMUv3BaseCache::decodePolicyName(const std::string &policy_name)
73{
74    if (policy_name == "rr") {
75        return SMMU_CACHE_REPL_ROUND_ROBIN;
76    } else if (policy_name == "rand") {
77        return SMMU_CACHE_REPL_RANDOM;
78    } else if (policy_name == "lru") {
79        return SMMU_CACHE_REPL_LRU;
80    } else {
81        panic("Unknown cache replacement policy '%s'\n", policy_name);
82    }
83}
84
85void
86SMMUv3BaseCache::regStats(const std::string &name)
87{
88    using namespace Stats;
89
90
91    averageLookups
92        .name(name + ".averageLookups")
93        .desc("Average number lookups per second")
94        .flags(pdf);
95
96    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    for (size_t s = 0; s < sets.size(); s++) {
1126        Set &set = sets[s];
1127
1128        for (size_t i = 0; i < set.size(); i++) {
1129            Entry &e = set[i];
1130
1131            if (e.asid==asid && e.vmid==vmid)
1132                e.valid = false;
1133        }
1134    }
1135}
1136
1137void
1138WalkCache::invalidateVMID(uint16_t vmid)
1139{
1140    for (size_t s = 0; s < sets.size(); s++) {
1141        Set &set = sets[s];
1142
1143        for (size_t i = 0; i < set.size(); i++) {
1144            Entry &e = set[i];
1145
1146            if (e.vmid == vmid)
1147                e.valid = false;
1148        }
1149    }
1150}
1151
1152void
1153WalkCache::invalidateAll()
1154{
1155    for (size_t s = 0; s < sets.size(); s++) {
1156        Set &set = sets[s];
1157
1158        for (size_t i = 0; i < set.size(); i++)
1159            set[i].valid = false;
1160    }
1161}
1162
1163size_t
1164WalkCache::pickSetIdx(Addr va, Addr vaMask,
1165                      unsigned stage, unsigned level) const
1166{
1167    (void) stage;
1168
1169    int size, offset;
1170
1171    switch (stage) {
1172        case 1:
1173            assert (level<=3);
1174            size = sizes[0*WALK_CACHE_LEVELS + level];
1175            offset = offsets[0*WALK_CACHE_LEVELS + level];
1176            break;
1177
1178        case 2:
1179            assert (level<=3);
1180            size = sizes[1*WALK_CACHE_LEVELS + level];
1181            offset = offsets[1*WALK_CACHE_LEVELS + level];
1182            break;
1183
1184        default:
1185            panic("bad stage");
1186    }
1187
1188    return ((va >> findLsbSet(vaMask)) % size) + offset;
1189}
1190
1191size_t
1192WalkCache::pickEntryIdxToReplace(const Set &set,
1193                                 unsigned stage, unsigned level)
1194{
1195    size_t lru_idx = 0;
1196    uint32_t lru_tick = UINT32_MAX;
1197
1198    for (size_t i = 0; i < set.size(); i++) {
1199        if (!set[i].valid) {
1200            insertions++;
1201            insertionsByStageLevel[stage-1][level]++;
1202            return i;
1203        }
1204
1205        if (set[i].lastUsed < lru_tick) {
1206            lru_idx = i;
1207            lru_tick = set[i].lastUsed;
1208        }
1209    }
1210
1211    switch (replacementPolicy) {
1212    case SMMU_CACHE_REPL_ROUND_ROBIN:
1213        return nextToReplace = ((nextToReplace+1) % associativity);
1214
1215    case SMMU_CACHE_REPL_RANDOM:
1216        return random.random<size_t>(0, associativity-1);
1217
1218    case SMMU_CACHE_REPL_LRU:
1219        return lru_idx;
1220
1221    default:
1222        panic("Unknown replacement policy %d\n", replacementPolicy);
1223    }
1224
1225}
1226
1227void
1228WalkCache::regStats(const std::string &name)
1229{
1230    using namespace Stats;
1231
1232    SMMUv3BaseCache::regStats(name);
1233
1234    for (int s = 0; s < 2; s++) {
1235        for (int l = 0; l < WALK_CACHE_LEVELS; l++) {
1236            averageLookupsByStageLevel[s][l]
1237                .name(csprintf("%s.averageLookupsS%dL%d", name, s+1, l))
1238                .desc("Average number lookups per second")
1239                .flags(pdf);
1240
1241            totalLookupsByStageLevel[s][l]
1242                .name(csprintf("%s.totalLookupsS%dL%d", name, s+1, l))
1243                .desc("Total number of lookups")
1244                .flags(pdf);
1245
1246            averageLookupsByStageLevel[s][l] =
1247                totalLookupsByStageLevel[s][l] / simSeconds;
1248
1249
1250            averageMissesByStageLevel[s][l]
1251                .name(csprintf("%s.averageMissesS%dL%d", name, s+1, l))
1252                .desc("Average number misses per second")
1253                .flags(pdf);
1254
1255            totalMissesByStageLevel[s][l]
1256                .name(csprintf("%s.totalMissesS%dL%d", name, s+1, l))
1257                .desc("Total number of misses")
1258                .flags(pdf);
1259
1260            averageMissesByStageLevel[s][l] =
1261                totalMissesByStageLevel[s][l] / simSeconds;
1262
1263
1264            averageUpdatesByStageLevel[s][l]
1265                .name(csprintf("%s.averageUpdatesS%dL%d", name, s+1, l))
1266                .desc("Average number updates per second")
1267                .flags(pdf);
1268
1269            totalUpdatesByStageLevel[s][l]
1270                .name(csprintf("%s.totalUpdatesS%dL%d", name, s+1, l))
1271                .desc("Total number of updates")
1272                .flags(pdf);
1273
1274            averageUpdatesByStageLevel[s][l] =
1275                totalUpdatesByStageLevel[s][l] / simSeconds;
1276
1277
1278            averageHitRateByStageLevel[s][l]
1279                .name(csprintf("%s.averageHitRateS%dL%d", name, s+1, l))
1280                .desc("Average hit rate")
1281                .flags(pdf);
1282
1283            averageHitRateByStageLevel[s][l] =
1284                (totalLookupsByStageLevel[s][l] -
1285                 totalMissesByStageLevel[s][l])
1286                / totalLookupsByStageLevel[s][l];
1287
1288            insertionsByStageLevel[s][l]
1289                .name(csprintf("%s.insertionsS%dL%d", name, s+1, l))
1290                .desc("Number of insertions (not replacements)")
1291                .flags(pdf);
1292        }
1293    }
1294}
1295