主要内容
- 全映射工作原理及工作过程
- Cache仿真设计
- 具体实现
- 测试用例
- 完整源码
全相联映射的工作原理
- 主存分块,Cache分行(Line),两者大小相同;
- 设每块4个字,主存大小为1024个字,则第61个字的主存地址为:
00001111 01 (块号 块内地址)
- 主存分块后地址从一位转为二维,即从本来的第几个字变为第几块的第几个字
- 映射算法:主存的数据块可映射到Cache任意行,同时将该数据块地址对应的标记存储体中保存
工作过程
- 将CPU给出的地址剥离出标记部分和块内偏移量
- 利用多路比较电路将从地址中剥离出的标记部分与Cache中每一行的标记部分进行比较
- 如果命中,利用块内偏移地址从命中的行中取出具体数据
- 如果没有命中,利用CPU给出的原始地址直接访问主存
- 在第4步的基础上,将原始地址所指的块的内容搬迁到Cache中的某一行(任意一行,如果cache中每行都有数据,则根据相应的替换策略选择替换掉哪一行)
- 在第5步基础上,将从原始地址中剥离出的标志填入cache行中的标志,将有效位置1
设计思路
- 映射方法采用全相联映射
- 替换策略采用随机替换
- 多路比较电路通过比较算法代替
- 通过三个类分别模拟主存储器、cache、地址
具体实现
- 设计内存分块所需的块结构体
struct Lump { char data[LUMP_SIZE]; //块内数据 };
- 设计用于模拟主存储器的类FuMemo,主要包括私有成员一个一维char型数组用于模拟1024字节储存、一个Lump型数组模拟分块以及需要的接口
class FuMemo { public: FuMemo(); void writeData(); //写入数据 void divideLump(); //主存分块 char getData(Address address); //cpu直接访问主存接口 char getDataArea(Address address); private: char data[SIZE]; //主存数据 Lump lump[SIZE/LUMP_SIZE]; //块 Area area[SIZE /AREA_SIZE]; //根据cache行数对主存进行分区 };
- 设计模拟Cache所需的行结构体Line
struct Line { bool valid; //有效位 int tag[8]; //标志位 char data[LINE_SIZE]; //块数据 };
- 设计用于模拟Cache的类Cache,主要包括Line型数组,以及映射策略选择(为以后增加其它映射策略预留)
class Cache { public: Cache(); ~Cache(); void accessContrl(char addr[3],FuMemo memory); //cpu访问cache接口 bool compare(int ta[8]); //比较cpu传入地址的tag是否在cache行 char getData(Address address); //从cache中取一个字数据 void moveDate(FuMemo memory,Address address); //从主存中搬一块数据到cache的一行 void setPolice(int po); //设置映射策略 int getPolice(); private: Line line[CACHE_SIZE]; //cache行 int police; //映射策略 };
- 设计地址类用于存放从CPU给出的标准地址中分离出来的各部分、注意实现拷贝构造函数
class Address { public: int tag[8]; //标志位 int lu_ad[2]; //块内地址 Address() { } Address(const Address &add) { for (int i = 0; i < 8; i++) { tag[i] = add.tag[i]; } for (int i = 0; i < 2; i++) { lu_ad[i] = add.lu_ad[i]; } for (int i = 0; i < 2; i++) { area[i] = add.area[i]; } } private: };
- Cache类中提供给CPU访问Cache的接口void accessContrl(char addr[3],FuMemo memory)的具体实现
void Cache::accessContrl(char addr[3], FuMemo memory) { //cpu访问cache时传入16进制地址 char data; Address address; //全相联映射 address = addreAna(addr); //解析地址 if (compare(address.tag)) { //命中 cout << "命中cache" << endl; data = this->getData(address); //从cache中取数据 } else { //没有命中,cpu访问主存 cout << "未命中cache" << endl; data = memory.getData(address); //访问主存 moveDate(memory, address);//将主存中某一块数据搬至cache并填写相应标志位 } cout << "data is " << data << endl; }
- 利用addreAna(char *)对CPU传入的16进制地址进行解析得到Address型地址
Address addreAna(char addr[3]) { //地址解析,将8位16进制地址解析为二进制tag和lu_ad即块号和块内地址 //返回Address型地址 Address address; int bin[12] = { 0 }; //转二进制 hexToBin(addr, bin); for (int i = 2; i < 10; i++) { address.tag[i-2] = bin[i]; } for (int i = 10; i < 12; i++) { address.lu_ad[i - 10] = bin[i]; } return address; }
- 利用compareArea(Address address)对要读取的地址的标志(tag)与cache中每一行进行比较,判断是否命中
bool Cache::compare(int ta[8]) { //将cpu地址标记位传入,与cache行进行比较,命中返回true,不命中返回false for (int i = 0; i < CACHE_SIZE; i++) { if (comp(ta, this->line[i].tag)) return true; } return false; }
- 命中:利用getData(Address address)从cache中取出要取的数据
char Cache::getData(Address address) { //根据地址从Cache中取数据 char data; for (int i = 0; i < SIZE; i++) { if (comp(this->line[i].tag, address.tag)) { data = this->line[i].data[binToDec_2_(address.lu_ad)]; return data; } } }
- 没有命中:利用Memory类的接口getData(address)直接从主存中访问数据,然后利用moveDate(FuMemo memory,Address address)将主存中的数据搬迁到cache中对应的行。
char FuMemo::getData(Address address) { //cache未命中,cpu直接访问主存获取数据 //int lu_no = address.tag return this->lump[binToDec_8(address.tag)].data[binToDec_2(address.lu_ad)]; }
void Cache::moveDate(FuMemo memory,Address address) { //根据相应策略将主存中数据搬至cache并填写相应标志位 int i; srand((unsigned)(time(NULL))); int bre = 0; do { bre++; i = rand() % CACHE_SIZE; //随机放入行 }while(this->line[i].valid&&bre == CACHE_SIZE); cout << "将主存中数据放入cache哪一行:" << i << endl; for (int j = 0; j < LINE_SIZE; j++) { switch (j) { case 0:address.lu_ad[0] = 0; address.lu_ad[1] = 0; break; case 1:address.lu_ad[0] = 0; address.lu_ad[1] = 1; break; case 2:address.lu_ad[0] = 1; address.lu_ad[1] = 0; break; case 3:address.lu_ad[0] = 1; address.lu_ad[1] = 1; break; default: break; } this->line[i].data[j] = memory.getData(address); } this->line[i].valid = true; for (int j = 0; j < 8; j++) { this->line[i].tag[j] = address.tag[j]; } }
- 利用addreAna(char *)对CPU传入的16进制地址进行解析得到Address型地址
5、输出data
测试
- 使用for循环对主存储器中写入数据,奇数位写J偶数位写L
- 输入三位十六进制地址001、000、04E、04F分别测试命中与不命中的情况
写在最后:
- 本篇只实现了全相联策略,并且只有读取数据
- 为方便替换策略采用的随即策略,需要改进
- 文章名字有欺世盗名之嫌
- 代码我也需要交,可以借鉴请勿直接复制
- 能力有限,不足之处敬请指出。
点击获取完整源码 下载时顺手点个star吧,hahahahaha