系列文章目录

(一)从零开始设计RISC-V处理器——指令系统
(二)从零开始设计RISC-V处理器——单周期处理器的设计
(三)从零开始设计RISC-V处理器——单周期处理器的仿真
(四)从零开始设计RISC-V处理器——ALU的优化
(五)从零开始设计RISC-V处理器——五级流水线之数据通路的设计
(六)从零开始设计RISC-V处理器——五级流水线之控制器的设计
(七)从零开始设计RISC-V处理器——五级流水线之数据冒险
(八)从零开始设计RISC-V处理器——五级流水线之控制冒险
(九)从零开始设计RISC-V处理器——五级流水线之分支计算前移
(十)从零开始设计RISC-V处理器——五级流水线之静态预测


文章目录

  • 系列文章目录
  • 前言
  • 一、控制冒险
  • 二、控制冒险之假设分支不发生
    • 1.冲刷流水线(flush)
    • 2.代码示例
  • 三、数据冒险之加载-使用型冒险
    • 1.停顿流水线(stall)
    • 2.代码示例
  • 四、仿真
    • 1.控制冒险仿真
    • 2.加载-使用型冒险仿真
  • 总结

前言

上一篇文章详细介绍了处理中的冒险,包括数据冒险,控制冒险和结构冒险,并且针对数据冒险进行分类,细分了5种情况,其中3种情况可以通过添加数据前递单元来解决,1种情况可以通过寄存器堆的前递机制进行解决,1种情况需要进行流水线的停顿。在上一篇文章中已经解决了数据冒险中的4种情况,本篇文章重点解决控制冒险以及数据冒险的最后一种情况。


一、控制冒险

上一篇文章中提到的控制冒险定义如下:
控制冒险,在遇到条件分支指令时,由于跳转条件是否成立在执行阶段才知道,因此流水线就需要停顿或者冲刷指令才能正确运行。
但是在实际设计的时候发现,对于我们现阶段的处理器来说,控制冒险不仅仅发生在“条件分支指令”中,对于无条件跳转指令,如jal,jalr,我们在解决控制冒险时也应当将其考虑在内,因为无条件跳转的目标地址同样在执行阶段才知道。
如果我们把无条件跳转指令的跳转地址在取指阶段进行计算,那么我们考虑控制冒险的时候便只需要考虑条件跳转指令了,这将会在最后一篇博文《从零开始设计RISC-V处理器——五级流水线之静态预测》中介绍。
下面再来详细分析一下控制冒险的现象(请大家自行画出时序图进行理解):
1.在第一个时钟周期的开始,取指阶段取出一条跳转指令。
2.在第二个时钟周期的开始,跳转指令开始译码,同时取指阶段取出一条新的指令Instr1。
3.在第三个时钟周期的开始,跳转指令进入执行阶段,同时取指阶段取出一条新的指令Instr2,在第三个时钟周期结束的时候,跳转的目标地址已经产生。
4.在第四个时钟周期的上升沿,取指令阶段即将取出跳转的目标地址对应的指令,此时Instr1即将进入执行阶段,Instr2即将进入译码阶段。

对于条件分支指令,如果跳转条件不成立,那么此时无需干预,如果跳转条件成立,则Instr1和Instr2是多余取出的指令。
对于无条件跳转指令,Instr1和Instr2同样是多余取出的指令。

那如何解决控制冒险呢?
1.最简单粗暴的方法,每次遇到跳转指令就将流水线停顿,等目标地址计算出来之后,再取新的指令。这无疑会大大降低流水线的运行速度。
2.总是假设不跳转,就像上面分析的,遇到分支指令之后,先正常的去取指令,如果分支不发生,则流水线正常运行,如果发生跳转,则冲刷掉多取的两条指令。这便是本篇文章要解决的方案。
3.仍然假设不跳转,但是将分支地址的计算前递到译码阶段,这样在分支目标地址结果出来之前,仅仅会多余的取出一条指令,那么如果发生跳转,则只需要冲刷掉多取的这一条指令。这是下一篇文章要解决的方案。
4.在第3种方案的基础上,加入“假设反向跳转,假设前向不跳转”的静态预测机制,这样,在预测正确的情况下,流水线能够全速运行,在预测错误的情况下,只需要冲刷掉一条指令。这是最后一篇文章要解决的方案。

