博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OpenRisc-47-or1200的WB模块分析
阅读量:5773 次
发布时间:2019-06-18

本文共 6595 字,大约阅读时间需要 21 分钟。

引言

“善妖善老,善始善终”,说的是无论什么事情要从有头有尾,别三分钟热度。

对于or1200的流水线来说,MA阶段是最后一个阶段,也是整条流水线的收尾阶段,负责战场的清扫工作。比如,把运算指令的运算结果要写到寄存器里,把从内存读来的数据写到寄存器里,如果在前面的流水阶段出现了异常,WB阶段还要负责把异常指令的地址存到寄存器里。总之呢,就是写回寄存器。

本小节,我们就分析一下or1200五级流水线最后一级,也就是WB(write back)。

 

1,整体结构

or1200的WB模块,主要包括的rtl文件是or1200_wbmux.v和or1200_rf.v。上面,我们介绍了WB阶段的功能和任务,那么这个模块的整体结构是什么样子的呢?如下所示。

 

WB模块负责将其他流水阶段的结果写回寄存器堆,而寄存器堆只有一个,所以就需要一个多路选择器(wb_mux)。

wb_mux是一个5选1的多路选择器,指令解码的结果和ex_freeze信号作为多路选择器的选择信号。

需要选择的5路信号分别是:

a,来自运算单元的alu的运算结果和fpu的运算结果(fpu模块,并没有实现)。

b,load指令的处理结果(从内存读来的数据)。

c,异常指令的地址。这个需要说明的是,在采用流水线的cpu结构时,各个流水阶段都可能产生异常,但并不是一旦产生异常后就马上处理,而是把所有流水阶段的异常统一处理,这样才能保证精确异常。

d,来自特殊功能寄存器的数据。

 

多路选择器的输出:

wb_mux模块的输出,

a,oprandmuxes模块。这个是用作forword的。流水线的forword技术,之前我们曾经介绍过,这里不再赘述。

b,rf模块。这个是显然的,写回阶段,肯定要将数据写回到reg_file里面。

c,调试模块。将输出数据给上述模块的同时,也会送给debug模块,这个是调试单元的需要。

 

2,wb_mux模块

上面介绍了wb_mux的整体功能,下面我们就是其对应的rtl代码。

 

 

