创建一个 2-way set-associative cache 有 8 个 sets 的缓存设计,主要需要以下几个步骤:


缓存结构

  1. 缓存概念

    • 2-way:每个 set 中包含 2 条缓存线(lines)。
    • 8 sets:缓存总共分为 8 个 sets。
    • 每条缓存线包含:
      • Valid Bit:指示缓存线是否有效。
      • Tag:用来标识地址。
      • Data:存储实际数据。
      • Dirty Bit(对于 write-back 策略需要)。
  2. 缓存存储容量
    如果每条缓存线大小是 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;
}

核心概念

  1. Index:根据地址计算出需要访问的 set。
    • index = (address / line_size) % sets_count
    • 这里 sets_count = 8
  2. Tag:用于标识缓存中的地址是否匹配。
    • tag = address / (line_size * sets_count)
  3. 2-Way:每个 set 中有两个缓存线,可以选择替换策略如 LRU 或 FIFO。
  4. Data:每条缓存线存储实际数据。
  5. Write-back:如果脏位为 true,需要将缓存中的数据写回内存。

此代码完整实现了 2-way 8-sets 缓存的初始化、访问、替换和销毁功能。

11-18 20:45