二、控制冒险之假设分支不发生

1.冲刷流水线(flush)

当遇到控制冒险时,如过采用假设分支不发生的方法,当分支跳转发生时,就需要冲刷掉多余的两条指令,那么具体怎样实现冲刷呢
事实上,如果将控制信号置零,那么对应的指令也就无效了,处理器既不会访存,也不会写回寄存器堆。
正如上面分析的,在第四个时钟周期的上升沿,取指令阶段即将取出跳转的目标地址对应的指令,此时Instr1即将进入执行阶段,Instr2即将进入译码阶段。

此时,Instr1已经完成译码并且将控制信号输入到了ID/EX流水线寄存器。Instr1即将进入执行阶段,换一种说法就是,第四个时钟周期的上升沿,ID/EX流水线寄存器即将采集Instr1的控制信号,所以只需要在此时将ID/EX流水线寄存器中的控制信号置0而不采集Instr1的控制信号,即可实现对Instr1指令的冲刷。

此时,Instr2已经完成了取指令并且将指令输入到了IF/ID流水线寄存器。Instr2即将进入译码阶段,换一种说法就是,第四个时钟周期的上升沿,IF/ID流水线寄存器即将采集Instr2的32位二进制指令,所以只需要在此时将IF/ID流水线寄存器中的指令置0,即可实现对Instr1指令的冲刷。(指令全为0,即是一条空指令,译码出来的控制信号也是全0)

那么,如何判断控制冒险的到来呢,由于我们现阶段的处理器,把无条件跳转也考虑到了控制冒险中,所以只要发生跳转,就需要冲刷掉多余的两条指令,所以可以直接把前面设计的branch_judge模块的输出信号jump_flag当作发生控制冒险的信号。

2.代码示例

下面贴出关键代码,除此之外还需要修改顶层模块,请读者自行完成。

branch_judge模块代码如下:

module branch_judge(beq,bne,blt,bge,bltu,bgeu,jal,jalr,zero,ALU_result_sig,jump_flag);input beq;input bne;input blt;input bge;input bltu;input bgeu;input jal;input jalr;input zero;input ALU_result_sig;output jump_flag;assign jump_flag =    jal |jalr |(beq && zero)|(bne && (!zero))|(blt && ALU_result_sig)|(bge && (!ALU_result_sig))|(bltu && ALU_result_sig)|(bgeu && (!ALU_result_sig));endmodule

将IF/ID模块的代码修改如下:

`include "define.v"module if_id_regs(input clk,input rst_n,input jump_flag,input [31:0]pc_if_id_i,input [31:0]instr_if_id_i,output reg [31:0]pc_if_id_o,output reg [31:0]instr_if_id_o);always@(posedge clk or negedge rst_n)beginif(!rst_n)pc_if_id_o<=`zeroword;elsepc_if_id_o<=pc_if_id_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)instr_if_id_o<=`zeroword;else if(jump_flag)instr_if_id_o<=`zeroword;elseinstr_if_id_o<=instr_if_id_i;endendmodule

将ID/EX模块的代码修改如下:


