1. 程式人生 > >SimpleScalar中分支預測與例項原始碼

SimpleScalar中分支預測與例項原始碼

//==============================================================================
//寫檔案的人:  wahaha_nescafe
//聯絡方式:    [email protected]
//
//系統說明:    這是一個32位微處理器的模擬程式,屬於原型,實現的功能有限。
//              參考了MIPSR10000、POWPC620處理器的結構,以及體系結構
//              中的聖經《計算機體系結構——量化研究方法》。
//              實現了指令的4發射、亂序執行。
//版權說明:    Copyright (C) 2004-2005 by wahaha_nescafe
//              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)作者:
			wahaha_nescafe
*/                                                                                                                                                         
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)作者:
			wahaha_nescafe
*/                                                                                                                                                     
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)作者:
			wahaha_nescafe
*/                                                                                                                                                  
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)作者:
			wahaha_nescafe
*/                                                                                                                                                           
 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)
				{
					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)作者:
			wahaha_nescafe
*/                                                                                                                                                         
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)作者:
			wahaha_nescafe
*/
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)作者:
			wahaha_nescafe
*/
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)作者:
			wahaha_nescafe
*/                                                                                                                                                         
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)作者:
			wahaha_nescafe
*/                
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)作者:
			wahaha_nescafe
*/
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;
}


相關推薦

SimpleScalar分支預測例項原始碼

//============================================================================== //寫檔案的人: wahaha_nescafe //聯絡方式: [email protected] // //系統說明:

Python的類例項屬性

類屬性與例項屬性 在瞭解了類基本的東西之後,下面看一下python中這幾個概念的區別 先來談一下類屬性和例項屬性 在前面的例子中我們接觸到的就是例項屬性(物件屬性),顧名思義,類屬性就是類物件所擁有的屬性,它被所有類物件的例項物件所共有,在記憶體中只存在一個副本,這個和C++,java中

ios開發類方法例項方法區別 以及self有什麼不同

Objective-C裡面既有例項方法也類方法。類方法(Class Method) 有時被稱為工廠方法(Factory Method)或者方便方法(Convenience method)。工廠方法的稱謂明顯和一般意義上的工廠方法不同,從本質上來說,類方法可以獨立於物件而執行,所以在其他的語言裡面類方法有的

python類屬性例項屬性總結

stackoverflow上面的相關討論 1. 類屬性     為在類定義時直接指定的屬性(不是在__init__方法中) class Test: class_attribute1="attr-value"2. 例項屬性     在__init_

Pyhon 類屬性例項屬性的區別

根據網上的一些部落格和自己的理解,說一下自己的想法,某些地方加了一些自己的理解,某些地方直接摘抄他人的,具體可以直接看例子,如有不準確非常希望大家指正,如有表達不好請大家見諒。 情景一: # -*- coding: utf-8 -*- class A(o

Djangourl命名反轉url,應用名稱空間和例項名稱空間

url命名 為什麼需要給url命名? 因為url是經常變化的。如果在程式碼中寫死可能會經常改程式碼。給url取個名字,以後使用url的時候就使用他的名字進行反轉就可以了,就不需要寫死url了。 示例: 在專案中新建一個front的app,views.py中寫入一下程式碼: fro

03 -2 numpypandasisnull()、notnull()、dropna()、fillna()處理丟失資料的理解例項

引入三劍客 import numpy as np import pandas as pd from pandas import Series,DataFrame 處理丟失資料 1.有兩種丟失資料: None: Python自帶的資料型別 不能參與到任何計算中

03 -1 pandas DataFrame理解建立、索引、運算的詳解以及例項

DataFrame DataFrame是一個【表格型】的資料結構,可以看做是【由Series組成的字典】(共用同一個索引)。DataFrame由按一定順序排列的多列資料組成。設計初衷是將Series的使用場景從一維拓展到多維。DataFrame既有行索引,也有列索引。 行索引

Python的類屬性,類方法例項屬性,例項方法

