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

📄 bpred.c

📁 这是一个32位微处理器的模拟程序
💻 C
📖 第 1 页 / 共 2 页
字号:
//==============================================================================
//写文件的人:  (sha)fish
//联系方式:    wahaha_nescafe@hotmail.com
//
//系统说明:    这是一个32位微处理器的模拟程序,属于原型,实现的功能有限。
//              参考了MIPSR10000、POWPC620处理器的结构,以及体系结构
//              中的圣经《计算机体系结构——量化研究方法》。
//              实现了指令的4发射、乱序执行。
//版权说明:    Copyright (C) 2004-2005 by (sha)fish
//              All Rights Not Reserved!
//             (没有版权,随便折腾——不能用于商业目的)                                                                               
//==============================================================================

#include <stdio.h>
#include <malloc.h>
#include <math.h>

#include "system.h"
#include "config.h"
#include "bpred.h"

//******************************************************************************
//创建分支方向预测器 
/*
(1)函数名:
			
(2)接收参数:
			type			:	采用什么类型的分支方向预测
			hreg_num		:	使用的分支历史寄存器的个数
			history_width	:	使用的分支历史寄存器的长度
			pht_num			:	PHT表的个数
			pht_entry_num	:	每个PHT表的项数
(3)返回值:
			
(4)函数过程说明:
			分支历史寄存器的最大长度是32位
(5)修改于:
			2005-8-28 0:30
(6)作者:
			(sha)fish
*/                                                                                                                                                         
struct bpred_dir_t* 
bpred_dir_create (	enum BPRED_TYPE type,		//预测的类型
					word_t hreg_num,		//分支历史寄存器的个数
					word_t history_width,//分支历史寄存器的长度
					word_t pht_num,		//PHT表的个数
					word_t pht_entry_num //PHT表入口的个数
					)
{
	struct bpred_dir_t*  bpdir 	= NULL;
	struct bpred_hreg_t* bphreg	= NULL;
	struct bpred_pht_t*  bppht	= NULL;
	byte_t* tdata;
	
	word_t i, j;
	
	//分支历史寄存器的个数是2的幂次
	ASSERT(hreg_num>=1 && ((hreg_num & (hreg_num-1))==0));
	
	//由于是利用一个无符号整数保存分支历史因此分支历史最大32位
	ASSERT(history_width>=0 && history_width<=32);
	
	//PHT表也是一样的道理
	ASSERT( (pht_num>=1) && ((pht_num & (pht_num-1))==0 ) );
	ASSERT( (pht_entry_num>=1) && ((pht_entry_num & (pht_entry_num-1))==0 ) );
	
	
	//给分支方向预测器分配空间
	bpdir = (struct bpred_dir_t*)malloc(sizeof(struct bpred_dir_t));
	if(!bpdir)
		system_error("ERROR When create branch prediction predictor\n");
	
	//得到分支方向预测器的类型	
	bpdir->type = type;
	
	//分支历史寄存器分配空间
	bphreg = (struct bpred_hreg_t*)
				malloc( sizeof(struct bpred_hreg_t) * hreg_num );
	if(!bphreg)
		system_error("ERROR When create branch history register\n");
	
	//初始化分支历史寄存器
	for(i=0; i<hreg_num; i++)
		(bphreg+i)->reg = 0;
	
	bpdir->hreg = bphreg;
	bpdir->hreg_num = hreg_num;
	bpdir->hreg_width = history_width;
	
	//分配分支模式历史表的空间
	bppht = (struct bpred_pht_t*)
				malloc( sizeof(struct bpred_pht_t) * pht_num );
	if(!bppht)
		system_error("ERROR When create pht \n");
		
	//然后要在每一个PHT表中分配存放预测数据的具体的空间
	//先判断类型,再for循环的效率高点
	for(i=0; i<pht_num; i++)
	{
		//如果是神经网络预测
		if(type == BPRED_NEURAL)
		{
			//在这儿可以看出,基于神经网络的权重最大为-128-127
			//使用时要进行无符号数和有符号数之间的转换
			tdata = (byte_t*)
					malloc( sizeof(byte_t) * history_width * pht_entry_num );
			if(!tdata)
				system_error("ERROR When create data\n");
			
			//然后需要初始化其中的数据
			for(j=0; j<(history_width*pht_entry_num); j++)
			{
				(byte_t)(*(tdata+i)) = 0;
			}
		} 
		else
		{
			tdata = (byte_t*)malloc( sizeof(byte_t) * pht_entry_num );
			if(!tdata)
				system_error("ERROR When create data\n");
			
			//然后需要初始化其中的数据
			for(j=0; j<pht_entry_num; j++)
			{
				(byte_t)(*(tdata+i)) = 0;
			}
		}
		(bppht+i)->data = tdata;
	}
	
	bpdir->pht 				= bppht;
	bpdir->pht_num 			= pht_num;
	bpdir->pht_entry 		= pht_entry_num;
	
	bpdir->pred_taken	 	= 0;
	bpdir->actual_taken 	= 0;
	bpdir->pred_ntaken 		= 0;
	bpdir->actual_ntaken 	= 0;
	
	return bpdir;
}