`include "define.v"
module id_ex_regs(input clk,input rst_n,input jump_flag,input [31:0]pc_id_ex_i,input [31:0]imme_id_ex_i,input [31:0]Rd_data1_id_ex_i,input [31:0]Rd_data2_id_ex_i,input [4:0]Rd_id_ex_i,input [4:0]Rs1_id_ex_i,input [4:0]Rs2_id_ex_i,output reg [31:0]pc_id_ex_o,output reg [31:0]imme_id_ex_o,output reg [31:0]Rd_data1_id_ex_o,output reg [31:0]Rd_data2_id_ex_o,output reg [4:0]Rd_id_ex_o,output reg [4:0]Rs1_id_ex_o,output reg [4:0]Rs2_id_ex_o,//control signalsinput ALUSrc_id_ex_i,input [3:0]ALUctl_id_ex_i,input beq_id_ex_i,input bne_id_ex_i,input blt_id_ex_i,input bge_id_ex_i,input bltu_id_ex_i,input bgeu_id_ex_i,input jal_id_ex_i,input jalr_id_ex_i,input MemRead_id_ex_i,input MemWrite_id_ex_i,input [2:0]RW_type_id_ex_i,input lui_id_ex_i,input U_type_id_ex_i,input MemtoReg_id_ex_i,input RegWrite_id_ex_i,output  reg ALUSrc_id_ex_o,output  reg [3:0]ALUctl_id_ex_o,output  reg beq_id_ex_o,output  reg bne_id_ex_o,output  reg blt_id_ex_o,output  reg bge_id_ex_o,output  reg bltu_id_ex_o,output  reg bgeu_id_ex_o,output  reg jal_id_ex_o,output  reg jalr_id_ex_o,output  reg MemRead_id_ex_o,output  reg MemWrite_id_ex_o,output  reg [2:0]RW_type_id_ex_o,output  reg lui_id_ex_o,output  reg U_type_id_ex_o,output  reg MemtoReg_id_ex_o,output  reg RegWrite_id_ex_o);always@(posedge clk or negedge rst_n)beginif(!rst_n)pc_id_ex_o<=`zeroword;elsepc_id_ex_o<=pc_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)imme_id_ex_o<=`zeroword;elseimme_id_ex_o<=imme_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)Rd_data1_id_ex_o<=`zeroword;elseRd_data1_id_ex_o<=Rd_data1_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)Rd_data2_id_ex_o<=`zeroword;elseRd_data2_id_ex_o<=Rd_data2_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)Rd_id_ex_o<=5'd0;elseRd_id_ex_o<=Rd_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)Rs1_id_ex_o<=5'd0;elseRs1_id_ex_o<=Rs1_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)Rs2_id_ex_o<=5'd0;elseRs2_id_ex_o<=Rs2_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n )ALUSrc_id_ex_o<=`zero;else if(jump_flag)ALUSrc_id_ex_o<=`zero;elseALUSrc_id_ex_o<=ALUSrc_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)ALUctl_id_ex_o<=4'b0000;else if(jump_flag)ALUctl_id_ex_o<=4'b0000;elseALUctl_id_ex_o<=ALUctl_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)beq_id_ex_o<=`zero;else if(jump_flag)beq_id_ex_o<=`zero;elsebeq_id_ex_o<=beq_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)bne_id_ex_o<=`zero;else if(jump_flag)bne_id_ex_o<=`zero;elsebne_id_ex_o<=bne_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)blt_id_ex_o<=`zero;else if(jump_flag)blt_id_ex_o<=`zero;elseblt_id_ex_o<=blt_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)bge_id_ex_o<=`zero;else if(jump_flag)bge_id_ex_o<=`zero;elsebge_id_ex_o<=bge_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)bltu_id_ex_o<=`zero;else if(jump_flag)bltu_id_ex_o<=`zero;elsebltu_id_ex_o<=bltu_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)bgeu_id_ex_o<=`zero;else if(jump_flag)bgeu_id_ex_o<=`zero;elsebgeu_id_ex_o<=bgeu_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)jal_id_ex_o<=`zero;else if(jump_flag)jal_id_ex_o<=`zero;elsejal_id_ex_o<=jal_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)jalr_id_ex_o<=`zero;else if(jump_flag)jalr_id_ex_o<=`zero;elsejalr_id_ex_o<=jalr_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)MemRead_id_ex_o<=`zero;else if(jump_flag)MemRead_id_ex_o<=`zero;elseMemRead_id_ex_o<=MemRead_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)MemWrite_id_ex_o<=`zero;else if(jump_flag)MemWrite_id_ex_o<=`zero;elseMemWrite_id_ex_o<=MemWrite_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)RW_type_id_ex_o<=3'b000;else if(jump_flag)RW_type_id_ex_o<=3'b000;elseRW_type_id_ex_o<=RW_type_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)lui_id_ex_o<=`zero;else if(jump_flag)lui_id_ex_o<=`zero;elselui_id_ex_o<=lui_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)U_type_id_ex_o<=`zero;else if(jump_flag )U_type_id_ex_o<=`zero;elseU_type_id_ex_o<=U_type_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)MemtoReg_id_ex_o<=`zero;else if(jump_flag)MemtoReg_id_ex_o<=`zero;elseMemtoReg_id_ex_o<=MemtoReg_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)RegWrite_id_ex_o<=`zero;else if(jump_flag)RegWrite_id_ex_o<=`zero;elseRegWrite_id_ex_o<=RegWrite_id_ex_i;endendmodule

三、数据冒险之加载-使用型冒险

1.停顿流水线(stall)

上一篇文章中已经详细介绍了加载-使用型冒险,最后得出的结论是,需要对流水线进行停顿一个周期。

下面再来详细分析一下加载-使用型冒险时流水线的运行情况(请大家自行画出时序图进行理解):
1.在第一个时钟周期的开始,取指阶段取出一条load指令。
2.在第二个时钟周期的开始,load指令开始译码,同时取指阶段取出一条新的指令Instr1。
3.在第三个时钟周期的开始,load指令进入执行阶段,Instr1开始译码,同时取指阶段取出一条新的指令Instr2。在此周期内便可判断出加载-使用型冒险。
4.在第四个时钟周期的上升沿,Instr1即将进入执行阶段,Instr2即将进入译码阶段,同时取指阶段即将取出Instr3。

此时我们想要在Instr1和load指令之间插入一条空指令,即Instr1不能进入执行阶段,那么按照控制冒险的冲刷指令的思路,只需要将ID/EX流水线寄存器的控制信号清零即可。
同时,我们不想冲刷掉Instr1和Instr2,而希望两条指令在插入的nop之后继续执行,那么对于IF/ID流水线寄存器来说,我们希望他不采集Instr2的32位指令信号而是保持Instr1的32位指令信号,对于取指部件来说,我们不希望他即将取出Instr3,而希望他再次取一条Instr2,所以要让pc信号保持而不更新。

以上就是停顿流水线的方法。

总结一下,在执行load指令阶段,判断出加载-使用型冒险并产生一个标志信号,在下一个时钟周期的上升沿采集到这个信号,然后清除ID/EX流水线寄存器的控制信号,暂停IF/ID流水线寄存器的更新,暂pc的更新。

2.代码示例

判断出加载-使用型冒险代码如下:


//以上就是我们面临的五种冒险的分析,简单总结如下:
//a.在一个周期开始,EX 阶段要使用上一条处在 EX 阶段指令的执行结果,此时我们将 EX/MEM 寄存器的数据前递。
//b.在一个周期开始,EX 阶段要使用上一条处在 MEM 阶段指令的执行结果,此时我们将 MEM/WB 寄存器的数据前递。
//c.在一个周期开始,EX 阶段要使用上一条处在 WB 阶段指令的执行结果,此时不需要前递(寄存器堆前递机制)
//d.在第一种情况下,如果是上一条是访存指令,即发生加载—使用型冒险。则需要停顿一个周期。
//e.在发生加载——使用型冒险的时候,如果是load后跟着store指令,并且load指令的rd与store指令的rs1 不同而与rs2相同,则不需要停顿,只需要将MEM/WB 寄存器的数据前递到MEM阶段。module forward_unit(input [4:0]Rs1_id_ex_o,input [4:0]Rs2_id_ex_o,input [4:0]Rd_ex_mem_o,input [4:0]Rd_mem_wb_o,input RegWrite_ex_mem_o,input RegWrite_mem_wb_o,input MemWrite_id_ex_o,input MemRead_ex_mem_o,output  [1:0]forwardA,output  [1:0]forwardB,output forwardC,input [4:0]Rs1_id_ex_i,input [4:0]Rs2_id_ex_i,input [4:0]Rd_id_ex_o,input MemRead_id_ex_o,input MemWrite_id_ex_i,input RegWrite_id_ex_o,output load_use_flag);assign forwardA[1]=(RegWrite_ex_mem_o &&(Rd_ex_mem_o!=5'd0)&&(Rd_ex_mem_o==Rs1_id_ex_o));assign forwardA[0]=(RegWrite_mem_wb_o && (Rd_mem_wb_o !=5'd0) &&(Rd_mem_wb_o==Rs1_id_ex_o));assign forwardB[1]=(RegWrite_ex_mem_o &&(Rd_ex_mem_o!=5'd0)&&(Rd_ex_mem_o==Rs2_id_ex_o));assign forwardB[0]=(RegWrite_mem_wb_o && (Rd_mem_wb_o !=5'd0) &&(Rd_mem_wb_o==Rs2_id_ex_o));//load后紧跟sw但是不需要停顿assign forwardC=(RegWrite_ex_mem_o &&(Rd_ex_mem_o!=5'd0)&&(Rd_ex_mem_o!=Rs1_id_ex_o)&& (Rd_ex_mem_o==Rs2_id_ex_o)&& MemWrite_id_ex_o && MemRead_ex_mem_o );//load-useassign load_use_flag=  MemRead_id_ex_o & RegWrite_id_ex_o & (Rd_id_ex_o!=5'd0)   //load&(!MemWrite_id_ex_i)     //非store& ((Rd_id_ex_o ==Rs1_id_ex_i) | (Rd_id_ex_o ==Rs2_id_ex_i))|MemRead_id_ex_o & RegWrite_id_ex_o & (Rd_id_ex_o!=5'd0)     //load&(MemWrite_id_ex_i)     //store& (Rd_id_ex_o ==Rs1_id_ex_i);endmodule

ID/EX流水线寄存器修改代码如下:

`include "define.v"
module id_ex_regs(input clk,input rst_n,input jump_flag,input [31:0]pc_id_ex_i,input [31:0]imme_id_ex_i,input [31:0]Rd_data1_id_ex_i,input [31:0]Rd_data2_id_ex_i,input [4:0]Rd_id_ex_i,input [4:0]Rs1_id_ex_i,input [4:0]Rs2_id_ex_i,output reg [31:0]pc_id_ex_o,output reg [31:0]imme_id_ex_o,output reg [31:0]Rd_data1_id_ex_o,output reg [31:0]Rd_data2_id_ex_o,output reg [4:0]Rd_id_ex_o,output reg [4:0]Rs1_id_ex_o,output reg [4:0]Rs2_id_ex_o,//control signalsinput ALUSrc_id_ex_i,input [3:0]ALUctl_id_ex_i,input beq_id_ex_i,input bne_id_ex_i,input blt_id_ex_i,input bge_id_ex_i,input bltu_id_ex_i,input bgeu_id_ex_i,input jal_id_ex_i,input jalr_id_ex_i,input MemRead_id_ex_i,input MemWrite_id_ex_i,input [2:0]RW_type_id_ex_i,input lui_id_ex_i,input U_type_id_ex_i,input MemtoReg_id_ex_i,input RegWrite_id_ex_i,output  reg ALUSrc_id_ex_o,output  reg [3:0]ALUctl_id_ex_o,output  reg beq_id_ex_o,output  reg bne_id_ex_o,output  reg blt_id_ex_o,output  reg bge_id_ex_o,output  reg bltu_id_ex_o,output  reg bgeu_id_ex_o,output  reg jal_id_ex_o,output  reg jalr_id_ex_o,output  reg MemRead_id_ex_o,output  reg MemWrite_id_ex_o,output  reg [2:0]RW_type_id_ex_o,output  reg lui_id_ex_o,output  reg U_type_id_ex_o,output  reg MemtoReg_id_ex_o,output  reg RegWrite_id_ex_o,input load_use_flag);always@(posedge clk or negedge rst_n)beginif(!rst_n)pc_id_ex_o<=`zeroword;elsepc_id_ex_o<=pc_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)imme_id_ex_o<=`zeroword;elseimme_id_ex_o<=imme_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)Rd_data1_id_ex_o<=`zeroword;elseRd_data1_id_ex_o<=Rd_data1_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)Rd_data2_id_ex_o<=`zeroword;elseRd_data2_id_ex_o<=Rd_data2_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)Rd_id_ex_o<=5'd0;elseRd_id_ex_o<=Rd_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)Rs1_id_ex_o<=5'd0;elseRs1_id_ex_o<=Rs1_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)Rs2_id_ex_o<=5'd0;elseRs2_id_ex_o<=Rs2_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n )ALUSrc_id_ex_o<=`zero;else if(jump_flag | load_use_flag)ALUSrc_id_ex_o<=`zero;elseALUSrc_id_ex_o<=ALUSrc_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)ALUctl_id_ex_o<=4'b0000;else if(jump_flag | load_use_flag)ALUctl_id_ex_o<=4'b0000;elseALUctl_id_ex_o<=ALUctl_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)beq_id_ex_o<=`zero;else if(jump_flag | load_use_flag)beq_id_ex_o<=`zero;elsebeq_id_ex_o<=beq_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)bne_id_ex_o<=`zero;else if(jump_flag | load_use_flag)bne_id_ex_o<=`zero;elsebne_id_ex_o<=bne_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)blt_id_ex_o<=`zero;else if(jump_flag | load_use_flag)blt_id_ex_o<=`zero;elseblt_id_ex_o<=blt_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)bge_id_ex_o<=`zero;else if(jump_flag | load_use_flag)bge_id_ex_o<=`zero;elsebge_id_ex_o<=bge_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)bltu_id_ex_o<=`zero;else if(jump_flag | load_use_flag)bltu_id_ex_o<=`zero;elsebltu_id_ex_o<=bltu_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)bgeu_id_ex_o<=`zero;else if(jump_flag | load_use_flag)bgeu_id_ex_o<=`zero;elsebgeu_id_ex_o<=bgeu_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)jal_id_ex_o<=`zero;else if(jump_flag | load_use_flag)jal_id_ex_o<=`zero;elsejal_id_ex_o<=jal_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)jalr_id_ex_o<=`zero;else if(jump_flag | load_use_flag)jalr_id_ex_o<=`zero;elsejalr_id_ex_o<=jalr_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)MemRead_id_ex_o<=`zero;else if(jump_flag | load_use_flag)MemRead_id_ex_o<=`zero;elseMemRead_id_ex_o<=MemRead_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)MemWrite_id_ex_o<=`zero;else if(jump_flag | load_use_flag)MemWrite_id_ex_o<=`zero;elseMemWrite_id_ex_o<=MemWrite_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)RW_type_id_ex_o<=3'b000;else if(jump_flag | load_use_flag)RW_type_id_ex_o<=3'b000;elseRW_type_id_ex_o<=RW_type_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)lui_id_ex_o<=`zero;else if(jump_flag | load_use_flag)lui_id_ex_o<=`zero;elselui_id_ex_o<=lui_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)U_type_id_ex_o<=`zero;else if(jump_flag | load_use_flag)U_type_id_ex_o<=`zero;elseU_type_id_ex_o<=U_type_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)MemtoReg_id_ex_o<=`zero;else if(jump_flag | load_use_flag)MemtoReg_id_ex_o<=`zero;elseMemtoReg_id_ex_o<=MemtoReg_id_ex_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)RegWrite_id_ex_o<=`zero;else if(jump_flag | load_use_flag)RegWrite_id_ex_o<=`zero;elseRegWrite_id_ex_o<=RegWrite_id_ex_i;endendmodule

IF/ID流水线寄存器代码修改代码如下:


`include "define.v"module if_id_regs(input clk,input rst_n,input jump_flag,input [31:0]pc_if_id_i,input [31:0]instr_if_id_i,output reg [31:0]pc_if_id_o,output reg [31:0]instr_if_id_o,input load_use_flag);always@(posedge clk or negedge rst_n)beginif(!rst_n)pc_if_id_o<=`zeroword;elsepc_if_id_o<=pc_if_id_i;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)instr_if_id_o<=`zeroword;else if(jump_flag)instr_if_id_o<=`zeroword;else if(load_use_flag)instr_if_id_o<=instr_if_id_o;elseinstr_if_id_o<=instr_if_id_i;endendmodule

