SimpleScalar中分支预测与实例源代码

来源:互联网 发布:linux 日志等级 编辑:程序博客网 时间:2024/05/28 05:18
支持的分支预测类型
l 二级分支预测 :也就是通过移位寄存器(一级)和PHT表(二级),在PHT表中根据2位饱和计数器分析当前分支的跳转情况。
参数:   N —— 一级入口数目
        W —— 移位寄存器的宽度
        M —— 二级入口数目
对于 Gag :三个参数值为别为:1   2^W
    Gap: 三个参数值为别为: 1   M
(只有一个移位寄存器,因此N为1,又因为当只有一个PHT表,因此,Gag中M是2^w,此时,通过移位寄存器,查找PHT表,得到分支情况;而在Gap中,不同的分支有不同的PHT表,因此需要利用分支指令的地址获取多个PHT表中的其中一个,因此M = K*2^w,其中的K应该是设定的PHT表的个数,而且,K应该是2的指数级)
    Pag:三个参数值为别为: N  W  2^W
    Pap:三个参数值为别为: N  W  M
(此时,将同时利用分支指令的地址索引移位寄存器和PHT表,如果是PAG,那么,利用分支指令地址索引多个移位寄存器,获取的值再去索引PHT表,获取跳转情况,如果是PAP,则同时利用分支指令地址索引移位寄存器和PHT表,得到跳转情况)。
  二位饱和计数预测模式
支持的预测方式是当  XX = 0X时不跳转
                 XX = 1X时跳转
                 预测不跳转
                 预测跳转
分支预测的各参数定义
1) 定义了分支预测的类型
enum bpred_class{ }
2) 定义了BTB(分支目的缓冲)表的结构
书中定义的BTB表含有三个部分:分支指令的地址,分支跳转的目的地址,以及预测结果
这儿的结构中定义了四个部分:分支指令的地址;针对当前地址的OP;如果分支跳转,那么跳转的目的地地址;以及形成BTB表的链表结构(双向链表)
3) 定义了二位饱和计数的预测结构,考虑到二级分支预测也使用了二位饱和计数器,因此利用union定义了各部分:
class -说明是二级分支预测还是bimod预测;
然后如果是bimod,那么需要定义一个计数器结构,即:bimod.size和bimod->table;
如果是二级分支预测,需要定义的类型为:寄存器的个数,PHT表的个数,移位寄存器中保存的分支历史长度;把分支历史和分支地址是否XOR的标志;指向历史表的指针和指向PHT表中的指针;
4) 定义了分支预测器:
  定义分支预测的类型,以及指向的相应的预测器元素;
  定义了BTB表的组数,以及BTB组中的相联程度,以及BTB表
  定义了返回栈:返回栈的大小,返回栈的栈顶,而返回栈也是利用了BTB的结构
  同时还定义了多个计数:方向预测的正确性(向前,向后——是否跳转)、地址预测的正确性(不仅是向前向后正确,还要有地址的正确。等等结构。
  这儿不仅预测分支指令,其中还包括了程序间调用的返回等。
5) 定义了预测更新的消息
然后是各函数的声明
1)  创建分支预测器
2)  创建了分支方向预测器
3)  分支预测器配置的输出
4)  分支预测状态的输出
5)  在数据库(sdb)中存储分支预测的信息
6)  在主要的地点存储分支信息
7)  利用分支预测器处理下一条指令
8)  预防因为前瞻执行导致的错误
9)  对分支预测器中若干元素的更新
10) 为了调试导出分支预测器的信息。
各函数的具体说明
1)  创建一个分支预测器
   看这个函数需要先看一下创建分支方向预测器的函数
   根据class,首先创建分支方向预测器(包括二级、bimod等方向预测器)
   然后继续根据分支预测器的类型class,分配返回栈
   首先看一下BTB表的结构,如果BTB表大小为0或者不是2的指数倍,不行。然后根据BTB表的组×相联 分配空间
   接下来把各项BTB结构挂接成为链表
   在分配完BTB之后,分配返回栈结构。而返回栈其实就是一个数组
