⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bpred.c

📁 这是一个32位微处理器的模拟程序
💻 C
📖 第 1 页 / 共 2 页
字号:
					bp_entry2->counter++;
				}
				bp_entry2 = bp_entry2->next;
			}
			bp_entry->counter = 0;	
		}
		*entry = bp_entry;
	}	
	
	ASSERT(*entry != NULL);
	return ;	
}

//******************************************************************************
//进行分支地址的预测 
/*
(1)函数名:
			
(2)接收参数:
			bp_addr		:	分支地址预测器
			source_addr	:	源地址,分支指令地址
			result_addr	:	目标地址,即预测的分支跳转地址
(3)返回值:
			void
(4)函数过程说明:
			进行分支地址的预测,这个是在取指令周期调用的
			如果在BTB中找到对应的指令地址,则得到对应的目标地址
			如果没有找到,返回的目标地址是0xffffffff
			这个处理方法不是很好!
			2005-9-20 11:21
			今天修改,得到返回值,即:没有在BTB中找到目的地址,将返回FALSE
(5)修改于:
			2005-8-28 10:35
(6)作者:
			(sha)fish
*/                                                                                                                                                         
bool_t 
bpred_addr_pred(	struct bpred_addr_t* bp_addr,	
	     			addr_t source_addr,				
	     			addr_t* result_addr				
	     		)
{
	struct bpred_btb_entry_t* entry = NULL;
	bool_t result = 0;
	
	//获取相应的entry
	bpred_read_btb_entry(bp_addr, source_addr, &entry);
	
	//如果找到了指令地址对应的entry
	if(entry)
	{
		result = 1;
		*result_addr = entry->target_addr;
	}
	else
	{
		result = 0;
		*result_addr = 0xffffffff;
	}
	
	return result;
}  


//******************************************************************************
//进行分支方向的预测                                                                                                                                                          
/*
(1)函数名:
			
(2)接收参数:		
			bp_dir		:	分支方向预测器
			source_addr	:	分支指令地址
			dir_result	:	方向预测结果
(3)返回值:
			
(4)函数过程说明:
			这个是在第二个周期即译码和发射周期调用的
			根据分支指令的地址,查找PHT表进行预测,得到预测结果
			对于2lev和gshare,预测的结果是00\01\10\11
			对于neural,预测的结果则有可能是正数或负数
			最后在得到结果后,要进行处理:跳转为1;不跳转为-1
			在最后,还要处理,即跳转为“1”,不跳转为“-1”
(5)修改于:
			2005-8-28 13:27
(6)作者:
			(sha)fish
*/
void 
bpred_dir_pred(	struct bpred_dir_t* bp_dir,		
				addr_t source_addr,				
	     		long* dir_result					
			)
{
	struct bpred_hreg_t* 	bp_reg;
	struct bpred_pht_t* 	bp_pht;
	word_t tag;
	byte_t* data;
	word_t reg;
	
	//首先根据指令地址选择某一个分支历史寄存器
	bp_reg = bp_dir->hreg + ((source_addr>>2)&(bp_dir->hreg_num-1));
	
	//然后选择模式历史表
	bp_pht = bp_dir->pht + ((source_addr>>2)&(bp_dir->pht_num-1));
	
	switch(bp_dir->type)
	{
		//一起处理了啊,简单点
		case BPRED_2LEV:
		case BPRED_GSHARE:
		{	
			//根据分支历史寄存器中的值选取模式历史表的一个二位饱和计数器
			//首先得到分支历史寄存器中的值,gshare类型的要做一个XOR
			if(bp_dir->type == BPRED_GSHARE)
				tag = source_addr ^ (bp_reg->reg);
			else
				tag = bp_reg->reg;
			//然后选择其中的某个二位饱和计数器
			tag = tag & (bp_dir->pht_entry-1);
			*((byte_t*)dir_result) = *(bp_pht->data + tag);
			
			//最后进行转化
			if( (*dir_result) >=2 )
				*dir_result = 1;
			else
				*dir_result = 0;
			break;
		}
		case BPRED_NEURAL:
		{	
			//此时需要利用指令地址选择PHT表中的某一个向量
			tag = (bp_dir->hreg_width)*((source_addr>>2)&(bp_dir->pht_entry-1));
			data = bp_pht->data + tag;
			
			*dir_result = 0;
			reg = bp_reg->reg;
			for(tag=0; (word_t)tag<bp_dir->hreg_width; tag++)
			{
				//这儿需要做正负数的变换
				if((reg&1)==1)
					*dir_result += (signed long) (*(data+tag));
				else
					*dir_result -= (signed long) (*(data+tag));
					
				reg = reg>>1;
			}
			if( (*dir_result) >= 0)
				*dir_result = 1;
			else 
				*dir_result = 0;
			break;
		}
		default:
		{
			ASSERT(-1);
			break;
		}
	}
	
	if(*dir_result == 1)
	{
		bp_dir->pred_taken++;
	}
	else if(*dir_result == 0)
	{
		bp_dir->pred_ntaken++;
	}
	return;
}