class Person(object): num=10000 def __init__(self,name): self.name=name @classmethod def getNum(cls): return cls.n

JS建構函式普通函式的區別及JS建構函式、原型和例項的關係

JS中建構函式與普通函式的區別: https://www.cnblogs.com/cindy79/p/7245566.html JS建構函式、原型和例項的關係: https://blog.csdn.net/u012443286/article/details/78823955 Java

03 -2 numpypandas處理丟失資料的理解例項

引入三劍客 import numpy as np import pandas as pd from pandas import Series,DataFrame 處理丟失資料 1.有兩種丟失資料: None: Python自帶的資料型別 不能參與到任何計算中

【nginx原始碼】nginx的鎖原子操作

問題引入 多執行緒或者多程序程式訪問同一個變數時,需要加鎖才能實現變數的互斥訪問,否則結果可能是無法預期的,即存在併發問題。解決併發問題通常有兩種方案: 1)加鎖:訪問變數之前加鎖,只有加鎖成功才能訪問變數,訪問變數之後需要釋放鎖;這種通常稱為悲觀鎖,即認為每次

開源處理器Rocket的分支預測機制研究效能評估(二)

3 Rocket處理器分支預測機制分析 3.1 分支預測機制設計分析 Rocket處理器除了實現BTB、BHT,還實現了RAS,都在BTB Module中實現,BTB Module的介面以及與Rocket Core的連線如圖3所示。 Rocket處理器主要在流水線的取

例項理解c++向前宣告引用標頭檔案的區別

使用C++程式設計,編譯時經常出現這種錯誤"error: invalid use of incomplete type ‘class xxx’",或“error: forward declaration of ‘class xxx’”. 解決這種錯誤就是用理解c++中向前宣告與引用標頭檔案的

使用mavenMyEclipse整合ssm(Spring MVC、Spring、Mybatis)三大框架並實現使用者註冊(環境搭載+例項原始碼下載)

相對於ssh(Struts2、Spring、hibernate),ssm(Spring MVC、Spring、mybatis)可能會更受歡迎。本文主要分兩部分:ssm框架的整合、註冊案例的實現 所用到的框架及資料庫對應的版本: 資料庫: MySQL Spring:3.2.

持續交付分支管理版本控制

現在,越來越多的專案使用Git作為版本控制的工具,通過Git來進行分支和Tag管理,但大多數情況這個過程都由手工完成,缺乏相應的規範,對於分支和版本號的控制也很隨意,出現這樣的情況往往是大家對軟體交付過程中的軟體版本控制不夠重視,“只要確保軟體是最新的版本即可”,甚至是專案管

jdk1.8HashSetLinkedHashSet原始碼分析

注:基於JDK 1.8.0_131原始碼為例進行分析: 一、HashSet分析 1.1 HashSet的實現   HashSet實現set介面,是基於HashMap或者LinkedHashMap實現的。   HashSet中封裝了一個 HashM

搭建:canal部署例項執行和解析MysqlBinlog日誌傳送到Kafka

1、準備: github:https://github.com/alibaba/canal 裡面有包括canal的文件,server端 client端的 例子 原始碼包等等。 2、canal概述:

JAVA final 、super 關鍵字以及繼承關係父類子類例項變數初始化的 理解

1,final 修飾符修飾變數、方法、類 時有什麼作用? ①final 修飾變數:該變數被賦初值後,不能對它重新賦值 ②final 修飾方法:此方法不能重寫,即父類中某方法被final修飾,在子類中將不能定義一個與父類final 方法同名且有相同方法識別符號(引數個數也相同

EclipseAndroid原始碼ProGuard工具的使用

由於工作需要,這兩天和同事在研究android下面的ProGuard工具的使用,通過檢視android官網對該工具的介紹以及網路上其它相關資料,再加上自己的親手實踐,算是有了一個基本瞭解。下面將自己的理解和認識簡要的做個筆記,有異議或者不解的,可以直接留言。 什麼是ProGuard工具? ProGua