C/C++实现Cache仿真器

本文详细介绍了一种基于全相联映射策略的Cache仿真设计,包括其工作原理、实现流程与测试案例。设计中采用了随机替换策略,通过具体代码展示了如何实现地址解析、数据比较与迁移等功能。

主要内容

  • 全映射工作原理及工作过程
  • Cache仿真设计
  • 具体实现
  • 测试用例
  • 完整源码

 

全相联映射的工作原理

  • 主存分块,Cache分行(Line),两者大小相同;
  • 设每块4个字,主存大小为1024个字,则第61个字的主存地址为:

            00001111    01    (块号    块内地址)

  • 主存分块后地址从一位转为二维,即从本来的第几个字变为第几块的第几个字
  • 映射算法:主存的数据块可映射到Cache任意行,同时将该数据块地址对应的标记存储体中保存

工作过程

  1.  将CPU给出的地址剥离出标记部分和块内偏移量
  2. 利用多路比较电路将从地址中剥离出的标记部分与Cache中每一行的标记部分进行比较
  3. 如果命中,利用块内偏移地址从命中的行中取出具体数据
  4. 如果没有命中,利用CPU给出的原始地址直接访问主存
  5. 在第4步的基础上,将原始地址所指的块的内容搬迁到Cache中的某一行(任意一行,如果cache中每行都有数据,则根据相应的替换策略选择替换掉哪一行)
  6. 在第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;
    }
    1. 利用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;
      }

       

    2. 利用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;
      }

       

    3. 命中:利用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;
      		}
      	}
      }

       

    4. 没有命中:利用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];
      	}
      
      }
      

       

                 5、输出data

 测试

  • 使用for循环对主存储器中写入数据,奇数位写J偶数位写L
  • 输入三位十六进制地址001、000、04E、04F分别测试命中与不命中的情况

     

写在最后:

  • 本篇只实现了全相联策略,并且只有读取数据
  • 为方便替换策略采用的随即策略,需要改进
  • 文章名字有欺世盗名之嫌
  • 代码我也需要交,可以借鉴请勿直接复制
  • 能力有限,不足之处敬请指出。

点击获取完整源码 下载时顺手点个star吧,hahahahaha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值