2)  创建一个分支方向预测器
   首先分配一个结构大小的空间
   然后是说明当前要创建的分支方向预测器的类型(class)
   接下来是根据class按需要给其中的参数赋值:
   如果是二级分支预测:参数有:
    一级的大小(即移位寄存器有几个)
    二级的大小(即PHT表的个数)
    移位寄存器的大小(0<SIZE<=30)
    是否需要分支指令地址与移位寄存器XOR
  根据移位寄存器的大小分配合适的int空间
  根据PHT表的大小分配合适的unsigned char空间
  然后要在PHT表中初始化预测信息,这儿是初始为1-2-1-2的格式
  如果是2位饱和计数器的预测:参数都类似
3)  输出分支方向预测器的配置信息
4)  输出分支预测器的配置信息
5)  以及预测的一些信息
6)  然后是保存sdb中分支预测的信息
7)  关键地区的信息,不重要。
8)  预测一个分支的方向
  首先给的参数的选择的方向预测器的类型class和该分支的指令地址。函数处理过程中,首先让分支指令地址右移2位,与移位寄存器做与 得到当前有效的
   而l2index则是获取当前移位寄存器中的值
反正现在知道l2index是获取2位饱和计数器的值的索引的。
   这儿是考虑了如果有多个移位寄存器时的情况,因为l1size是移位寄存器的个数,不是移位寄存器的宽度。因此如果是Ga*的话,l1index=0。然后,l2index只是获取了如果有多个移位寄存器的话的其中之一。
   如果需要移位寄存器中的数和分支地址做XOR的话(也就是哪个什么很精确的分支预测法),这儿考虑了。
   是直接取得二位饱和计数器中的值,用到了一个BIMOD_HASH的宏,而在有很多2位的饱和计数器时,通过BIMOD_HASH宏获取其中的一个,可以看出,是把多个2位计数器听过HASH链表连接的。
9)  预测下一条指令的地址
注意,这儿是考虑了所有的指令,不仅仅是分支指令,即代码中的获取指令操作码的宏。
在这儿的预测中,首先是预测一下分支的方向,得到pdir1的结果。然后是对分支指令的跳转地址的预测。由于是预测条件分支,也就是说,非条件分支是不预测的。在这儿,先看一下对返回栈的处理。
        先得到返回栈的栈顶。
        然后如果指令的返回指令,即过程调用的return,那么目的地址就是栈里面的地址。
              然后如果指令是函数调用指令,那么则把返回地址值压入栈中
              然后如果两者都不是,就先查找BTB表,由于BTB可能是组相联,因此先找到组数,然后找具体的BTB入口。
如果在BTB中没有找到相应的指令地址,那么根据前面的pdir,分析是不是跳转,即只能给出方向
10)              为了防止分支预测出现错误,和推测执行情况下,程序的问题,在预测的分支处保存当前的返回栈地址
11)              对分支预测的各个参数的更新。
首先是判断是不是分支指令。
如果是分支指令,而且地址也对,那么预测正确数+1,否则是方向+1
这个函数是当分支指令执行完毕之后处理的。但不是很明白其中的stateful 预测是什么意思。针对BTB表的处理是有的更新,没有的话利用LRU替换。 

以下是我写的一个模拟器的代码中的分支预测代码

