OpenRisc-43-or1200的IF模块分析
引言
“喂饱饥饿的CPU”,是计算机体系结构设计者时刻要考虑的问题。要解决这个问题,方法大体可分为两部分,第一就是利用principle of locality而引进的cache技术,缩短取指时间,第二就是采用各种转移预测技术,提高取指正确的概率。
只有cache,没有好的转移预测,取指时间再短,每次提前取的指令都是错的,不行。
只有转移预测,没有cache,即使每次取得指令都对,但是每次取指都需要很长时间,也不行。
所以,只有两部分通力合作,才能喂饱饥饿的ALU。
“吃的是草,挤出来的是奶”,赞颂的是牛的奉献精神,但是要想让牛奉献更多,没有足够的草也不行啊。
之前我们分析了or1200的cache和MMU模块,了解了如何缩短取指时间,本小节就来介绍一下or1200的IF(取指)模块。
1,转移预测
咱们都知道,cache的本质原理是局部性原理,但是上天是公平的,上帝在赐给程序具有局部性特性的同时,还赐给了局部性的克星,这就是分支指令(控制相关指令)。控制相关指令的转移地址是会破坏局部性的,如果一个程序中的分支指令很多,而又没有好的转移预测技术,那么在遇到转移指令之后,如果事先取的指令不是最终的跳转入口指令,那么就需要刷新整个流水线,这样,CPU的整体性能就会受到很大影响。所以,为了减少犯错几率,就需要研究各种转移预测技术。
大体来说,解决控制相关指令的方式可分为两个方面,软件,硬件。
软件解决控制相关的技术主要有循环展开,软流水,条件指令等。
循环展开的核心思想是将一个多级循环进行处理,首先将循环间相关变成循环内相关,然后通过编译器中的循环展开技术消除循环内相关,这其中会用到寄存器重命名技术(register rename)。
软流水的核心思想是将循环最小单元进行扩大,消除相关性。比如把一个100次的小循环,变成20次的大循环。软流水技术就相当于软件tomasulo算法。
关于tomasulo算法,请参考:http://en.wikipedia.org/wiki/Tomasulo_algorithm
条件指令的核心思想是在执行指令的同时判断转移条件,如果条件为真,则将指令加到流水线执行,如果条件为假,则空一个clock(相当于NOP指令)。
硬件转移预测技术主要有PHT(pattern history table),BTB(branch target buffer),BHT,tomasulo算法等。
PHT的核心思想是记录每次转移的方向(PC的低位偏移值),转移时根据前面的记录预测下次转移方向。
由于PHT记录所有的转移地址,不止是只记录跳转指令的跳转方向,而是记录所有的下一条指令的地址。这是一个方面,另外一个方面,PHT只记录转移方向,不记录转移目标。
为了解决这两个问题才引入了BTB,BTB首先要判断是否是转移指令,如果是才记录其转移方向和转移目标的地址。
上面两种方式只是记录某转移指令本身的历史,其实转移指令之间还有相关性,所以需要把所有转移指令的转移情况记录下来,存放这些转移指令历史的地方这就是BHR(branch history register),把BHR和PHT结合起来就既能考虑到某条指令的历史,也能考虑到转移指令之间的历史,这就是两级转移预测器。
BHR和PHT的组合有很多方式,其中gshare算法简单而高效,所以在现在很多CPU中使用。
虽然两级转移预测器的效率很高,但是仍然有一个问题,就是所有的转移指令使用同一个PHT,这就造成的分支别名干扰的问题,为了解决这个问题,人们想了很多办法,如Bi-Mode预测器和Agree预测器等。
后来,人们为了提高预测成功率,就把上面所有的预测方法进行组合,找出适合自己系统的预测器。
2,or1200的IF模块分析
1>整体介绍
上面我们介绍了很多分支预测技术,遗憾的是,or1200作为一个static in-order pipeline,这些技术都没有使用。genpc模块只是简单的将跳转地址传给icache,icache模块将指令传给 if模块,if模块将取得的指令再交给ID阶段(就是ctrl模块)。需要说明的是genpc模块和icache模块之间也是wishbone接口,只不过genpc模块提供选择信号,地址信号给icache,然后icache将数据信号(指令),传给if模块。整体结构如下所示:
2>genpc模块
genpc模块的功能就是计算下一条指令的地址(PC,program counter),给icache模块。genpc模块产生PC可大致分为以下几个方面:
a,正常情况
正常指令的执行时,没有分支和跳转,没有异常,直接将PC+4就是下次PC的值。
b,异常
如果出现异常,genpc模块根据异常类型,产生下次PC的值,就是异常入口的地址。当然要保存当前PC的值,留作异常返回时使用。
c,跳转指令
直接取出指令中的跳转地址,作为PC的值。
d,分支指令
ctrl模块先假设条件满足,计算出分支指令的偏移值(eg.PC1),保存当前的PC值(eg.PC2),genpc模块根据条件是否满足来产生对应的PC。
e,复位
如果是复位,genpc模块会根据变量OR1200_BOOT_ADR来产生PC值,这个值在or1200_define.c文件的最后有定义,如下所示:
关于这个启动地址,我们之前在分析ORPSoC的启动过程时专门讨论过,请参考:http://blog.csdn.net/rill_zhen/article/details/8855743
///////////////////////////////////////////////////////////////////////////////
// Boot Address Selection //
// //
// Allows a definable boot address, potentially different to the usual reset //
// vector to allow for power-on code to be run, if desired. //
// //
// OR1200_BOOT_ADR should be the 32-bit address of the boot location //
// OR1200_BOOT_PCREG_DEFAULT should be ((OR1200_BOOT_ADR-4)>>2) //
// //
// For default reset behavior uncomment the settings under the "Boot 0x100" //
// comment below. //
// //
///////////////////////////////////////////////////////////////////////////////
// Boot from 0xf0000100
//`define OR1200_BOOT_PCREG_DEFAULT 30'h3c00003f
//`define OR1200_BOOT_ADR 32'hf0000100
// Boot from 0x100
`define OR1200_BOOT_PCREG_DEFAULT 30'h0000003f
`define OR1200_BOOT_ADR 32'h00000100
genpc模块对应的文件是,or1200_genpc.v,下面是genpc模块的核心代码:
//
// Address of insn to be fecthed
//
assign icpu_adr_o = !no_more_dslot & !except_start & !spr_pc_we
& (icpu_rty_i | genpc_refetch) ?
icpu_adr_i : {pc[31:2], 1'b0, ex_branch_taken|spr_pc_we}; //
// Control access to IC subsystem
//
assign icpu_cycstb_o = ~(genpc_freeze | (|pre_branch_op && !icpu_rty_i));
assign icpu_sel_o = 4'b1111;
assign icpu_tag_o = `OR1200_ITAG_NI; //
// genpc_freeze_r
//
always @(posedge clk or `OR1200_RST_EVENT rst)
if (rst == `OR1200_RST_VALUE)
genpc_refetch_r <= 1'b0;
else if (genpc_refetch)
genpc_refetch_r <= 1'b1;
else
genpc_refetch_r <= 1'b0; //
// Async calculation of new PC value. This value is used for addressing the
// IC.
//
always @(pcreg or ex_branch_addrtarget or flag or branch_op or except_type
or except_start or operand_b or epcr or spr_pc_we or spr_dat_i or
except_prefix)
begin
casez ({spr_pc_we, except_start, branch_op}) // synopsys parallel_case
{2'b00, `OR1200_BRANCHOP_NOP}: begin
pc = {pcreg + 30'd1, 2'b0};
ex_branch_taken = 1'b0;
end
{2'b00, `OR1200_BRANCHOP_J}: begin
pc = {ex_branch_addrtarget, 2'b00};
ex_branch_taken = 1'b1;
end
{2'b00, `OR1200_BRANCHOP_JR}: begin
pc = operand_b;
ex_branch_taken = 1'b1;
end
{2'b00, `OR1200_BRANCHOP_BF}:
if (flag) begin
pc = {ex_branch_addrtarget, 2'b00};
ex_branch_taken = 1'b1;
end
else begin
pc = {pcreg + 30'd1, 2'b0};
ex_branch_taken = 1'b0;
end
{2'b00, `OR1200_BRANCHOP_BNF}:
if (flag) begin
pc = {pcreg + 30'd1, 2'b0};
ex_branch_taken = 1'b0;
end
else begin
pc = {ex_branch_addrtarget, 2'b00};
ex_branch_taken = 1'b1;
end
{2'b00, `OR1200_BRANCHOP_RFE}: begin
pc = epcr;
ex_branch_taken = 1'b1;
end
{2'b01, 3'b???}: begin
pc = {(except_prefix ?
`OR1200_EXCEPT_EPH1_P : `OR1200_EXCEPT_EPH0_P),
except_type, `OR1200_EXCEPT_V};
ex_branch_taken = 1'b1;
end
default: begin
pc = spr_dat_i;
ex_branch_taken = 1'b0;
end
endcase
end //
// PC register
//
always @(posedge clk or `OR1200_RST_EVENT rst)
// default value
if (rst == `OR1200_RST_VALUE) begin
pcreg_default <= `OR1200_BOOT_PCREG_DEFAULT; // jb
pcreg_select <= 1'b1;// select async. value due to reset state
end
// selected value (different from default) is written into FF after
// reset state
else if (pcreg_select) begin
// dynamic value can only be assigned to FF out of reset!
pcreg_default <= pcreg_boot[31:2];
pcreg_select <= 1'b0; // select FF value
end
else if (spr_pc_we) begin
pcreg_default <= spr_dat_i[31:2];
end
else if (no_more_dslot | except_start | !genpc_freeze & !icpu_rty_i
& !genpc_refetch) begin
pcreg_default <= pc[31:2];
end // select async. value for pcreg after reset - PC jumps to the address selected
// after boot.
assign pcreg_boot = `OR1200_BOOT_ADR; // changed JB always @(pcreg_boot or pcreg_default or pcreg_select)
if (pcreg_select)
// async. value is selected due to reset state
pcreg = pcreg_boot[31:2];
else
// FF value is selected 2nd clock after reset state
pcreg = pcreg_default ;
3>if模块
if模块接收icache模块送来的指令,转交给ID级(ctrl模块),用来进行指令译码,如果在取指的时候有什么异常(ITLB miss,IMMU页异常,指令总线error),if模块将异常信号给异常模块,并告诉genpc模块,重新获得PC的值。if模块在遇到异常时会产生信号给freeze模块来停止流水线。
if模块对应的文件是,or1200_if.v,下面是if模块的核心代码:
assign save_insn = (icpu_ack_i | icpu_err_i) & if_freeze & !saved;
assign saving_if_insn = !if_flushpipe & save_insn; //
// IF bypass
//
assign if_bypass = icpu_adr_i[0] ? 1'b0 : if_bypass_reg | if_flushpipe; always @(posedge clk or `OR1200_RST_EVENT rst)
if (rst == `OR1200_RST_VALUE)
if_bypass_reg <= 1'b0;
else
if_bypass_reg <= if_bypass; //
// IF stage insn
//
assign if_insn = no_more_dslot | rfe | if_bypass ? {`OR1200_OR32_NOP, 26'h041_0000} : saved ? insn_saved : icpu_ack_i ? icpu_dat_i : {`OR1200_OR32_NOP, 26'h061_0000};
assign if_pc = saved ? addr_saved : {icpu_adr_i[31:2], 2'h0};
assign if_stall = !icpu_err_i & !icpu_ack_i & !saved;
assign genpc_refetch = saved & icpu_ack_i;
assign except_itlbmiss = no_more_dslot ? 1'b0 : saved ? err_saved[0] : icpu_err_i & (icpu_tag_i == `OR1200_ITAG_TE);
assign except_immufault = no_more_dslot ? 1'b0 : saved ? err_saved[1] : icpu_err_i & (icpu_tag_i == `OR1200_ITAG_PE);
assign except_ibuserr = no_more_dslot ? 1'b0 : saved ? err_saved[2] : icpu_err_i & (icpu_tag_i == `OR1200_ITAG_BE); //
// Flag for saved insn/address
//
always @(posedge clk or `OR1200_RST_EVENT rst)
if (rst == `OR1200_RST_VALUE)
saved <= 1'b0;
else if (if_flushpipe)
saved <= 1'b0;
else if (save_insn)
saved <= 1'b1;
else if (!if_freeze)
saved <= 1'b0; //
// Store fetched instruction
//
always @(posedge clk or `OR1200_RST_EVENT rst)
if (rst == `OR1200_RST_VALUE)
insn_saved <= {`OR1200_OR32_NOP, 26'h041_0000};
else if (if_flushpipe)
insn_saved <= {`OR1200_OR32_NOP, 26'h041_0000};
else if (save_insn)
insn_saved <= icpu_err_i ? {`OR1200_OR32_NOP, 26'h041_0000} : icpu_dat_i;
else if (!if_freeze)
insn_saved <= {`OR1200_OR32_NOP, 26'h041_0000}; //
// Store fetched instruction's address
//
always @(posedge clk or `OR1200_RST_EVENT rst)
if (rst == `OR1200_RST_VALUE)
addr_saved <= 32'h00000000;
else if (if_flushpipe)
addr_saved <= 32'h00000000;
else if (save_insn)
addr_saved <= {icpu_adr_i[31:2], 2'b00};
else if (!if_freeze)
addr_saved <= {icpu_adr_i[31:2], 2'b00}; //
// Store fetched instruction's error tags
//
always @(posedge clk or `OR1200_RST_EVENT rst)
if (rst == `OR1200_RST_VALUE)
err_saved <= 3'b000;
else if (if_flushpipe)
err_saved <= 3'b000;
else if (save_insn) begin
err_saved[0] <= icpu_err_i & (icpu_tag_i == `OR1200_ITAG_TE);
err_saved[1] <= icpu_err_i & (icpu_tag_i == `OR1200_ITAG_PE);
err_saved[2] <= icpu_err_i & (icpu_tag_i == `OR1200_ITAG_BE);
end
else if (!if_freeze)
err_saved <= 3'b000;
3,小结
or1200采用经典五级流水线,而IF模块是整条流水线的开端,把守着流水线的大门,所以IF模块一般为了产生精准的PC值,一般要有复杂的转移预测器,但是or1200并没有任何的转移预测技术。
OpenRisc-43-or1200的IF模块分析的更多相关文章
- OpenRisc-41-or1200的cache模块分析
		
引言 为CPU提供足够的,稳定的指令流和数据流是计算机体系结构设计中两个永恒的话题.为了给CPU提供指令流,需要设计分支预测机构,为了给CPU提供数据流,就需要设计cache了.其实,无论是insn还 ...
 - OpenRisc-40-or1200的MMU模块分析
		
引言 MMU(memory management unit),无论对于computer architecture designer还是OS designer,都是至关重要的部分,设计和使用的好坏,对性 ...
 - OpenRisc-48-or1200的SPRS模块分析
		
引言 之前,我们在分析or1200的WB模块时(http://blog.csdn.net/rill_zhen/article/details/10220619),介绍了OpenRISC的GPRS(ge ...
 - OpenRisc-42-or1200的ALU模块分析
		
引言 computer(计算机),顾名思义,就是用来compute(计算)的.计算机体系结构在上世纪五六十年代的时候,主要就是研究如何设计运算部件,就是想办法用最少的元器件(那时元器件很贵),最快的速 ...
 - OpenRisc-47-or1200的WB模块分析
		
引言 “善妖善老,善始善终”,说的是无论什么事情要从有头有尾,别三分钟热度. 对于or1200的流水线来说,MA阶段是最后一个阶段,也是整条流水线的收尾阶段,负责战场的清扫工作.比如,把运算指令的运算 ...
 - OpenRisc-45-or1200的ID模块分析
		
引言 之前,我们分析了or1200流水线的整体结构,也分析了流水线中IF级,EX级,本小节我们来分析ID(insn decode)级的一些细节. 1,基础 or1200的pipeline的ID阶段包含 ...
 - nginx事件模块分析(一)
		
nginx ngx_events_module模块分析 ngx_events_module模块是核心模块之一,它是其它所有事件模块的代理模块.nginx在启动时只与events模块打交道,而由even ...
 - 游戏模块分析总结(2)之UI、操作篇
		
转自:http://www.gameres.com/309812.html 游戏模块分析总结(2)之UI.操作篇 发布者: wuye | 发布时间: 2014-12-12 15:03| 评论数: 0 ...
 - css扁平化博客学习总结(一)模块分析
		
一.模块分析 1.每开发一个项目之前,首先要对项目进行一个大致规划,它到底要做什么功能,它有什么具体需求. 2.所以需要进行模块化分析,把这些东西具象化,把一个问题模块化,对需求有一个宏观的了解. 3 ...
 
随机推荐
- Android学习笔记(广播机制)
			
1.Android的广播机制介绍 收听收音机也是一种广播,在收音机中有很多个广播电台,每个广播电台播放的内容都不相同.接受广播时广播(发送方)并不在意我们(接收方)接收到广播时如何处理.好比我们收听交 ...
 - 【转】iOS- 详解文本属性Attributes
			
原文: http://www.cnblogs.com/qingche/p/3574995.html?utm_source=tuicool 1.NSKernAttributeName: @10 调整字句 ...
 - modifytime是一个神奇的column name----这边文章是错的totally,因为我的实验不彻底。timestamp属性很神奇,头一个timestamp,会自动的成DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
			
在mysql里边modifytime是一个神奇的column name,试一下. 请执行sql语句 CREATE TABLE `test_time` ( `modifytime` timestamp ...
 - 常见ORACLE错误,及解决方案(遇则即时更新)
			
1.当登陆时提示“ORA-03113:通信通道的文件结束”时: 解决方案: 需在X:\oraclexe\app\oracle\product\10 ...
 - 原生JS写Ajax的请求函数
			
一.JS原生ajax ajax:一种请求数据的方式,不需要刷新整个页面:ajax的技术核心是 XMLHttpRequest 对象:ajax 请求过程:创建 XMLHttpRequest 对象.连接服务 ...
 - 转:Win7 IIS7应用PHP Manager使用FastCGI通道快速部署PHP支持
			
原文来自于:http://www.jb51.net/os/windows/62390.html 正常情况下,我们在Windows系统中部署WEB服务器(iis)支持PHP是采用ISAPI通道.参照这篇 ...
 - C#程序设计基础——运算符与表达式
			
运算符就是完成操作的一系列符号,它主要包括算术运算符.赋值运算符.关系运算符.逻辑运算符.条件运算.位操作运算符和字符串运算符. 表达式就是运算符和操作数的组合,如a*b+1-c.表达式主要包括算术表 ...
 - c#分支语句;循环语句(随堂练习)
			
1. 输入月份,日期号,输出是今年的第几天 平年,2月28天 switch (变量名) {case "": break} 2. 循环语句: for(int i ...
 - Integer Intervals(贪心)
			
Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 12123 Accepted: 5129 Description An i ...
 - [转]是String,StringBuffer还是StringBuilder?
			
原文链接. 相信大家对 String 和 StringBuffer 的区别也已经很了解了,但是估计还是会有很多同志对这两个类的工作原理有些不清楚的地方,今天我在这里重新把这个概念给大家复习一下,顺便牵 ...