//******************************************************************************
//创建分支地址预测器     
/*
(1)函数名:
			
(2)接收参数:
			btb_entry_num  	: 	BTB表项的数目
			btb_associative	:	BTB的相联度
			policy	        :  BTB的替换方法
(3)返回值:
			
(4)函数过程说明:
			这个BTB表可以使用数组实现的
(5)修改于:
			2005-8-28 0:39
(6)作者:
			(sha)fish
*/                                                                                                                                                     
struct bpred_addr_t* 
bpred_addr_create(	word_t btb_entry_num,  			//BTB表项的数目
					word_t btb_associative,			//BTB的相联度
					enum BPRED_BTB_POLICY policy	//BTB的替换方法
					)
{
	struct bpred_addr_t* 		bpaddr;
	struct bpred_btb_t* 		bpbtb;
	struct bpred_btb_set_t* 	bpset;
	struct bpred_btb_entry_t* 	bpentry;
	struct bpred_btb_entry_t* 	temp_bpentry;
	
	word_t i, j;
	word_t nsets;
	
	//确保他们为2的幂
	ASSERT((btb_entry_num & (btb_entry_num-1))==0);
	ASSERT((btb_associative & (btb_associative-1))==0);
	
	//分配了分支地址预测器的空间
	bpaddr = (struct bpred_addr_t*)malloc( sizeof(struct bpred_addr_t) );
	if(!bpaddr)
		system_error("ERROR When create address prediction \n");
	
	bpaddr->addr_hits 	= 0;
	bpaddr->lookups 	= 0;
	bpaddr->pred_hits 	= 0;
	
	//分配BTB表的空间
	bpbtb = (struct bpred_btb_t*)malloc( sizeof(struct bpred_btb_t) );
	if(!bpbtb)
		system_error("ERROR When create address prediction \n");
	
	bpaddr->btb = bpbtb;
	
	//求出一共有多少组
	nsets = btb_entry_num/btb_associative;
	
	//分配组
	bpset = (struct bpred_btb_set_t*)
				malloc( sizeof(struct bpred_btb_set_t) * nsets );
	if(!bpset)
		system_error("ERROR When create address prediction set\n");
	
	//在组中没有建立连接的机制,访问的话就是+1访问类型的
	//设置BTB表的属性
	bpbtb->set 			= bpset;
	bpbtb->policy 		= policy;
	bpbtb->associative 	= btb_associative;
	bpbtb->nsets 		= nsets;
	
	//在BTB表的每一个组内部进行处理
	for(i=0; i<nsets; i++)
	{
		//每个组内部分配若干个表项
		bpentry = (struct bpred_btb_entry_t*)
				malloc( sizeof(struct bpred_btb_entry_t) * btb_associative );
		if(!bpentry)
			system_error("ERROR When create address prediction set\n");
		
		//各个组的头尾指针指好了
		(bpset+i)->head = bpentry;
		(bpset+i)->tail = bpentry + (btb_associative-1);
		(bpset+i)->tail->next = NULL;
		
		//组中的各个块赋值
		for(j=0; j<btb_associative; j++)
		{
			(bpentry+j)->valid 			= 0;
			(bpentry+j)->source_addr 	= 0;
			(bpentry+j)->target_addr 	= 0;
			(bpentry+j)->counter 		= 0;
		}
		
		//组中各个块连接成链表结构
		j = 0;
		while( j<(btb_associative-1) )
		{
			temp_bpentry 	= bpentry+1;
			bpentry->next 	= temp_bpentry;
			bpentry 		= temp_bpentry;
			j++;
		}	
	}
	
	return bpaddr;
}

//******************************************************************************
//以下为读取BTB中的某个entry  
/*
(1)函数名:
			
(2)接收参数:
			bp_addr		:	分支地址预测器
			addr		:	分支指令地址,即利用这个地址查找BTB表
			entry		:	寻找到的相应的块
(3)返回值:
			
(4)函数过程说明:
			如果在BTB中找到addr地址对应的项,就返回相应的entry
			得到的entry存放在传入的参数“entry”中
			如果没有找到,就返回NULL
			因此,需要entry参数在传进来的时候为NULL,这样才能进行判断
(5)修改于:
			2005-8-28 10:05
(6)作者:
			(sha)fish
*/                                                                                                                                                  
void 
bpred_read_btb_entry(	struct bpred_addr_t* bp_addr,	
						addr_t addr, 				
						struct bpred_btb_entry_t** entry 
					)
{
	byte_t n;
	struct bpred_btb_t* 		bp_btb;
	struct bpred_btb_set_t* 	bp_set;
	struct bpred_btb_entry_t* 	bp_entry;
	struct bpred_btb_entry_t* 	bp_entry2;
	
	ASSERT(bp_addr!=NULL);
	ASSERT((*entry)==NULL);
	
	bp_addr->lookups++;
	
	//得到BTB表
	bp_btb = bp_addr->btb;
	
	//得到BTB表内的某一个组
	bp_set = (struct bpred_btb_set_t*)
				(bp_btb->set + BPRED_GET_BTB_SET(bp_btb, addr));
	
	//得到组内的entry
	bp_entry = bp_set->head;
	
	ASSERT(bp_entry!=NULL);
	
	//在一个组内循环比较地址(如果组比较多,可以使用HASH方法摆放组)
	for(n=0; n<bp_btb->associative; n++)
	{
		//发现了就break
		if( (bp_entry->source_addr==addr) && (bp_entry->valid==1) )
			break;
		else
			bp_entry = bp_entry->next;
	} 		
	
	//如果找到了我们所需要的entry
	if(bp_entry)
	{
		bp_addr->addr_hits++;

		//在一个组进行替换算法的处理
		if(bp_btb->policy==BTB_LRU)
		{
			bp_entry2 = bp_set->head;
			for(n=0; n<bp_btb->associative; n++)
			{
				//如果其他的计数器的值比较小,则需要加“1”
				if( 	(bp_entry2->valid==1)
				 	 &&	(bp_entry2->counter<bp_entry->counter) )
				{
				  	bp_entry2->counter++;
				}
				  
				bp_entry2 = bp_entry2->next;
			}
			//找到的这个块的计数器设置为“0”
			bp_entry->counter = 0;
		}
				
		//然后返回找到的块
		*entry = bp_entry;				
	}
	//如果没有找到块
	else
	{
		*entry = NULL;
	}
	return;
}