分支预测头文件.h
//==============================================================================//写文件的人:  wahaha_nescafe//联系方式:    wahaha_nescafe@hotmail.com////系统说明:    这是一个32位微处理器的模拟程序,属于原型,实现的功能有限。//              参考了MIPSR10000、POWPC620处理器的结构,以及体系结构//              中的圣经《计算机体系结构——量化研究方法》。//              实现了指令的4发射、乱序执行。//版权说明:    Copyright (C) 2004-2005 by wahaha_nescafe//              All Rights Not Reserved!//             (没有版权,随便折腾——不能用于商业目的)                                                                               //==============================================================================#ifndef BPRED_H#define BPRED_H//******************************************************************************//整个分支预测的说明//在这儿将实现三种类型的分支预测://two_level:二级分支预测//gshare:gshare分支预测//neural:基于神经网络的分支预测//整个分支预测的分支配置  N, W, M, V// N:分支历史寄存器的个数// W:分支历史寄存器的长度// M:模式表的个数// V:每个模式表的表项数//注意:一般情况下,V = 2^W////对于下列几种情况的配置说明://    GAg (一个分支历史寄存器,一个PHT表)   : 1, W, 1, V //    GAp (一个分支历史寄存器,多个PHT表)   : 1, W, M, V//    PAg (多个分支历史寄存器,一个PHT表)   : N, W, 1, V//    PAp (多个分支历史寄存器,多个PHT表)   : N, W, M, V////CPU利用分支预测的方法://在取指阶段,利用BTB进行分支地址的预测//在译码阶段,进行分支方向的预测//这两种预测算法之间没有必然的联系//如果在译码阶段发现利用BTB预测的是错误的,则译码阶段有最高权利//实现的是两段分支预测方法(模仿了PowPC620)//******************************************************************************#include "config.h"#include "system.h"//******************************************************************************//以下定义分支预测的类型                                                                             enum BPRED_TYPE{BPRED_2LEV = 1,BPRED_GSHARE = 2,BPRED_NEURAL = 3};//******************************************************************************//以下定义BTB的结构                                                                              //先定义BTB表的替换策略enum BPRED_BTB_POLICY{//利用计数器方法实现的LRU替换方法://每一块都设置一个计数器,计数器的操作规则是: // (1) 在调入或者替换出去一个块时, 其计数器清“0”,而其它的计数器则加“1”。 // (2) 当访问命中时,所有块的计数值与命中块的计数值要进行比较,//如果计数值小于命中块的计数值, 则该块的计数值加“1”;//如果块的计数值大于命中块的计数值,则数值不变。最后将命中块的计数器清为0。 // (3) 需要替换时,则选择计数值最大的块被替换。 BTB_LRU = 1,//利用计数器方法实现的FIFO的替换方法://每一块都设置一个计数器,计数器的操作规则是://(1) 在调入一个块时,其计数器清“0”,而其他的计数器加“1”。//  (2) 在访问命中时,不进行操作。//(3) 需要替换时,选择计数器最大的块进行替换。BTB_FIFO = 2,BTB_RANDOM = 3};//******************************************************************************//定义BTB表中的各个表项struct bpred_btb_entry_t {  bool_t valid;//该表项是不是有效    addr_t source_addr;//分支指令的地址  addr_t target_addr;//分支目标指令的地址    counter_t counter;//计数器,主要是用于BTB替换时处理的  struct bpred_btb_entry_t *next; //各个表项的链接指针};//定义BTB表中一个组的结构struct bpred_btb_set_t {struct bpred_btb_entry_t* head;  //形成表项的头指针struct bpred_btb_entry_t* tail;  //形成表项的尾指针,几乎没用};//定义整个BTB表struct bpred_btb_t{struct bpred_btb_set_t* set;//BTB中的组enum BPRED_BTB_POLICY policy;//BTB中的组的块使用的替换策略word_t associative; //BTB的相联度word_t nsets;//BTB的组数};//分支地址预测器的结构struct bpred_addr_t {struct bpred_btb_t* btb;//指向BTB表的指针   counter_t addr_hits;//找到相应地址的次数(找到的不一定是正确的)  counter_t lookups;//查找BTB表的次数(不一定能找到相应的表项) counter_t pred_hits;//预测正确的次数};//******************************************************************************//以下为获取指令地址在BTB中组的宏                                                                              #defineBPRED_GET_BTB_SET(btb, addr)  ( ((addr)>>2) & ((btb->nsets)-1) )//******************************************************************************//以下定义分支模式历史表的结构                                                                              //分支历史寄存器的结构struct bpred_hreg_t{word_t reg;//分支历史寄存器存放分支历史的地方};//分支模式历史表的结构struct bpred_pht_t{byte_t*data;};//分支方向预测器的结构struct bpred_dir_t{enum BPRED_TYPEtype;struct bpred_hreg_t* hreg; //指向分支历史寄存器word_t hreg_num;//分支历史寄存器的个数word_t hreg_width;//分支历史寄存器的长度struct bpred_pht_t* pht;  //指向模式历史表word_t pht_num;//模式历史表的个数word_t pht_entry;//每个模式历史表的入口个数counter_tpred_taken;//预测跳转的次数    counter_tactual_taken;//实际跳转的次数    counter_tpred_ntaken;//预测不跳转的次数    counter_tactual_ntaken;  //实际不跳转的次数};//******************************************************************************//创建分支方向预测器和分支地址预测器                                                                              //创建分支方向预测器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_addr_t* bpred_addr_create(word_t btb_entry_num,  //分支地址预测器中的btb表项的数目word_t btb_associative, //BTB的相联度enum BPRED_BTB_POLICY policy//BTB的替换方法);//******************************************************************************//以下为读取BTB中的某个entry                                                                              //如果该entry正好命中,则读出来,否则为NULLstatic void bpred_read_btb_entry(struct bpred_addr_t*, //分支地址预测器 addr_t addr, //搜寻的指令地址struct bpred_btb_entry_t** entry //表项);//******************************************************************************//以下为在BTB中的某个set中选择将要被替换出来的entry                                                                           static void bpred_replaced_entry( struct bpred_addr_t*, //分支地址预测器 addr_t addr, //搜寻的指令地址struct bpred_btb_entry_t** entry //表项 );//******************************************************************************//以下为进行分支地址的预测                                                                              bool_t bpred_addr_pred(struct bpred_addr_t* bp_addr,//接受的分支方向预测器     addr_t source_addr,//进行分支方向预测的分支指令地址     addr_t* result_addr//分支地址预测的结果     //如果在BTB表中找到了对应的分支指令地址,则说明     //这次分支是要跳转的,然后查看当前的表项是不是有效     //有效就取出预测结果地址,作为下一条PC);  //******************************************************************************//以下为进行分支方向的预测                                                                              void bpred_dir_pred(struct bpred_dir_t* bp_dir,//接受的分支方向预测器     addr_t source_addr,//进行分支方向预测的分支指令地址     long* dir_result//分支方向预测的结果     //对于二级分支预测,结果不外呼:00/01/10/11     //对于神经网络,则有可能是正负数);  //******************************************************************************//以下为分支预测失效后的分支部件的恢复void bpred_update(struct bpred_dir_t* bp_dir,//分支方向预测器struct bpred_addr_t* bp_addr,//分支地址预测器addr_tsource_addr,//该条分支指令的地址addr_tresult_addr//该条分支指令的目的地址);//******************************************************************************//以下为分支预测的初始化                                                                              void bpred_init(void);//******************************************************************************//分支预测的结束处理void bpred_uninit(void);//******************************************************************************//分支预测的统计信息void bpred_statistic(struct bpred_dir_t* bp_dir,struct bpred_addr_t* bp_addr);#endif //BPRED_H

