O3 Cache Block
Cache internal class hierarchies in GEM5
1
2
3
4
5
6
7
8
92 /**
93 * A basic cache interface. Implements some common functions for speed.
94 */
95 class BaseCache : public ClockedObject
96 {
......
349 /** Tag and data Storage */
350 BaseTags *tags;
1
2
3
4
5
6
7
8
9
10
11
70 /**
71 * A common base class of Cache tagstore objects.
72 */
73 class BaseTags : public ClockedObject
74 {
......
88 /** Indexing policy */
89 BaseIndexingPolicy *indexingPolicy;
......
102 /** The data blocks, 1 per cache block. */
103 std::unique_ptr<uint8_t[]> dataBlks;
The main cache structure called BaseCache contains member field tags (object of BaseTags) The BaseTags class contains the actual data blocks for maintaining data to the cache. Also, it has BaseIndexingPolicy that determines which entry should be selected or evicted as a result of cache access operations (including cache read and write).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
60 /**
61 * A common base class for indexing table locations. Classes that inherit
62 * from it determine hash functions that should be applied based on the set
63 * and way. These functions are then applied to re-map the original values.
64 * @sa \ref gem5MemorySystem "gem5 Memory System"
65 */
66 class BaseIndexingPolicy : public SimObject
67 {
68 protected:
69 /**
70 * The associativity.
71 */
72 const unsigned assoc;
73
74 /**
75 * The number of sets in the cache.
76 */
77 const uint32_t numSets;
78
79 /**
80 * The amount to shift the address to get the set.
81 */
82 const int setShift;
83
84 /**
85 * Mask out all bits that aren't part of the set index.
86 */
87 const unsigned setMask;
88
89 /**
90 * The cache sets.
91 */
92 std::vector<std::vector<ReplaceableEntry*>> sets;
Note that the BaseIndexingPolicy contains sets consisting of multiple entries of ReplaceableEntry. The CacheBlk will be stored in the sets member field because the CacheBlk is a child of ReplaceableEntry class. Also, sets can be indexed with set and way to provide nomenclature of the cache.
CacheBlk the basic unit of each cache block entry
1
2
3
4
5
6
65 /**
66 * A Basic Cache block.
67 * Contains information regarding its coherence, prefetching status, as
68 * well as a pointer to its data.
69 */
70 class CacheBlk : public TaggedEntry
1
2
3
4
5
6
41 /**
42 * A tagged entry is an entry containing a tag. Each tag is accompanied by a
43 * secure bit, which informs whether it belongs to a secure address space.
44 * A tagged entry's contents are only relevant if it is marked as valid.
45 */
46 class TaggedEntry : public ReplaceableEntry
1
2
3
4
5
6
7
8
9
10
11
53 /**
54 * A replaceable entry is a basic entry in a 2d table-like structure that needs
55 * to have replacement functionality. This entry is located in a specific row
56 * and column of the table (set and way in cache nomenclature), which are
57 * stored within the entry itself.
58 *
59 * It contains the replacement data pointer, which must be instantiated by the
60 * replacement policy before being used.
61 * @sa Replacement Policies
62 */
63 class ReplaceableEntry
The cache block maintains three important data related with one cache entry: coherence information, prefetching status, and pointer to the data. Therefore, let’s take a look at the structure and interface required for managing those information. Remaining question: Then where the data exactly is stored? in the CacheBlk object? or in the dataBlks member field of the tags?
Cache management
Initializing cache blocks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
79 BaseCache::BaseCache(const BaseCacheParams &p, unsigned blk_size)
80 : ClockedObject(p),
81 cpuSidePort (p.name + ".cpu_side_port", this, "CpuSidePort"),
82 memSidePort(p.name + ".mem_side_port", this, "MemSidePort"),
83 mshrQueue("MSHRs", p.mshrs, 0, p.demand_mshr_reserve, p.name),
84 writeBuffer("write buffer", p.write_buffers, p.mshrs, p.name),
85 tags(p.tags),
86 compressor(p.compressor),
87 prefetcher(p.prefetcher),
88 writeAllocator(p.write_allocator),
89 writebackClean(p.writeback_clean),
90 tempBlockWriteback(nullptr),
91 writebackTempBlockAtomicEvent([this]{ writebackTempBlockAtomic(); },
92 name(), false,
93 EventBase::Delayed_Writeback_Pri),
94 blkSize(blk_size),
95 lookupLatency(p.tag_latency),
96 dataLatency(p.data_latency),
97 forwardLatency(p.tag_latency),
98 fillLatency(p.data_latency),
99 responseLatency(p.response_latency),
100 sequentialAccess(p.sequential_access),
101 numTarget(p.tgts_per_mshr),
102 forwardSnoops(true),
103 clusivity(p.clusivity),
104 isReadOnly(p.is_read_only),
105 replaceExpansions(p.replace_expansions),
106 moveContractions(p.move_contractions),
107 blocked(0),
108 order(0),
109 noTargetMSHR(nullptr),
110 missCount(p.max_miss_count),
111 addrRanges(p.addr_ranges.begin(), p.addr_ranges.end()),
112 system(p.system),
113 stats(*this)
114 {
115 // the MSHR queue has no reserve entries as we check the MSHR
116 // queue on every single allocation, whereas the write queue has
117 // as many reserve entries as we have MSHRs, since every MSHR may
118 // eventually require a writeback, and we do not check the write
119 // buffer before committing to an MSHR
120
121 // forward snoops is overridden in init() once we can query
122 // whether the connected requestor is actually snooping or not
123
124 tempBlock = new TempCacheBlk(blkSize);
125
126 tags->tagsInit();
127 if (prefetcher)
128 prefetcher->setCache(this);
129
130 fatal_if(compressor && !dynamic_cast<CompressedTags*>(tags),
131 "The tags of compressed cache %s must derive from CompressedTags",
132 name());
133 warn_if(!compressor && dynamic_cast<CompressedTags*>(tags),
134 "Compressed cache %s does not have a compression algorithm", name());
135 if (compressor)
When the BaseCache is constructed, it initializes its member field tags by passing the parameters required for initializing the BaseTags class. Because we are currently dealing with the BaseSetAssoc class which inherits the BaseTags class, its constructor will be invoked first instead of the BaseTags’s constructor.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
55 BaseSetAssoc::BaseSetAssoc(const Params &p)
56 :BaseTags(p), allocAssoc(p.assoc), blks(p.size / p.block_size),
57 sequentialAccess(p.sequential_access),
58 replacementPolicy(p.replacement_policy)
59 {
60 // There must be a indexing policy
61 fatal_if(!p.indexing_policy, "An indexing policy is required");
62
63 // Check parameters
64 if (blkSize < 4 || !isPowerOf2(blkSize)) {
65 fatal("Block size must be at least 4 and a power of 2");
66 }
67 }
68
69 void
70 BaseSetAssoc::tagsInit()
71 {
72 // Initialize all blocks
73 for (unsigned blk_index = 0; blk_index < numBlocks; blk_index++) {
74 // Locate next cache block
75 CacheBlk* blk = &blks[blk_index];
76
77 // Link block to indexing policy
78 indexingPolicy->setEntry(blk, blk_index);
79
80 // Associate a data chunk to the block
81 blk->data = &dataBlks[blkSize*blk_index];
82
83 // Associate a replacement data entry to the block
84 blk->replacementData = replacementPolicy->instantiateEntry();
85 }
86 }
When the BaseSetAssoc is populated, it initializes the blk member field with (p.size / p.block_size) entries. After that, the constructor of the BaseCache invokes the tagsInit function implemented in the BaseSetAssoc class. As shown in the above code, it select one CacheBlk from the blk memeber field of the BaseSetAssoc.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
80 void
81 BaseIndexingPolicy::setEntry(ReplaceableEntry* entry, const uint64_t index)
82 {
83 // Calculate set and way from entry index
84 const std::lldiv_t div_result = std::div((long long)index, assoc);
85 const uint32_t set = div_result.quot;
86 const uint32_t way = div_result.rem;
87
88 // Sanity check
89 assert(set < numSets);
90
91 // Assign a free pointer
92 sets[set][way] = entry;
93
94 // Inform the entry its position
95 entry->setPosition(set, way);
96 }
The setEntry function of the indexingPolicy will be invoked next so that it associates the CacheBlk in the Tag class to the sets member field of the indexingPolicy
Map datablk to cacheblk
When the BaseSetAssoc object is constructed, it invoked its parent’s constructor BaseTags with the parameter.
1
2
3
4
5
6
7
8
9
10
11
61 BaseTags::BaseTags(const Params &p)
62 : ClockedObject(p), blkSize(p.block_size), blkMask(blkSize - 1),
63 size(p.size), lookupLatency(p.tag_latency),
64 system(p.system), indexingPolicy(p.indexing_policy),
65 warmupBound((p.warmup_percentage/100.0) * (p.size / p.block_size)),
66 warmedUp(false), numBlocks(p.size / p.block_size),
67 dataBlks(new uint8_t[p.size]), // Allocate data storage in one big chunk
68 stats(*this)
69 {
70 registerExitCallback([this]() { cleanupRefs(); });
71 }
You might remember that the BaseTags class contains dataBlks member field. Here the constructor initializes the dataBlks to have (p.size / p.block_size) entries for storing the cache data. Note that the number of entries of the dataBlks and the blk are same because one dataBlk is associated with one CacheBlk.
1
2
3
4
5
6
7
8
9
10
11
12
13
69 void
70 BaseSetAssoc::tagsInit()
71 {
72 // Initialize all blocks
73 for (unsigned blk_index = 0; blk_index < numBlocks; blk_index++) {
74 // Locate next cache block
75 CacheBlk* blk = &blks[blk_index];
76
77 // Link block to indexing policy
78 indexingPolicy->setEntry(blk, blk_index);
79
80 // Associate a data chunk to the block
81 blk->data = &dataBlks[blkSize*blk_index];
When you go back to the tagsInit, you can find that one dataBlks having blkSize size is mapped to the one CacheBlk. Because this CacheBlk is mapped to particular way of one set, the data will be also associated with that cache entry. To summary, the BaseCache maintains the tag. And the tag provides cache storage and cacheblks to the BaseIndexingPolicy object. Conceptually, the cache is maintained by the BaseIndexingPolicy class which maintains the cache with set and ways indexing.
Comments powered by Disqus.