Files
csapp2025/cachelab/Cache.bak2
2025-04-24 22:32:48 +08:00

404 lines
21 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

///////////////////////////////////////////////////////////////////////
//// Copyright 2022 by mars. //
///////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#define DEBUG 0
#define GET_POWER_OF_2(X) (X == 0x00 ? 0 : \
X == 0x01 ? 0 : \
X == 0x02 ? 1 : \
X == 0x04 ? 2 : \
X == 0x08 ? 3 : \
X == 0x10 ? 4 : \
X == 0x20 ? 5 : \
X == 0x40 ? 6 : \
X == 0x80 ? 7 : \
X == 0x100 ? 8 : \
X == 0x200 ? 9 : \
X == 0x400 ? 10 : \
X == 0x800 ? 11 : \
X == 0x1000 ? 12 : \
X == 0x2000 ? 13 : \
X == 0x4000 ? 14 : \
X == 0x8000 ? 15 : \
X == 0x10000 ? 16 : \
X == 0x20000 ? 17 : \
X == 0x40000 ? 18 : \
X == 0x80000 ? 19 : \
X == 0x100000 ? 20 : \
X == 0x200000 ? 21 : \
X == 0x400000 ? 22 : \
X == 0x800000 ? 23 : \
X == 0x1000000 ? 24 : \
X == 0x2000000 ? 25 : \
X == 0x4000000 ? 26 : \
X == 0x8000000 ? 27 : \
X == 0x10000000 ? 28 : \
X == 0x20000000 ? 29 : \
X == 0x40000000 ? 30 : \
X == 0x80000000 ? 31 : \
X == 0x100000000 ? 32 : 0)
/*
全相联Data Cache16KB大小
每行存放64个字节
*/
#define DCACHE_SIZE 16384
#define DCACHE_DATA_PER_LINE 128 // 必须是8字节的倍数
#define DCACHE_DATA_PER_LINE_ADDR_BITS GET_POWER_OF_2(DCACHE_DATA_PER_LINE) // 必须与上面设置一致即64字节需要6位地址
#define DCACHE_LINES (DCACHE_SIZE/DCACHE_DATA_PER_LINE)
#define MAX_AGE (DCACHE_LINES-1) // 每行有一个唯一的Age
// Cache行的结构包括Valid、Age、Dirty、Tag和Data。你所有的状态信息只能记录在Cache行中
struct DCACHE_LineStruct
{
UINT8 Valid;
UINT16 Age;
UINT8 Dirty;
UINT64 Tag;
UINT8 Data[DCACHE_DATA_PER_LINE];
} DCache[DCACHE_LINES];
/*
DCache初始化代码一般需要把DCache的有效位Valid设置为0
模拟器启动时会调用此InitDataCache函数
*/
void InitDataCache()
{
UINT32 i;
printf("[%s] +-----------------------------------+\n", __func__);
printf("[%s] | 威震天的Data Cache初始化ing.... |\n", __func__);
printf("[%s] +-----------------------------------+\n", __func__);
for (i = 0; i < DCACHE_LINES; i++) {
DCache[i].Valid = 0;
DCache[i].Age = i;
DCache[i].Dirty = 0;
}
}
/*
从Memory中读入一行数据到Data Cache中
*/
void LoadDataCacheLineFromMemory(UINT64 Address, UINT32 CacheLineIndex)
{
// 一次性从Memory中将DCACHE_DATA_PER_LINE数据读入某个Data Cache行
// 提供了一个函数一次可以读入8个字节
UINT32 i;
UINT64 ReadData;
UINT64 AlignAddress;
UINT64* pp;
AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1); // 地址必须对齐到DCACHE_DATA_PER_LINE (64)字节边界
pp = (UINT64*)DCache[CacheLineIndex].Data;
for (i = 0; i < DCACHE_DATA_PER_LINE / 8; i++)
{
ReadData = ReadMemory(AlignAddress + 8LL * i);
if (DEBUG)
printf("[%s] Address=%016llX ReadData=%016llX\n", __func__, AlignAddress + 8LL * i, ReadData);
pp[i] = ReadData;
}
}
/*
将Data Cache中的一行数据写入存储器
*/
void StoreDataCacheLineToMemory(UINT64 Address, UINT32 CacheLineIndex)
{
// 一次性将DCACHE_DATA_PER_LINE数据从某个Data Cache行写入Memory中
// 提供了一个函数一次可以写入8个字节
UINT32 i;
UINT64 WriteData;
UINT64 AlignAddress;
UINT64* pp;
AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1); // 地址必须对齐到DCACHE_DATA_PER_LINE (64)字节边界
pp = (UINT64*)DCache[CacheLineIndex].Data;
WriteData = 0;
for (i = 0; i < DCACHE_DATA_PER_LINE / 8; i++)
{
WriteData = pp[i];
WriteMemory(AlignAddress + 8LL * i, WriteData);
if (DEBUG)
printf("[%s] Address=%016llX WriteData=%016llX\n", __func__, AlignAddress + 8LL * i, WriteData);
}
}
// 从所有行中找到需要替换的Cache行
// 如果有某行的Valid=0则返回该行行号
// 否则返回Age最大的行的行号
int GetReplaceLine()
{
int replace_line = -1;
UINT16 max_age = 0;
for (int i = 0; i < DCACHE_LINES; i++)
{
if (!DCache[i].Valid)
{
return i;
}
if (DCache[i].Age > max_age)
{
max_age = DCache[i].Age;
replace_line = i;
}
}
return replace_line;
}
// 更新Age将HitLine指定的Cache行的Age设置为0其他行的Age要相应调整
// 注意要确保所有行的Age分别为0~MAX_AGE且唯一
// void UpdateAge(int HitLine)
// {
// for (int i = 0; i < DCACHE_LINES; i++) {
// if (i != HitLine && DCache[i].Valid && DCache[i].Age < MAX_AGE) {
// DCache[i].Age++;
// }
// }
// DCache[HitLine].Age = 0;
// }
void UpdateAge(int HitLine)
{
UINT16 old_age = DCache[HitLine].Age;
DCache[HitLine].Age = 0;
for (int i = 0; i < DCACHE_LINES; i++)
{
if (i != HitLine && DCache[i].Valid)
{
if (DCache[i].Age < old_age)
{
DCache[i].Age++;
}
}
}
}
/*
Data Cache访问接口系统模拟器会调用此接口来实现对你的Data Cache访问
Address: 访存字节地址
Operation: 操作:读操作('L')、写操作('S')、读-修改-写操作('M'
DataSize: 数据大小1字节、2字节、4字节、8字节
StoreValue: 当执行写操作的时候,需要写入的数据
LoadResult: 当执行读操作的时候从Cache读出的数据
*/
UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, UINT64 StoreValue, UINT64* LoadResult)
{
UINT8 BlockOffset;
UINT64 AddressTag;
UINT8 MissFlag = 'M';
UINT64 ReadValue;
int HitLine = -1;
*LoadResult = 0;
// 全相联中Address被切分为 AddressTag 和 BlockOffset
BlockOffset = Address % DCACHE_DATA_PER_LINE;
AddressTag = Address >> DCACHE_DATA_PER_LINE_ADDR_BITS;
// 查找是否命中
for (int i = 0; i < DCACHE_LINES; i++) {
if (DCache[i].Valid == 1 && DCache[i].Tag == AddressTag) {
MissFlag = 'H';
HitLine = i;
break;
}
}
if (MissFlag == 'H') // 命中!
{
if (Operation == 'L') // 读操作
{
ReadValue = 0;
switch (DataSize)
{
case 1: // 1个字节
ReadValue = DCache[HitLine].Data[BlockOffset + 0];
break;
case 2: // 2个字节
BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界
ReadValue = DCache[HitLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 0];
break;
case 4: // 4个字节
BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界
ReadValue = DCache[HitLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 0];
break;
case 8: // 8个字节
BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界
ReadValue = DCache[HitLine].Data[BlockOffset + 7]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 6]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 5]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 4]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
ReadValue |= DCache[HitLine].Data[BlockOffset + 0];
break;
}
*LoadResult = ReadValue;
if (DEBUG)
printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX ReadValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue, ReadValue);
}
else if (Operation == 'S' || Operation == 'M') // 写操作(修改操作在此等价于写操作)
{
if (DEBUG)
printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue);
switch (DataSize)
{
case 1: // 1个字节
DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF;
break;
case 2: // 2个字节
BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界
DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 1] = StoreValue & 0xFF;
break;
case 4: // 4个字节
BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界
DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 3] = StoreValue & 0xFF;
break;
case 8: // 8个字节
BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界
DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 3] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 4] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 5] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 6] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[HitLine].Data[BlockOffset + 7] = StoreValue & 0xFF;
break;
}
// 写回策略数据只写入Cache并设置脏位
DCache[HitLine].Dirty = 1;
}
// 更新访问时间
UpdateAge(HitLine);
}
else
{
if (DEBUG)
printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue);
// 不命中, 获取要替换的行
int ReplaceLine = GetReplaceLine();
// 如果要替换的行有效且脏,则需要写回到内存
if (DCache[ReplaceLine].Valid == 1 && DCache[ReplaceLine].Dirty == 1)
{
UINT64 OldAddress = DCache[ReplaceLine].Tag << DCACHE_DATA_PER_LINE_ADDR_BITS;
StoreDataCacheLineToMemory(OldAddress, ReplaceLine);
}
// 需要从Memory中读入新的行
LoadDataCacheLineFromMemory(Address, ReplaceLine);
DCache[ReplaceLine].Valid = 1;
DCache[ReplaceLine].Tag = AddressTag;
DCache[ReplaceLine].Dirty = 0;
if (Operation == 'S' || Operation == 'M') // 写操作(修改操作在此等价于写操作)
{
// 写操作需要将新的StoreValue更新到CacheLine中
switch (DataSize)
{
case 1: // 1个字节
DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF;
break;
case 2: // 2个字节
BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界
DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 1] = StoreValue & 0xFF;
break;
case 4: // 4个字节
BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界
DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 3] = StoreValue & 0xFF;
break;
case 8: // 8个字节
BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界
DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 3] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 4] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 5] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 6] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
DCache[ReplaceLine].Data[BlockOffset + 7] = StoreValue & 0xFF;
break;
}
DCache[ReplaceLine].Dirty = 1;
}
// 更新访问时间
UpdateAge(ReplaceLine);
if (Operation == 'L') // 读操作需要返回读取的值
{
ReadValue = 0;
switch (DataSize)
{
case 1: // 1个字节
ReadValue = DCache[ReplaceLine].Data[BlockOffset + 0];
break;
case 2: // 2个字节
BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界
ReadValue = DCache[ReplaceLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 0];
break;
case 4: // 4个字节
BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界
ReadValue = DCache[ReplaceLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 0];
break;
case 8: // 8个字节
BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界
ReadValue = DCache[ReplaceLine].Data[BlockOffset + 7]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 6]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 5]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 4]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 0];
break;
}
*LoadResult = ReadValue;
}
}
return MissFlag;
}
/* 指令Cache实现部分可选实现 */
void InitInstCache(void)
{
return;
}
void LoadInstCacheLineFromMemory(UINT64 Address, UINT32 CacheLineAddress)
{
return;
}
UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize, UINT64* InstResult)
{
// 返回值'M' = Miss'H'=Hit
return 'M';
}