//******************************************************************************
//以下为在BTB中的某个set中选择将要被替换出来的entry  
/*
(1)函数名:
			
(2)接收参数:
			bp_addr	:	分支地址预测器
			addr	:	分支指令地址
			entry	:	寻找到的相应的块
(3)返回值:
			
(4)函数过程说明:
			这个函数是在更新BTB表的时候使用的,在取指令周期,进行分支地址预测时,
			不会使用这个函数。而更新BTB表则是在第四个周期即完成周期进行的
			利用完成周期时处理的的分支指令地址,
			此时的结果不可能为NULL
(5)修改于:
			2005-9-6 11:06
(6)作者:
			(sha)fish
*/                                                                                                                                                           
 void 
 bpred_replaced_entry(	struct bpred_addr_t* bp_addr,	
						addr_t addr, 				
						struct bpred_btb_entry_t** entry 
					)
{
	byte_t n;
	byte_t nx;
	long k;
	long max;
	struct bpred_btb_t* bp_btb;
	struct bpred_btb_set_t* bp_set;
	struct bpred_btb_entry_t* bp_entry;
	struct bpred_btb_entry_t* bp_entry2;
	
	ASSERT(bp_addr!=NULL);
	ASSERT((*entry)==NULL);
	
	bp_addr->lookups++;
	
	//得到BTB表
	bp_btb = bp_addr->btb;
	
	//得到BTB表内的某一个组
	bp_set = (bp_btb->set + BPRED_GET_BTB_SET(bp_btb, addr));
	
	//得到组内的entry
	bp_entry = bp_set->head;
	ASSERT(bp_entry!=NULL);
	
	for(n=0; n<bp_btb->associative; n++)
	{
		if(bp_entry->valid==0)
			break;
		else
			bp_entry = bp_entry->next;
	}
	
	//如果有空位置
	if(bp_entry)
	{
		if(	(bp_btb->policy==BTB_LRU)||(bp_btb->policy==BTB_FIFO) )
		{
			bp_entry2 = bp_set->head;
			
			ASSERT(bp_entry2!=NULL);
			
			//因为要新调进来块
			//因此对其他块来说,计数器的值要变化
			for(n=0; n<bp_btb->associative; n++)
			{
				if(bp_entry2->valid==1)
				{
					bp_entry2->counter++;
				}
				bp_entry2 = bp_entry2->next;
			}
			ASSERT(bp_entry2==NULL);
			bp_entry->counter = 0;			
		}		
		*entry = bp_entry;
	}
	//如果没有空位置
	else if(bp_entry==NULL)
	{
		if(bp_btb->policy==BTB_RANDOM)
		{
			k = random(bp_btb->associative);
			
			//得到将要被替换出去的块
			bp_entry = bp_set->head + k;
		}
		else if((bp_btb->policy==BTB_LRU)||(bp_btb->policy==BTB_FIFO) )
		{
			//寻找计数器最大的一个块准备替换
			bp_entry = bp_set->head;
			max = bp_entry->counter;
			nx = 0;
			for(n=1; n<bp_btb->associative; n++)
			{
				bp_entry2 = bp_entry+1;
				
				//此时可以不判断valid的值,因为这儿的前提是
				//没有空位置,也就是说valid都为1
				if((counter_t)max < bp_entry2->counter)
				{	
					max = bp_entry2->counter;
					nx = n;
				}
				bp_entry = bp_entry2;
			}
			//得到计数器最大的一个块
			bp_entry = bp_set->head+nx;
			ASSERT(bp_entry!=NULL);
			
			//现在得到了计数器最大的一个块
			//然后把这个块的计数器值置为0,其他块的计数器值加“1”
			bp_entry2 = bp_set->head;
			for(n=0; n<bp_btb->associative; n++)
			{
				if(n!=nx)
				{

⌨️ 快捷键说明

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