pc寄存器修改代码如下:


`include "define.v"
module pc_reg(clk,rst_n,pc_new,pc_out,load_use_flag);input clk;input rst_n;input [31:0]pc_new;input load_use_flag;output reg [31:0]pc_out;always@(posedge clk or negedge rst_n)beginif(!rst_n)pc_out<=`zeroword;else if(load_use_flag)pc_out<=pc_out;elsepc_out<=pc_new;end   endmodule

四、仿真

1.控制冒险仿真

1.第一组指令

addi x1,x0,1
addi x2,x0,1
beq x1,x2,label0
addi x3,x0,3
addi x4,x0,4
label0:
addi x5,x0,5
bne x1,x5,label1
addi x6,x0,6
addi x7,x0,7
label1:
addi x8,x0,8
jal x31,label2
addi x9,x0,9
addi x10,x0,10
label2:
addi x11,x0,11
beq x1,x3,label3
addi x12,x0,12
addi x13,x0,13
label3:
addi x14,x0,14

仿真波形如下:


可以看到最终运行结果完全正确,能够正常冲刷指令。

2.第二组指令

addi x1,x0,1
addi x2,x0,2
jal x31,label1
addi x3,x0,3
label1:
addi x4,x0,4
add x5,x2,x2
beq x4,x5,label2
addi x6,x0,6
label2:
bne x4,x5,label3
addi x7,x0,7
label3:
bne x7,x6,label4
addi x8,x0,8
label4:
addi x9,x0,0x30
jalr x10,x9,12
addi x11,x0,11
addi x12,x0,-12
addi x13,x0,-13
blt x13,x12,label5
addi x14,x0,-14
label5:
bltu x13,x12,label6
addi x15,x0,-15
label6:
bltu x12,x13,label7
addi x16,x0,-16
label7:
bge x12,x13,label8
addi x17,x0,-17
label8:
bge x1,x2,label9
addi x18,x0,-18
label9:
bgeu x12,x13,label10
addi x19,x0,-19
label10:
bgeu x13,x12,label11
addi x20,x0,-20
label11:
addi x21,x0,-20
addi x22,x0,-20
bge x21,x22,label12
addi x23,x0,-23
label12:
addi x24,x0,-24