//******************************************************************************
//分支预测的更新工作
/*
(1)函数名:
			
(2)接收参数:
			bp_dir			:	分支方向预测器
			bp_addr			:	分支地址预测器
			source_addr		:	正在更新的分支指令的地址
			result_addr		:	正在更新的分支指令的目的地址	
(3)返回值:
			void
(4)函数过程说明:
			分支预测的更新工作包括更新二个部分:
			(1)更新BTB表
				首先在BTB表中寻找是不是有这条分支指令对应的项
				※如果有,则利用这个新的跳转地址更新这个项
				※如果没有,则找一个地方,安排这条分支指令住下来
			(2)更新PHT表以及分支历史寄存器
				首先根据分支指令地址和分支历史寄存器找到PHT表的表项,
				然后更新分支历史寄存器,以及PHT表中的内容
(5)修改于:
			2005-9-1 21:44
(6)作者:
			(sha)fish
*/
void 
bpred_update(	struct bpred_dir_t* bp_dir,		
				struct bpred_addr_t* bp_addr,	
				addr_t	source_addr,			
				addr_t	result_addr				
			)
{
	struct bpred_btb_entry_t* btb_entry = NULL;
	struct bpred_hreg_t* 	bp_reg = NULL;
	struct bpred_pht_t* 	bp_pht = NULL;
	word_t tag;
	bool_t branch_taken;
	long dir_result;
	byte_t* data = NULL;
	unsigned long reg = 0;
	ASSERT( (bp_dir!=NULL) && (bp_addr!=NULL) );
	
	//先看看这条分支指令有没有跳转成功
	if( (source_addr+4) == result_addr )
	{
		branch_taken = 0;
		bp_dir->actual_ntaken++;
	}
	else 
	{
		branch_taken = 1;
		bp_dir->actual_taken++;
	}
		
	//更新BTB表,首先得到BTB中被替换出去的块,
	bpred_replaced_entry( bp_addr, source_addr, &btb_entry );
	btb_entry->valid = 1;
	//进行信息统计
	if( 	(btb_entry->source_addr == source_addr)
		&&	(btb_entry->target_addr == result_addr)
			)
	{
		bp_addr->pred_hits++;
	}
	//然后在这个entry中写入分支指令地址和跳转地址
	else
	{
		btb_entry->source_addr = source_addr;
		btb_entry->target_addr = result_addr;
	}
	
	//之后是更新分支历史寄存器和PHT表
	//首先根据指令地址选择某一个分支历史寄存器
	bp_reg = bp_dir->hreg + ((source_addr>>2)&(bp_dir->hreg_num-1));
	//然后选择模式历史表
	bp_pht = bp_dir->pht + ((source_addr>>2)&(bp_dir->pht_num-1));
	
	switch(bp_dir->type)
	{
		//一起处理了啊,简单点
		case BPRED_2LEV:
		case BPRED_GSHARE:
		{	
			//根据分支历史寄存器中的值选取模式历史表的一个二位饱和计数器
			if(bp_dir->type == BPRED_GSHARE)
				tag = source_addr ^ (bp_reg->reg);
			else
				tag = bp_reg->reg;
			
			tag = tag & (bp_dir->pht_entry-1);
			
			//得到的dir_result 有可能是四个值:00/01/10/11
			dir_result = *(bp_pht->data + tag);
			
			//如果跳转
			if(branch_taken == 1)
			{
				if(dir_result<=2)
					*(bp_pht->data + tag) = dir_result + 1;
			}
			else
			{
				if(dir_result>0)
					*(bp_pht->data + tag) = dir_result - 1;
			}
			break;
		}
		case BPRED_NEURAL:
		{	
			//此时需要利用指令地址选择PHT表中的某一个向量
			tag = (bp_dir->hreg_width)*((source_addr>>2)&(bp_dir->pht_entry-1));
			data = bp_pht->data + tag;
			
			dir_result = 0;
			reg = bp_reg->reg;
			for(tag=0; (unsigned long)tag<bp_dir->hreg_width; tag++)
			{
				if((reg&1)==1)	//即原来的跳转
				{
					if(branch_taken==1)
					 (*(data+tag)) 
					= (((signed long) (*(data+tag))+1)>BPRED_NEURAL_MAX_EDGE)? 
				((signed long) (*(data+tag))) : ((signed long) (*(data+tag))+1) ;
					else if(branch_taken==0)
					 (*(data+tag)) 
					= (((signed long) (*(data+tag))-1)<BPRED_NEURAL_MIN_EDGE)? 
				((signed long) (*(data+tag))) : ((signed long) (*(data+tag))-1) ;
				}
				else if((reg&1)==0)
				{
					if(branch_taken==0)
					 (*(data+tag)) 
					= (((signed long) (*(data+tag))+1)>BPRED_NEURAL_MAX_EDGE)? 
				((signed long) (*(data+tag))) : ((signed long) (*(data+tag))+1) ;
					else if(branch_taken==1)
						 (*(data+tag)) 
					= (((signed long) (*(data+tag))-1)<BPRED_NEURAL_MIN_EDGE)? 
				((signed long) (*(data+tag))) : ((signed long) (*(data+tag))-1) ;
				}
					
					
				reg = reg>>1;
			}
			break;
		}
		default:
		{
			ASSERT(-1);
			break;
		}
	} //switch branch type
	
	//然后就是更新分支历史寄存器
	if(branch_taken==1)
		bp_reg->reg = (bp_reg->reg<<1)+1;
	else
		bp_reg->reg = (bp_reg->reg<<1);
	
	return;
}

