创建一个 2-way set-associative cache 有 8 个 sets 的缓存设计,主要需要以下几个步骤:
缓存结构
-
缓存概念:
- 2-way:每个 set 中包含 2 条缓存线(lines)。
- 8 sets:缓存总共分为 8 个 sets。
- 每条缓存线包含:
- Valid Bit:指示缓存线是否有效。
- Tag:用来标识地址。
- Data:存储实际数据。
- Dirty Bit(对于 write-back 策略需要)。
-
缓存存储容量:
如果每条缓存线大小是line_size
字节:- 总容量 =
line_size × 2 × 8
字节。
- 总容量 =
缓存代码实现
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
// 定义单个缓存线
typedef struct {
bool valid; // 有效位
bool dirty; // 脏位(可选)
uint32_t tag; // Tag 部分
uint8_t *data; // 实际数据(指向内存块)
} CacheLine;
// 定义缓存的 Set
typedef struct {
CacheLine lines[2]; // 每个 set 有 2 条缓存线(2-way)
} CacheSet;
// 定义缓存
typedef struct {
CacheSet sets[8]; // 缓存中有 8 个 sets
uint32_t line_size; // 每条缓存线大小
} Cache;
// 初始化缓存
void initializeCache(Cache *cache, uint32_t line_size) {
cache->line_size = line_size;
for (int i = 0; i < 8; i++) { // 8 个 sets
for (int j = 0; j < 2; j++) { // 每个 set 中有 2 条缓存线
cache->sets[i].lines[j].valid = false;
cache->sets[i].lines[j].dirty = false;
cache->sets[i].lines[j].tag = 0;
cache->sets[i].lines[j].data = (uint8_t *)malloc(line_size);
}
}
}
// 模拟访问地址
bool accessCache(Cache *cache, uint32_t address, uint8_t *memory) {
uint32_t line_size = cache->line_size;
uint32_t index = (address / line_size) % 8; // 从地址中提取 set index(3 bits)
uint32_t tag = address / (line_size * 8); // 提取 tag
// 遍历 set 中的每条缓存线(2-way)
for (int i = 0; i < 2; i++) {
CacheLine *line = &cache->sets[index].lines[i];
if (line->valid && line->tag == tag) { // 命中
printf("Address %u: Hit\n", address);
return true;
}
}
// 缺失,加载数据到缓存
printf("Address %u: Miss\n", address);
// 使用简单替换策略(如替换第一个空缓存线,或者随机替换)
CacheLine *lineToReplace = &cache->sets[index].lines[0];
if (cache->sets[index].lines[1].valid == false) {
lineToReplace = &cache->sets[index].lines[1];
}
// 如果需要 write-back,检查脏位
if (lineToReplace->dirty) {
uint32_t writeBackAddress = (lineToReplace->tag * 8 + index) * line_size;
memcpy(&memory[writeBackAddress], lineToReplace->data, line_size);
printf("Write-back to memory at address %u\n", writeBackAddress);
}
// 替换缓存线
lineToReplace->valid = true;
lineToReplace->dirty = false;
lineToReplace->tag = tag;
memcpy(lineToReplace->data, &memory[address], line_size);
return false;
}
// 清理缓存
void destroyCache(Cache *cache) {
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 2; j++) {
free(cache->sets[i].lines[j].data);
}
}
}
示例使用
int main() {
uint32_t memory[1024] = {0}; // 模拟主存
for (int i = 0; i < 1024; i++) {
memory[i] = i * 10; // 初始化内存
}
Cache cache;
initializeCache(&cache, 16); // 每条缓存线大小为 16 字节
// 模拟访问
accessCache(&cache, 0, (uint8_t *)memory);
accessCache(&cache, 128, (uint8_t *)memory);
accessCache(&cache, 256, (uint8_t *)memory);
accessCache(&cache, 0, (uint8_t *)memory);
// 销毁缓存
destroyCache(&cache);
return 0;
}
核心概念
- Index:根据地址计算出需要访问的 set。
index = (address / line_size) % sets_count
- 这里
sets_count = 8
。
- Tag:用于标识缓存中的地址是否匹配。
tag = address / (line_size * sets_count)
- 2-Way:每个 set 中有两个缓存线,可以选择替换策略如 LRU 或 FIFO。
- Data:每条缓存线存储实际数据。
- Write-back:如果脏位为
true
,需要将缓存中的数据写回内存。
此代码完整实现了 2-way 8-sets 缓存的初始化、访问、替换和销毁功能。