在仿真器中运行结果如下:


仿真波形如下:


观察仿真波形,可以发现,只有一个寄存器的值与仿真器的结果不一样,那就是X10,对应的指令为jalr x10,x9,12,但是此处能够正常跳转,经过分析发现,这里错误的原因是,由于jalr指令是将当前的pc加4的值写回寄存器,但是由于流水线机制,我们错误的把取指阶段的pc加4写回了寄存器,正确的结果应该是把执行阶段的pc加4写回寄存器,所以会出现错误的结果64,比正确结果56,正好大8(对应两条指令),在执行阶段修改代码之后再次仿真,结果完全正确。

2.加载-使用型冒险仿真

测试指令

lw x1,0,x0
addi x2,x1,1
addi x3,x1,2
addi x4,x1,3

仿真波形如下:


可以看到,在load_use_flag信号拉高后,处理器进行了一个周期的停顿,并且执行结果完全正确。

总结

以上就是对于流水线冲刷和流水线停顿的处理方法,关于数据冒险,到这里已经全部解决,而控制冒险目前采用的方法是假设分支不发生,这种情况下每次跳转都要冲刷掉两条指令,下一篇文章我们将把分支地址计算前移到译码阶段,提前得到跳转的目标地址,这样在发生跳转时仅仅需要冲刷一条指令。

