自己动手写CPU之第七阶段(5)——流水线暂停机制的设计与实现
来源:互联网 发布:淘宝创建追评失败 编辑:程序博客网 时间:2024/05/16 12:21
将陆续上传本人写的新书《自己动手写CPU》,今天是第28篇,我尽量每周四篇
China-pub的预售地址如下(有目录、内容简介、前言):
http://product.china-pub.com/3804025
亚马逊的预售地址如下,欢迎大家围观呵!
http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4
7.5 流水线暂停机制的设计与实现
7.5.1 流水线暂停机制设计
因为OpenMIPS设计乘累加、乘累减、除法指令在流水线执行阶段占用多个时钟周期,因此需要暂停流水线,以等待这些多周期指令执行完毕,一种直观的实现方法是:要暂停流水线,只需保持取指令地址PC的值不变,同时保持流水线各个阶段的寄存器(也就是IF/ID、ID/EX、EX/MEM、MEM/WB模块的输出)不变。
OpenMIPS采用的是一种改进的方法:假如位于流水线第n阶段的指令需要多个时钟周期,进而请求流水线暂停,那么需保持取指令地址PC的值不变,同时保持流水线第n阶段、第n阶段之前的各个阶段的寄存器不变,而第n阶段后面的指令继续运行。比如:流水线执行阶段的指令请求流水线暂停,那么保持PC不变,同时保持取指、译码、执行阶段的寄存器不变,但是可以允许访存、回写阶段的指令继续运行。
为此,设计添加CTRL模块,其作用是接收各个阶段传递过来的流水线暂停请求信号,从而控制流水线各个阶段的运行。
为了实现流水线暂停机制,对系统结构做如图7-10所示的修改。
CTRL模块的输入来自ID、EX模块的请求暂停信号stallreq,对于OpenMIPS教学版而言,只有译码、执行阶段可能会有暂停请求,取指、访存阶段都没有暂停请求,因为指令读取、数据存储器的读写操作都可以在一个时钟周期完成。
CTRL模块对暂停请求信号进行判断,然后输出流水线暂停信号stall。从图7-10中可知,stall输出到PC、IF/ID、ID/EX、EX/MEM、MEM/WB等模块,从而控制PC的值,以及流水线各个阶段的寄存器。
7.5.2 流水线暂停机制实现
1、CTRL模块的实现
CTRL模块的接口如图7-10中所示,各接口作用如表7-1所示。
读者需要注意:输出信号stall是一个宽度为6的信号,其含义如下。
- stall[0]表示取指地址PC是否保持不变,为1表示保持不变
- stall[1]表示流水线取指阶段是否暂停,为1表示暂停
- stall[2]表示流水线译码阶段是否暂停,为1表示暂停
- stall[3]表示流水线执行阶段是否暂停,为1表示暂停
- stall[4]表示流水线访存阶段是否暂停,为1表示暂停
- stall[5]表示流水线回写阶段是否暂停,为1表示暂停
CTRL模块的代码如下,源文件是本书附带光盘Code\Chapter7_2目录下的ctrl.v。
module ctrl(input wirerst,input wire stallreq_from_id, // 来自译码阶段的暂停请求input wire stallreq_from_ex, // 来自执行阶段的暂停请求output reg[5:0] stall );always @ (*) begin if(rst == `RstEnable) beginstall <= 6'b000000; end else if(stallreq_from_ex == `Stop) beginstall <= 6'b001111; end else if(stallreq_from_id == `Stop) beginstall <= 6'b000111; end else beginstall <= 6'b000000; endendendmodule其中宏定义Stop在defines.v中定义,如下:`define Stop 1'b1 // 流水线暂停`define NoStop 1'b0 // 流水线继续
可以从以下三点理解。
(1)当处于流水线执行阶段的指令请求暂停时(即stallreq_from_ex等于Stop),按照OpenMIPS流水线暂停机制的设计,要求取指、译码、执行阶段暂停,而访存、回写阶段继续,所以设置stall为6'b001111。
(2)当处于流水线译码阶段的指令请求暂停时(即stallreq_from_id等于Stop),按照OpenMIPS流水线暂停机制的设计,要求取指、译码阶段暂停,而执行、访存、回写阶段继续,所以设置stall为6'b000111。
(3)其余情况下,设置stall为6'b000000,表示不暂停流水线。
2、修改取指阶段
(1)修改PC模块
从图7-10中可知,PC模块新增加了一个输入信号stall,其值就是CTRL模块的接口stall。修改取指阶段PC模块的代码如下,其中修改的代码使用加粗、斜体标识。源文件是本书附带光盘Code\Chapter7_2目录下的pc_reg.v。
module pc_reg(inputwire clk,input wire rst,input wire[5:0] stall, // 来自控制模块CTRLoutput reg[`InstAddrBus] pc ,output reg ce);always @ (posedge clk) begin if (rst == `RstEnable) begince <= `ChipDisable; end else begince <= `ChipEnable; endend // 当stall[0]为NoStop时,pc加4,否则,保持pc不变always @ (posedge clk) begin if (ce == `ChipDisable) beginpc <= 32'h00000000; end else if(stall[0] == `NoStop) beginpc <= pc + 4'h4; endendendmodule
(2)修改IF/ID模块
参考图7-10,IF/ID模块也新增了一个输入信号stall,主要修改如下,修改的代码使用加粗、斜体标识。完整代码参考本书附带光盘Code\Chapter7_2目录下的if_id.v文件。
module if_id(......input wire[5:0] stall,); //(1)当stall[1]为Stop,stall[2]为NoStop时,表示取指阶段暂停, // 而译码阶段继续,所以使用空指令作为下一个周期进入译码阶段的指令。 //(2)当stall[1]为NoStop时,取指阶段继续,取得的指令进入译码阶段。 //(3)其余情况下,保持译码阶段的寄存器id_pc、id_inst不变。always @ (posedge clk) begin if (rst == `RstEnable) beginid_pc <= `ZeroWord;id_inst <= `ZeroWord; end else if(stall[1] == `Stop && stall[2] == `NoStop) beginid_pc <= `ZeroWord;id_inst <= `ZeroWord; end else if(stall[1] == `NoStop) begin id_pc <= if_pc;id_inst <= if_inst; endendendmodule
3、修改译码阶段
(1)修改ID模块
参考图7-10,ID模块新增了一个输出信号stallreq,在实现加载、存储指令的时候会给该信号赋值,目前暂时设置这个输出就是NoStop,具体代码不再给出,读者可以参考本书附带光盘Code\Chapter7_2目录下的id.v文件。
(2)修改ID/EX模块
参考图7-10,ID/EX模块新增了一个输入信号stall,主要修改如下,修改的代码使用加粗、斜体标识。完整代码位于本书附带光盘Code\Chapter7_2目录下的id_ex.v文件。
module id_ex(......//来自控制模块的信息input wire[5:0]stall,......); //(1)当stall[2]为Stop,stall[3]为NoStop时,表示译码阶段暂停, // 而执行阶段继续,所以使用空指令作为下一个周期进入执行阶段的指令。 //(2)当stall[2]为NoStop时,译码阶段继续,译码后的指令进入执行阶段。 //(3)其余情况下,保持执行阶段的寄存器ex_aluop、ex_alusel、ex_reg1、 // ex_reg2、ex_wd、ex_wreg不变。always @ (posedge clk) begin if (rst == `RstEnable) begin ...... end else if(stall[2] == `Stop && stall[3] == `NoStop) begin ex_aluop <= `EXE_NOP_OP;ex_alusel <= `EXE_RES_NOP;ex_reg1 <= `ZeroWord;ex_reg2 <= `ZeroWord;ex_wd <= `NOPRegAddr;ex_wreg <= `WriteDisable; end else if(stall[2] == `NoStop) beginex_aluop <= id_aluop;ex_alusel <= id_alusel;ex_reg1 <= id_reg1;ex_reg2 <= id_reg2;ex_wd <= id_wd;ex_wreg <= id_wreg; endendendmodule
4、修改执行阶段
(1)修改EX模块
参考图7-10,EX模块新增了一个输出信号stallreq_from_ex,在本章后面实现乘累加、乘累减、除法指令的时候会给该信号赋值。
(2)修改EX/MEM模块
参考图7-10,EX/MEM模块新增了一个输入信号stall,主要修改如下,修改的代码使用加粗、斜体标识。完整代码位于本书附带光盘Code\Chapter7_2目录下的ex_mem.v文件。module ex_mem(...... //来自控制模块的信息input wire[5:0] stall,......); //(1)当stall[3]为Stop,stall[4]为NoStop时,表示执行阶段暂停, // 而访存阶段继续,所以使用空指令作为下一个周期进入访存阶段的指令。 //(2)当stall[3]为NoStop时,执行阶段继续,执行后的指令进入访存阶段。 //(3)其余情况下,保持访存阶段的寄存器mem_wb、mem_wreg、mwm_wdata、 // mem_hi、mem_lo、mem_whilo不变。always @ (posedge clk) begin if(rst == `RstEnable) begin...... end else if(stall[3] == `Stop && stall[4] == `NoStop) beginmem_wd <= `NOPRegAddr;mem_wreg <= `WriteDisable;mem_wdata <= `ZeroWord;mem_hi <= `ZeroWord;mem_lo <= `ZeroWord;mem_whilo <= `WriteDisable; end else if(stall[3] == `NoStop) beginmem_wd <= ex_wd;mem_wreg <= ex_wreg;mem_wdata <= ex_wdata;mem_hi <= ex_hi;mem_lo <= ex_lo;mem_whilo <= ex_whilo; end //ifend //alwaysendmodule
5、修改访存阶段
访存阶段只要修改MEM/WB模块,参考图7-10,MEM/WB模块也新增了一个输入信号stall,主要修改如下,修改的代码使用加粗、斜体标识。完整代码位于本书附带光盘Code\Chapter7_2目录下的mem_wb.v文件。
module mem_wb(...... //来自控制模块的信息input wire[5:0] stall,......); //(1)当stall[4]为Stop,stall[5]为NoStop时,表示访存阶段暂停, // 而回写阶段继续,所以使用空指令作为下一个周期进入回写阶段的指令。 //(2)当stall[4]为NoStop时,访存阶段继续,访存后的指令进入回写阶段。 //(3)其余情况下,保持回写阶段的寄存器wb_wd、wb_wreg、wb_wdata、 // wb_hi、wb_lo、wb_whilo不变。always @ (posedge clk) begin if(rst == `RstEnable) begin...... end else if(stall[4] == `Stop && stall[5] == `NoStop) beginwb_wd <= `NOPRegAddr;wb_wreg <= `WriteDisable; wb_wdata <= `ZeroWord;wb_hi <= `ZeroWord;wb_lo <= `ZeroWord;wb_whilo <= `WriteDisable; end else if(stall[4] == `NoStop) beginwb_wd <= mem_wd;wb_wreg <= mem_wreg;wb_wdata <= mem_wdata;wb_hi <= mem_hi;wb_lo <= mem_lo;wb_whilo <= mem_whilo; end //ifend //alwaysendmodule
6、修改顶层模块OpenMIPS
因为上面添加了CTRL模块,而且对流水线各个阶段的模块也都增加了相应的接口,所以要修改OpenMIPS模块,以将新增接口与CTRL模块连接起来,连接关系如图7-10所示,具体代码不在书中列出,读者可以参考本书附带光盘Code\Chapter7_2目录下的openmips.v文件。
下一次将利用上面实现的流水线暂停机制实现乘累加、乘累减指令,敬请关注!- 自己动手写CPU之第七阶段(5)——流水线暂停机制的设计与实现
- 自己动手写CPU之第七阶段(7)——乘累加指令的实现
- 自己动手写CPU之第七阶段(2)——简单算术操作指令实现过程
- 自己动手写CPU之第七阶段(6)——乘累加指令实现思路
- 自己动手写CPU之第七阶段(9)——除法指令说明及实现思路
- 自己动手写CPU之第七阶段(10)——除法指令实现过程1
- 自己动手写CPU之第七阶段(11)——除法指令实现过程2
- 自己动手写CPU之第七阶段(12)——检验除法指令实现效果
- 自己动手写CPU之第七阶段(8)——验证乘累加指令的实现效果
- 自己动手写CPU之第五阶段(1)——流水线数据相关问题
- 自己动手写CPU之第五阶段(5)——测试逻辑、移位与空指令的实现
- 自己动手写CPU之第七阶段(3)——简单算术操作指令实现过程(续)
- 自己动手写CPU之第七阶段(4)——验证简单算术操作指令实现效果
- 自己动手写CPU之第七阶段(1)——简单算术操作指令说明
- 自己动手写CPU之第五阶段(4)——逻辑、移位与空指令的实现
- 自己动手写CPU之第六阶段(3)——移动操作指令的实现
- 自己动手写CPU之第九阶段(5)——实现加载存储指令1(修改译码阶段)
- 自己动手写CPU之第九阶段(5)——实现加载存储指令2(修改执行阶段)
- DICOM入门简介
- Objective-C中的日期格式器NSDateFormatter
- 【js报类型未定义的错误】20140826
- golang tcp 2 unix socket proxy
- synchronized用法
- 自己动手写CPU之第七阶段(5)——流水线暂停机制的设计与实现
- Android手机在开发调试时logcat不显示输出信息的解决办法
- NSAttributedString的用法
- C#学习笔记-委托
- SQL中left join和inner join配合使用
- 斯坦福大学公开课——傅里叶变换及其应用笔记
- Comparable和Comparator2个接口的作用和区别
- LeetCode 021. Merge Two Sorted Lists
- RSA算法