分支预测执行文件.c
//==============================================================================//写文件的人:  wahaha_nescafe//联系方式:    wahaha_nescafe@163.com////系统说明:    这是一个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));//得到组内的entrybp_entry = bp_set->head;ASSERT(bp_entry!=NULL);//在一个组内循环比较地址(如果组比较多,可以使用HASH方法摆放组)for(n=0; n<bp_btb->associative; n++){//发现了就breakif( (bp_entry->source_addr==addr) && (bp_entry->valid==1) )break;elsebp_entry = bp_entry->next;} //如果找到了我们所需要的entryif(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));//得到组内的entrybp_entry = bp_set->head;ASSERT(bp_entry!=NULL);for(n=0; n<bp_btb->associative; n++){if(bp_entry->valid==0)break;elsebp_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都为1if((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;//获取相应的entrybpred_read_btb_entry(bp_addr, source_addr, &entry);//如果找到了指令地址对应的entryif(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类型的要做一个XORif(bp_dir->type == BPRED_GSHARE)tag = source_addr ^ (bp_reg->reg);elsetag = 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_tsource_addr,addr_tresult_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);elsetag = bp_reg->reg;tag = tag & (bp_dir->pht_entry-1);//得到的dir_result 有可能是四个值:00/01/10/11dir_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;elsebp_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;}