从零开始设计RISC-V处理器——五级流水线之控制冒险相关推荐

  1. 处理器指令编码可重定义的方法_从零开始设计四位栈处理器(2)——结构与指令集...

    从零设计四位栈处理器(2)--结构与指令集 一句话概括: 在Toxic处理器中,万物皆栈. 熟悉汇编语言的同学会了解,一般的汇编语言,会包含以下几个部分: 寄存器 地址 立即数 操作码 在这期文章中, ...

  2. 从零开始设计RISC-V处理器——单周期处理器的设计

    系列文章目录 (一)从零开始设计RISC-V处理器--指令系统 (二)从零开始设计RISC-V处理器--单周期处理器的设计 (三)从零开始设计RISC-V处理器--单周期处理器的仿真 (四)从零开始设 ...

  3. mips的旁路_64位MIPS指令处理器的流水线设计

    1 引言 随着集成电路设计和工艺技术的发展,嵌入式系统(SOC)已经在PDA.机顶盒.手机等信息终端中被广泛应用.他不仅减小了电路尺寸,而且具有成本低廉,可靠性高,功耗低等优点.可以说嵌入式系统是未来 ...

  4. 从零开始实现一个基于RISC-V的流水线处理器 (1) :RISC-V指令集架构详解

    目录 基于RISC-V的流水线处理器 RISC-V指令集 RV32I R-Type I-Type J-Type B-Type Load & Store 总结 后记 基于RISC-V的流水线处理 ...

  5. 基于MIPS的五级流水线微处理器(CPU)设计、modelsim仿真通过、verilog编写

    基于MIPS的五级流水线微处理器(CPU)设计 摘要 本设计为一个五级流水线CPU,此CPU结构为MIPS结构.流水线CPU与单周期和多周期CPU相比较,提高了指令的执行速度,改善了CPU的整体吞吐率 ...

  6. [SystemVerilog] MIPS架构下的五级流水线CPU设计

    完整代码已上传 github 众所周知,MIPS体系的五级流水线CPU分为五个阶段:取指(IF).译码(ID).执行(EX).存储器(MEM).写回(WB).所以这根本算不上"设计" ...

  7. 计算机组成原理笔记--简单五级流水线设计与性能

    文章目录 前言 五级流水线 冒险/冲突(Hazards/Conflict) 控制单元 模块调度 系列目录 前言 上一章 单周期设计与多周期设计 一章中提到了两种微架构模型.这一章主要会讲解基本流水线的 ...

  8. 设计一个简易的处理器(8)--流水线冒险的避免

    上一篇已经介绍了流水线的相关和冒险,本篇介绍避免流水线冒险几种技术. 暂停技术(Stalling)避免数据冒险 ---- 通过上一篇的介绍,PIPELINE的数据冒险只发生在读/取同一个程序寄存器的时 ...

  9. 这本读者期待的芯片书《手把手教你设计CPU——RISC-V处理器》终于出版!

    点击关注异步图书,置顶公众号 每天与你分享IT好书 技术干货 职场知识 ​ ​点击图片购书​ 参与文末话题讨论,每日赠送异步图书 --异步小编 在摩尔定律减缓的今天,一味比拼硬件性能的技术竞赛变得越发 ...

  10. MIPS 五级流水线

    (本文为一个期末考试题,文中的图部分摘引自(美)David.patterson <<计算机结构与组成>>一书 第一章 32 位单周期RISC处理器设计 要设计一款处理器,首先要 ...

最新文章

  1. 论一枚数据科学家的自我修养
  2. 两种控制器的跳转与回跳
  3. Grails with ATS Transformation tutorial with a demo example
  4. 【c++】14.编译proto和proto相关用法
  5. Go使用go-clickhouse库
  6. 使用代码获得document的related document 列表
  7. 【LeetCode】剑指 Offer 41. 数据流中的中位数
  8. 寒武纪讯飞京东等合搞AI芯片评测标准,作者包括陈云霁陈天石
  9. 国外巨头鏖战的新领域——语音识别
  10. 事务的acid属性是指_Mysql事务的性质 为什么要用事务?
  11. 数字双极点低通滤波器-二阶巴特沃斯滤波器
  12. 怎样修改用户的计算机配置文件,用户配置文件
  13. opencv-pythons实现图像周长面积(三角形)检测DIY整理
  14. 基于canvas+uniapp的9宫格拼图游戏组件
  15. 在百度地图上展示dwg/dxf
  16. vue鼠标悬停更改图片
  17. 从硬件分析推挽输出和开漏输出详细区别
  18. 02 MSC类设备-基础篇(二)
  19. N76E003合并boot和app
  20. Git+码云+IDEA联合开发(附图详细步骤)

热门文章

  1. 【crossbeam系列】3 crossbeam-deque:work-stealing算法
  2. 现代汉语词典第五版_瑜 典 寻 瑕——第五版《现代汉语词典》的瑕疵(周克庸原创)...
  3. Windows 10免费升级
  4. 云杰恒指:8.19恒指期货仓位管理---交易复盘
  5. 转使用chrome命令行:disable
  6. matlab实现多目标测试集ZDT、DTLZ、MOP的最优理论前沿
  7. MD5在线加密,带盐
  8. 杭电 1242 Rescue
  9. 用“狗屁不通文章生成器”写作文,竟打败73.5%的学生?
  10. C++后台开发面试常考