//******************************************************************************
//分支预测的初始化 
/*
(1)函数名:
			
(2)接收参数:
			void
(3)返回值:
			void
(4)函数过程说明:
			没有特殊的过程,初始化系统中的分支地址预测和分支方向预测
(5)修改于:
			2005-9-20 11:36
(6)作者:
			(sha)fish
*/                                                                                                                                                         
void bpred_init(void)
{

	printf("%s\n", "branch prediction init>>>>>>>>>>");

	SYS_bpred_addr = bpred_addr_create(	BPRED_BTB_ENTRY,
									BPRED_BTB_ASSOCIATIVE,
									BPRED_BTB_REPLACEMENT
								);	
	SYS_bpred_dir = bpred_dir_create(	BPRED_PREDICTION_TYPE,
									BPRED_HISTORY_NUM,
									BPRED_HISTORY_WIDTH,
									BPRED_PHT_NUM,
									BPRED_PHT_ENTRY
								);
	return;
}

//******************************************************************************
//分支处理的结束
/*
(1)函数名:
			
(2)接收参数:
			void
(3)返回值:
			void
(4)函数过程说明:
			没有特殊的过程,释放系统中的空间
(5)修改于:
			2005-9-20 11:36
(6)作者:
			(sha)fish
*/                
void bpred_uninit(void)
{
	struct bpred_btb_set_t* btb_set 	= NULL;
	struct bpred_btb_set_t* btb_set2	= NULL;
	struct bpred_btb_entry_t* btb_entry 	= NULL;
	struct bpred_btb_entry_t* btb_entry2 	= NULL;
	unsigned long set	= 0;
	unsigned long entry	= 0;
	unsigned long k = 0;
	struct bpred_pht_t*	pht 	= NULL;
	
	ASSERT(SYS_bpred_addr);
	ASSERT(SYS_bpred_dir);
	
	//释放分支地址预测
	btb_set = SYS_bpred_addr->btb->set;
	for(set=1; set<=SYS_bpred_addr->btb->nsets; set++)
	{
		btb_set2 = btb_set + 1;
		btb_entry = btb_set->head;
		for(entry=1; entry<=SYS_bpred_addr->btb->associative; entry++)
		{
			btb_entry2 = btb_entry->next;
			free(btb_entry);
			btb_entry = btb_entry2;
		}	
		btb_set = btb_set2;
	}
	
	free(SYS_bpred_addr->btb->set);
	free(SYS_bpred_addr->btb);
	free(SYS_bpred_addr);
	
	//释放分支方向预测
	pht = SYS_bpred_dir->pht;
	for(k=1; k<=SYS_bpred_dir->pht_num; k++ , pht+=1)
		free(pht->data);
	
	free(SYS_bpred_dir->pht);
	free(SYS_bpred_dir->hreg);
	
	free(SYS_bpred_dir);
	
	return;
}

//******************************************************************************
//统计系统中的信息
/*
(1)函数名:
			
(2)接收参数:
			bp_dir	:	系统中的分支方向预测器
			bp_addr	:	系统中的分支地址预测器
(3)返回值:
			void
(4)函数过程说明:
			统计一下信息而已,主要是统计命中次数和预测正确率
			当然,也可以统计其他信息,暂时没想到呢
(5)修改于:
			2005-9-20 11:42
(6)作者:
			(sha)fish
*/
void bpred_statistic(	struct bpred_dir_t* bp_dir,
						struct bpred_addr_t* bp_addr
					)
{
	ASSERT(bp_dir && bp_addr);
	printf("\n%s\n","BRANCH PREDICTION STATISTIC:");
	printf("%s\n","branch address prediction:");
	printf("  %s	: %d\n","look up times ", bp_addr->lookups);
	printf("  %s	: %d\n","addr hit times", bp_addr->addr_hits);
	printf("  %s	: %d\n","pred hit times", bp_addr->pred_hits);
	printf("%s\n","branch direction prediction:");
	printf("  %s	: %d\n","pred taken times   ", bp_dir->pred_taken);
	printf("  %s	: %d\n","atcual taken times ", bp_dir->actual_taken);
	printf("========END========\n");
	
	return;
}


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -