lab3


目录

init_cache

  • cache结构设计

    • 思路

    1.根据读的要求设置了tag , valid_bit , dirty_bit; 根据回的要求设置了dirty_bit。

    2.根据主存地址划分:

    ( cache有16KB(2^14) , 主存大小有32KB(2^15) ,块大小有2^6 bit , 且是按四路组相联来处理的;那么块内偏移为6位 ,cache行号有2^8行(因为14 - 6 = 8,cache容量处以每个块的大小)。因为是四路组相联,所以cache组数等于cache行号处以每组的行数(4),即2(8-2)=26;那么现在主存地址有15位:tag = 15 -块内偏移长度(6) - cache组号(6)= 3。

    • 代码
    typedef struct
    {
    	bool dirty_bit:1;	//脏位
    	bool data[64];		//数据位:每块大小为2^6,即64字节
    	uint32_t tag:3;		//标记
    	bool valid_bit:1;	//有效位
    }cache_line;
    
    cache_line *Cache;
    
  • cache初始化

    • 思路

    遍历所有的cache行,将每个cache行的有效位和脏位置0;

    • 代码
    void init_cache(int total_size_width, int associativity_width) {
    	int i;
    	int cache_line_num = exp2(total_size_width - associativity_width);
    	Cache = malloc(cache_line_num * sizeof(cache_line ));
    	for(i = 0 ; i < cache_line_num ; i++)
    	{
    		Cache[i].valid_bit = 0;
    		Cache[i].dirty_bit = 0;		
    	} 
    }
    
    

cache_read

  • 思路

分为三个部分:主存地址进来

若命中:

(1)按主存地址先访问cache,如果命中,返回数据;

若不命中:

(2)在cache中找空闲行,即判断valid_bit是否为0,如果为0,则将此cache行作为新的读入的数据存放的地方;

(3)如果该cache组是全部满的,即所有的valid_bit为1,那么随便取一行,此时先判断脏位是否为1,为1的话就将当前的cache行先写回主存,再则将此cache行作为新的读入的数据存放的地方;

  • 代码
uint32_t cache_read(uintptr_t addr) {
	try_increase(1);	//访问cache次数加一
	uint32_t ret_data;
	addr = addr & 0x7FFF;		//主存地址划分
	uint32_t  mem_blocks_num , mem_tag , cache_group_num , block_offset;	
	block_offset = addr & 0x3C;
	cache_group_num = (addr >> 6) & 0x3F;
	mem_tag = (addr >> 12) & 0x7;
	mem_blocks_num = (addr >> 6) & 0x1FF;

	uint32_t paddr;
	int i , j , flag;
	int cache_group_start = cache_group_num * 4;	//情况一:在当前cache中查找
	for(i = cache_group_start ; i < cache_group_start + 4 ; i++)
	{
		if(Cache[i].tag == mem_tag && Cache[i].valid_bit == 1)
		{
			hit_increase(1);
			break;
		}
	}
	if(i < cache_group_start + 4)	//如果命中
	{
		ret_data = Cache[i].data[block_offset] + (Cache[i].data[block_offset + 1] << 8) + (Cache[i].data[block_offset + 2] << 16) + (Cache[i].data[block_offset + 3] << 24);//将该数据返回
	}
	else		//如果不命中,则分为寻找空闲行和随即替换两种情况
	{
		for(j = cache_group_start ; j < cache_group_start + 4 ; j++)//寻找空闲行
		{
			if(Cache[j].valid_bit == 0)
				break;
		}
		if(j < cache_group_start + 4 )	//情况二,不命中但找到空闲行
		{
			mem_read(mem_blocks_num , Cache[j].data);//先将数据读入
			Cache[j].tag = mem_tag;
			Cache[j].valid_bit = 1;
			ret_data = Cache[j].data[block_offset] + (Cache[j].data[block_offset + 1] << 8) + (Cache[j].data[block_offset + 2] << 16) + (Cache[j].data[block_offset + 3] << 24);		
		}
		else	//情况三:不命中且此组cache行全满,考虑随机替换
		{
			flag = rand()%4 + cache_group_start;	//mod4找到一个0~3的任意数
			if(Cache[flag].dirty_bit == 1)		//若脏位为1,先写回
			{
				paddr = ( Cache[flag].tag << 6 ) | cache_group_num ;
				mem_write(paddr , Cache[flag].data);
			}
			mem_read(mem_blocks_num , Cache[flag].data);	//读入主存块
			Cache[flag].tag = mem_tag;
			Cache[flag].valid_bit = 1;
		ret_data = Cache[flag].data[block_offset] + (Cache[flag].data[block_offset + 1] << 8) + (Cache[flag].data[block_offset + 2] << 16) + (Cache[flag].data[block_offset + 3] << 24);	
		}
	}
	return ret_data;
}

cache_write

  • 思路

1.总思路:

分为三个部分:主存地址进来,和读的函数是一样的。

若命中:

(1)按主存地址先访问cache,如果命中,则将掩码对应的偏移处的值修改;

若不命中:

(2)在cache中找空闲行,即判断valid_bit是否为0,如果为0,则将此cache行作为新的读入数据的地方,并将掩码对应的偏移处的值修改;

(3)如果该cache组是全部满的,即所有的valid_bit为1,那么随便取一行,此时先判断脏位是否为1,为1的话就将当前的cache行先写回主存,再则将此cache行作为新的读入的数据存放的地方 , 并将掩码对应的偏移处的值修改;

  • 代码
void cache_alter(int i , uint32_t block_offset , uint32_t wmask , uint32_t data)
{	//写cache行操作
	uint32_t Data;
	Data = Cache[i].data[block_offset] + (Cache[i].data[block_offset + 1] << 8) + (Cache[i].data[block_offset + 2] << 16) + (Cache[i].data[block_offset + 3] << 24); 
	Data = ( Data & (~wmask) ) | (data &wmask);
	Cache[i].data[block_offset+3] = (Data>>24) & 0xff;
	Cache[i].data[block_offset+2] = (Data>>16) & 0xff;
	Cache[i].data[block_offset+1] = (Data>>8) & 0xff;
	Cache[i].data[block_offset] = Data & 0xff;
}

void cache_write(uintptr_t addr, uint32_t data, uint32_t wmask) {
	try_increase(1);
	addr = addr & 0x7FFF;		//划分主存地址
	uint32_t mem_blocks_num , mem_tag , cache_group_num , block_offset;
	block_offset = addr & 0x3C;			
	cache_group_num = (addr >> 6) & 0x3F;
	mem_tag = (addr >> 12) & 0x7;
    mem_blocks_num = (addr >> 6) & 0x1FF;

	uint32_t paddr;

	int i , j ,flag;
	int cache_group_start = cache_group_num * 4;//第一种情况:在cache中寻找
	for(i = cache_group_start ; i < cache_group_start + 4; i++)
	{
		if(Cache[i].tag == mem_tag && Cache[i].valid_bit == 1)	
		{
			hit_increase(1);
			break;
		}
	}
	if(i < cache_group_start + 4)		//如果写命中
	{
		cache_alter(i , block_offset , wmask ,data);	//修改cache行的对应块内偏移的数据
		Cache[i].dirty_bit = 1;
	}
	else	//如果写不命中
	{
		for(j = cache_group_start ; j < cache_group_start + 4 ; j++)//找空闲行
		{
			if(Cache[j].valid_bit == 0)
				break;
		}
		if(j < cache_group_start + 4)  //如果有空闲行
		{
			mem_read(mem_blocks_num , Cache[j].data);//先读入一块新的主存块
			cache_alter(j , block_offset , wmask , data);//修改cache行的对应块内偏移的数据
			Cache[j].tag = mem_tag;	//设置标志位和其它
			Cache[j].valid_bit = 1;
			Cache[j].dirty_bit = 1;
		}
		else	//如果没有空闲行,则采用随机替换
		{
			flag = rand()%4 + cache_group_start;
			if(Cache[flag].dirty_bit == 1)	//如果脏位为1,需先写回主存
			{
				paddr = (Cache[flag].tag << 6 ) | cache_group_num;//拼接写回主存的主存块号
				mem_write(paddr , Cache[flag].data);//写回对应的主存块
			}
			mem_read(mem_blocks_num , Cache[flag].data);		//先读入新的主存块
			cache_alter(flag , block_offset , wmask , data);//修改cache行的对应块内偏移的数据
			Cache[flag].tag = mem_tag;//设置标志位和其它
			Cache[flag].valid_bit = 1;
			Cache[flag].dirty_bit = 1;
		}
	}
}

最终结果截图

image-20210619194230491

备注

posted @ 2021-08-05 10:45  shangjin2001  阅读(210)  评论(0)    收藏  举报