module or1200_wbmux(	// Clock and reset	clk, rst,	// Internal i/f	wb_freeze, rfwb_op,	muxin_a, muxin_b, muxin_c, muxin_d, muxin_e,	muxout, muxreg, muxreg_valid);parameter width = 32;//// Internal wires and regs//reg	[width-1:0]		muxout;reg	[width-1:0]		muxreg;reg				muxreg_valid;//// Registered output from the write-back multiplexer//always @(posedge clk or `OR1200_RST_EVENT rst) begin	if (rst == `OR1200_RST_VALUE) begin		muxreg <=  32'd0;		muxreg_valid <=  1'b0;	end	else if (!wb_freeze) begin		muxreg <=  muxout;		muxreg_valid <=  rfwb_op[0];	endend//// Write-back multiplexer//always @(muxin_a or muxin_b or muxin_c or muxin_d or muxin_e or rfwb_op) begin	casez(rfwb_op[4:1])		`OR1200_RFWBOP_ALU: muxout = muxin_a;		`OR1200_RFWBOP_LSU: begin			muxout = muxin_b;		end		`OR1200_RFWBOP_SPRS: begin			muxout = muxin_c;		end		`OR1200_RFWBOP_LR: begin			muxout = muxin_d + 32'h8;		end		`ifdef OR1200_FPU_IMPLEMENTED	        `OR1200_RFWBOP_FPU : begin	     muxout = muxin_e;	     	       end		      		`endif	  default : begin	     muxout = 0;	  end	  	endcaseendendmodule

 

3,rf模块

1>整体分析

写回的意思,就是写回到寄存器堆(register file),所以,rf模块是WB阶段的核心模块。

寄存器堆,其物理结构就是RAM。其整体结构如下所示:

 

or1200的寄存器堆是由两个双端口的RAM组成的。关于这两个RAM(rf_a,rf_b),有以下几点需要注意:

a,每个RAM由32个4字节的寄存器(32x32)组成,分别是r0~r31。

b,双端的RAM,有两个端口,portA和portB,其中portA用来读,portB用来写。

c,这两个双端口的RAM是同步的,也就是说读写端口的时钟信号是相同的。

d,这两个双端口RAM的连接也很有意思,rf_a和rf_b的写端口是连在一起的。也就是说,rf_a和rf_b两个寄存器堆的值是完全相同的。rf_a和rf_b的读端口是分开的。

关于为什么采用两个寄存器堆,这两个寄存器堆为什么要这样连接?原因如下:

在解释原因之前,我们先看一条汇编指令:add r2,r2,r1。其含义是将r1和r2的值相加,再将计算结果写回r2。下面我们看一下分别采用1个寄存器堆和两个寄存器堆的处理过程。

采用一个寄存器堆:共需要4步(不包括指令译码)。

a,读取r2的值

b,读取r1的值

c,运算

d,将结果写回r2

 

采用两个寄存器堆:只需3步(不包含指令译码)。

 

a,同时读取r2的值,r1的值

b,运算

c,将结果写回r2(同时写入rf_a和rf_b)

 

可见,如果采用两套寄存器堆,并且想办法使两套寄存器堆的值完全一样的话,就会减少操作时间。

其实CPU采用多套寄存器堆,是很常见的一种做法。采用不止一套寄存器堆,除了可以提高指令执行速度之外,还有另外一种好处,就是MIPS结构中的影子寄存器(shadow register),专门用来进行异常处理中的现场保护,减少异常上下文的切换时间。

关于指令中的两个源寄存器的地址解码是在ID阶段完成的,这个,我们前面在分析ID模块时已说过,如有疑问,请参考相关内容。下面是解码的核心代码(or1200_ctrl.v)。

 

 

assign rf_addra = if_insn[20:16];assign rf_addrb = if_insn[15:11];assign rf_rda = if_insn[31] || if_maci_op;assign rf_rdb = if_insn[30];

 

 

2>代码分析

了解了rf的整体结构之后,再来分析代码就简单多了。

rf模块对应的rtl文件是or1200_rf.v。

1》寄存器堆的例化

下面是例化两个寄存器堆的部分代码。

 

 

//// Instantiation of register file two-port RAM A//   or1200_dpram #     (      .aw(5),      .dw(32)      )   rf_a     (      // Port A      .clk_a(clk),      .ce_a(rf_ena),      .addr_a(rf_addra),      .do_a(from_rfa),            // Port B      .clk_b(clk),      .ce_b(rf_we),      .we_b(rf_we),      .addr_b(rf_addrw),      .di_b(rf_dataw)      );   //   // Instantiation of register file two-port RAM B   //   or1200_dpram #     (      .aw(5),      .dw(32)      )   rf_b     (      // Port A      .clk_a(clk),      .ce_a(rf_enb),      .addr_a(addrb),      .do_a(from_rfb),            // Port B      .clk_b(clk),      .ce_b(rf_we),      .we_b(rf_we),      .addr_b(rf_addrw),      .di_b(rf_dataw)      );

 

可见是例化了两个完全一样的双端口ram,关于双端口ram本身的逻辑,我想大家就都很熟悉了,这里不做过多解释,or1200_dpram_32x32.v文件的核心代码如下。

 

 

//// Generic RAM's registers and wires//reg	[dw-1:0]	mem [(1<

 

 

 

2》特殊处理

rf模块的读来源有两个,一个是sprs,一个是debug。

rf模块的写来源也有两个,一个是sprs,一个是其它流水阶段的正常结果。

由于两个寄存器堆的内容完全一样,所以debug模块只需要读一个就可以了,对于or1200的具体实现,debug读的是rf_a。

 

由于rf_a有两个读来源,那么如果不进行特殊处理,当sprs和debug同时发来读请求的话,那么就可能会出现竞争,造成错误的输出结果。为了避免这种情况出现,or1200增加了一个仲裁机制。首先本地设置一个寄存器来保存上次读操作的地址,此外还增加了一根竞争信号线。这样一旦产生竞争,根据优先级设置的仲裁策略,选择是由sprs读还是debug来读。代码如下所示:

其中有两个关键信号:spr_valid和spr_cs_fe。

spr_valid信号是对两个写来源的仲裁结果。

spr_cs_fe是对两个读来源的仲裁结果。

只要弄明白了这两个信号,这个模块就很好理解了。

 

 

 

// Logic to restore output on RFA after debug unit has read out via SPR if.   // Problem was that the incorrect output would be on RFA after debug unit   // had read out  - this is bad if that output is relied upon by execute   // stage for next instruction. We simply save the last address for rf A and   // and re-read it whenever the SPR select goes low, so we must remember   // the last address and generate a signal for falling edge of SPR cs.   // -- Julius      // Detect falling edge of SPR select    reg 				spr_du_cs;   wire 			spr_cs_fe;   // Track RF A's address each time it's enabled   reg	[aw-1:0]	addra_last;   always @(posedge clk)     if (rf_ena & !(spr_cs_fe | (du_read & spr_cs)))       addra_last <= addra;   always @(posedge clk)     spr_du_cs <= spr_cs & du_read;   assign spr_cs_fe = spr_du_cs & !(spr_cs & du_read);   //// SPR access is valid when spr_cs is asserted and// SPR address matches GPR addresses//assign spr_valid = spr_cs & (spr_addr[10:5] == `OR1200_SPR_RF);//// SPR data output is always from RF A//assign spr_dat_o = from_rfa;//// Operand A comes from RF or from saved A register//assign dataa = from_rfa;//// Operand B comes from RF or from saved B register//assign datab = from_rfb;//// RF A read address is either from SPRS or normal from CPU control//assign rf_addra = (spr_valid & !spr_write) ? spr_addr[4:0] : 		  spr_cs_fe ? addra_last : addra;//// RF write address is either from SPRS or normal from CPU control//assign rf_addrw = (spr_valid & spr_write) ? spr_addr[4:0] : addrw;//// RF write data is either from SPRS or normal from CPU datapath//assign rf_dataw = (spr_valid & spr_write) ? spr_dat_i : dataw;//// RF write enable is either from SPRS or normal from CPU control//always @(`OR1200_RST_EVENT rst or posedge clk)	if (rst == `OR1200_RST_VALUE)		rf_we_allow <=  1'b1;	else if (~wb_freeze)		rf_we_allow <=  ~flushpipe;assign rf_we = ((spr_valid & spr_write) | (we & ~wb_freeze)) & rf_we_allow;assign cy_we_o = cy_we_i && ~wb_freeze && rf_we_allow;   //// CS RF A asserted when instruction reads operand A and ID stage// is not stalled//assign rf_ena = (rda & ~id_freeze) | (spr_valid & !spr_write) | spr_cs_fe;//// CS RF B asserted when instruction reads operand B and ID stage// is not stalled//assign rf_enb = rdb & ~id_freeze;

 

4,小结

自此,我们已经分析了or1200的流水线的整个条数据通路。

 

 

 

 

你可能感兴趣的文章
王可言:「萬物聯網+人聯網+隨時在線」的新時代
查看>>
JAVA编程思想一
查看>>
java四种线程池简介,使用
查看>>
一般处理程序(.ashx)中session的使用方法
查看>>
EasyUI笔记(二)Layout布局
查看>>
ios View之间的切换 屏幕旋转
查看>>
js创建表格时最好要创建tbody元素
查看>>
ASN.1探索 - 3 编码规则与传输语法(1 - BER)(转)
查看>>
BZOJ2208:[JSOI2010]连通数(DFS)
查看>>
第三章:内存分配与回收策略
查看>>
Linux系统的优势
查看>>
jquery压缩图片插件
查看>>
Apple Swift学习资料汇总
查看>>
如何获取目录下所有文件名 c++
查看>>
SQL Server数据库无法启动(万金油解决办法)
查看>>
有关UIView、subview的几个基础知识点-IOS开发 (实例)
查看>>
【SICP练习】49 练习2.17
查看>>
OC 线程操作3 - NSOperation
查看>>
上载EXCEL到SAP系统的方法之一
查看>>
jquery 使用on方法给元素绑定事件
查看>>