系列文章目录

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



前言

前面几篇文章已经描述了设计一个单周期的CPU的完整过程以及代码。从这篇文章开始,我们逐步对这个CPU进行优化。今天首先进行五级流水线的数据通路的设计。


一、数据通路的划分

回顾以下单周期的处理器的数据通路图:
在这里插入图片描述
所谓单周期处理器,就是指处理器执行每一条指令都在一个时钟周期内完成。由于不同指令的数据通路不同,执行不同的指令做需要的时间也不同,单周期处理器必须保证所花时间最长的那一条指令能够在一个时钟周期内执行完毕。因此这就限制了时钟周期的大小。
流水线的思想,就是将一个很大的组合逻辑拆分成几个比较小的组合逻辑,使得这几个小的模块能够并行执行,通过增加数据吞吐量来提升执行效率。
在这里我们将处理器执行一条指令的过程拆分为取指,译码,执行,访存,写回五个阶段。
在这里插入图片描述

这相当于将一个很大的组合逻辑拆分成五个比较小的组合逻辑,只需要保证每个小模块能够在一个时钟周期内执行完毕。因此可以一定程度上缩小CPU时钟周期,即提升CPU的频率。
既然要进行拆分,就不是简单的将几个模块进行重组打包,而是要彻底的打断各个子模块之间的“路径”,同时各个子模块之间原有的信号流通不能改变。
所以需要在两个子模块之间添加一组流水线寄存器。
其作用有二:
(1)用寄存器打断组合逻辑
(2)进行信号在两个子模块之间的传递
其信号传递关系如下:
在这里插入图片描述

综上所述,进行五级流水线的数据通路的设计,首先需要进行模块的划分,然后在每两个模块之间添加流水线寄存器。下面进行详细描述。

二、流水线设计

1.取指阶段(IF_stage)

取指阶段的功能是产生新的pc值,并读取指令存储器。因此取指阶段包括pc_reg和instr_memory两个子模块。此处 instr_memory为一个存储器,将其当作CPU外部的部件而不将其写入if_stage阶段。

代码如下:

module if_stage(
	input clk,
	input rst_n,
	input [31:0]pc_if_i,
	output [31:0]pc_if_o,
	output [7:0]rom_addr

    );

	pc_reg pc_reg_inst (
    .clk(clk), 
    .rst_n(rst_n), 
    .pc_new(pc_if_i), 
    .pc_out(pc_if_o)
    );
	
	assign rom_addr=pc_if_o[9:2];

endmodule


2.IF_ID流水线寄存器

此模块传递两个信号,分别是pc和读出的32位的指令。
pc为什么向后传递?因为在执行阶段计算下一个pc的值以及跳转指令的目标地址时,都需要用到当前模块的pc,所以pc需要逐级传递,保证pc与所在阶段执行的指令是一一对应的。

代码如下:

`include "define.v"

module if_id_regs(
	input clk,
	input rst_n,
	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)
	begin
		if(!rst_n)
			pc_if_id_o<=`zeroword;
		else
			pc_if_id_o<=pc_if_id_i;
	end
	
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			instr_if_id_o<=`zeroword;
		else
			instr_if_id_o<=instr_if_id_i;
	end

endmodule


3.译码阶段(ID_stage)

译码阶段的功能是将上一级传递的32位指令进行译码并且读取寄存器堆。
所以此模块包含instr_decode和registers两个子模块。

代码如下:

module id_stage(
	input clk,
	input rst_n,
	input RegWrite_id_i,
	input [31:0]Wr_reg_data_id_i,
	input [31:0] instr_id_i,
	output [6:0] opcode_id_o,
	output [2:0] func3_id_o,
	output func7_id_o,
	output [31:0] imme_id_o,
	output [31:0] Rd_data1_id_o,
	output [31:0] Rd_data2_id_o

    );

	wire [4:0]Rs1;
	wire [4:0]Rs2;
	wire [4:0]Rd;
	
	
	instr_decode instr_decode_inst (
    .instr(instr_id_i), 
    .opcode(opcode_id_o), 
    .func3(func3_id_o), 
    .func7(func7_id_o), 
    .Rs1(Rs1), 
    .Rs2(Rs2), 
    .Rd(Rd), 
    .imme(imme_id_o)
    );
	
    registers registers_inst (
    .clk(clk), 
	.rst_n(rst_n), 
    .W_en(RegWrite_id_i), 
    .Rs1(Rs1), 
    .Rs2(Rs2), 
    .Rd(Rd), 
    .Wr_data(Wr_reg_data_id_i), 
    .Rd_data1(Rd_data1_id_o), 
    .Rd_data2(Rd_data2_id_o)
    );
endmodule


4.ID_EX流水线寄存器

此模块传递四个信号,pc,imme,Rd_data1,Rd_data2。

代码如下:

`include "define.v"
module id_ex_regs(
	input clk,
	input rst_n,
	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,
	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,
    );

always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			pc_id_ex_o<=`zeroword;
		else
			pc_id_ex_o<=pc_id_ex_i;
	end
	
always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			imme_id_ex_o<=`zeroword;
		else
			imme_id_ex_o<=imme_id_ex_i;
	end
	
always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			Rd_data1_id_ex_o<=`zeroword;
		else
			Rd_data1_id_ex_o<=Rd_data1_id_ex_i;
	end

always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			Rd_data2_id_ex_o<=`zeroword;
		else
			Rd_data2_id_ex_o<=Rd_data2_id_ex_i;
	end
	
	
	
	

endmodule


5.执行阶段(EX_stage)

执行阶段的功能是执行ALU的计算并且计算新的pc的值。
所以包含alu模块,分支判断模块,两个加法器(pc+4,pc+imme),三个选择器(alu数据来源选择,pc顺序执行与pc跳转选择,jalr选择)。

代码如下:

module ex_stage(
	input ALUctl_ex_i,
	input beq_ex_i,
	input bne_ex_i,
	input blt_ex_i,
	input bge_ex_i,
	input bltu_ex_i,
	input bgeu_ex_i,
	input jal_ex_i,
	input jalr_ex_i,
	input ALUSrc_ex_i,
	input [31:0]pc_ex_i,
	input [31:0]imme_ex_i,
	input [31:0]Rd_data1_ex_i,
	input [31:0]Rd_data2_ex_i,
	output [31:0]ALU_result_ex_o,
	output [31:0]pc_new_ex_o,
	output [31:0]pc_jump_o,
	output [31:0]Rd_data2_ex_o,
	output [31:0]imme_ex_o,
	output [31:0]pc_order_ex_o
    );

	wire [31:0]ALU_DB;
	wire zero;
	wire ALU_result_sig;
	wire jump_flag;
	wire [31:0]pc_order;
	wire [31:0]pc_jump_order;
	wire pc_jalr;
	
	
	assign pc_jalr={ALU_result[31:1],1'b0};
	assign ALU_result_sig=ALU_result[31];
	assign imme_ex_o=imme_ex_i;
	assign pc_order_ex_o=pc_order;
	
	alu alu_inst (
    .ALU_DA(Rd_data1_ex_i), 
    .ALU_DB(ALU_DB), 
    .ALU_CTL(ALUctl_ex_i), 
    .ALU_ZERO(zero), 
    .ALU_OverFlow(), 
    .ALU_DC(ALU_result_ex_o)
    );

	branch_judge branch_judge_inst (
    .beq(beq_ex_i,), 
    .bne(bne_ex_i,), 
    .blt(blt_ex_i,), 
    .bge(bge_ex_i,), 
    .bltu(bltu_ex_i,), 
    .bgeu(bgeu_ex_i,), 
    .jal(jal_ex_i,), 
    .jalr(jalr_ex_i,), 
    .zero(zero), 
    .ALU_result_sig(ALU_result_sig), 
    .jump_flag(jump_flag)
    );

///pc+4	
	cla_adder32 pc_adder_4 (
    .A(pc_ex_i), 
    .B(32'd4), 
    .cin(1'd0), 
    .result(pc_order), 
    .cout()
    );
	
///pc+imme
	cla_adder32 pc_adder_imme (
    .A(pc_ex_i), 
    .B(imme_ex_i), 
    .cin(1'd0), 
    .result(pc_jump_o), 
    .cout()
    );

///pc_sel
	mux pc_mux (
    .data1(pc_jump_o), 
    .data2(pc_order), 
    .sel(jump_flag), 
    .dout(pc_jump_order)
    );
///pc_jalr
	mux pc_jalr_mux (
    .data1(pc_jalr), 
    .data2(pc_jump_order), 
    .sel(jalr_ex_i), 
    .dout(pc_new_ex_o)
    );

///ALUdata_sel	
	mux ALU_data_mux (
    .data1(imme_ex_i), 
    .data2(Rd_data2_ex_i), 
    .sel(ALUSrc_ex_i), 
    .dout(ALU_DB)
    );
	
	
	
endmodule


6.EX_MEM流水线寄存器

此模块传递ALU_result,pc_jump,Rd_data2,imme,pc_order五个模块。
ALU_result将在访存阶段作为访存的地址使用,在写回阶段作为运算结果写回寄存器。
pc_jump,pc_order在写回阶段写回寄存器。
Rd_data2在访存阶段作为写入存储器的数据使用。
imme在写回阶段使用。

代码如下:

`include "define.v"
module ex_mem_regs(
	input clk,
	input rst_n,
	input [31:0]ALU_result_ex_mem_i,
	input [31:0]pc_jump_ex_mem_i,
	input [31:0]Rd_data2_ex_mem_i,
	input [31:0]imme_ex_mem_i,
	input [31:0]pc_order_ex_mem_i,
	output [31:0]ALU_result_ex_mem_o,   
	output [31:0]pc_jump_ex_mem_o,
	output [31:0]Rd_data2_ex_mem_o,     //DM
	output [31:0]imme_ex_mem_o,
	output [31:0]pc_order_ex_mem_o,
    );

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			ALU_result_ex_mem_o<=`zeroword;
		else
			ALU_result_ex_mem_o<=ALU_result_ex_mem_i;
	end
	
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			pc_jump_ex_mem_o<=`zeroword;
		else
			pc_jump_ex_mem_o<=pc_jump_ex_mem_i;
	end
	
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			Rd_data2_ex_mem_o<=`zeroword;
		else
			Rd_data2_ex_mem_o<=Rd_data2_ex_mem_i;
	end
	
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			imme_ex_mem_o<=`zeroword;
		else
			imme_ex_mem_o<=imme_ex_mem_i;
	end
	
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			pc_order_ex_mem_o<=`zeroword;
		else
			pc_order_ex_mem_o<=pc_order_ex_mem_i;
	end
	
	

endmodule


7.访存阶段(MEM_stage)

访存阶段与取指阶段类似,都是访问外部存储器,在这里将存储器与CPU进行分开,所以此阶段只需要输出地址与数据给数据存储器,并且接收数据存储器读出的数据,此部分代码在CPU的顶层模块体现。

8.MEM_WB流水线寄存器

此模块传递5个信号,均在写回阶段使用。loaddata为访存阶段新产生的数据,来自于读数据存储器。

代码如下:

`include "define.v"
module mem_wb_regs(
	input [31:0]ALU_result_mem_wb_i,   
	input [31:0]pc_jump_mem_wb_i,
	input [31:0]loaddata_mem_wb_i,     //DM
	input [31:0]imme_mem_wb_i,
	input [31:0]pc_order_mem_wb_i,
	output reg [31:0]ALU_result_mem_wb_o,   
	output reg [31:0]pc_jump_mem_wb_o,
	output reg [31:0]loaddata_mem_wb_o,     //DM
	output reg [31:0]imme_mem_wb_o,
	output reg [31:0]pc_order_mem_wb_o,
    );
	
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			ALU_result_mem_wb_o<=`zeroword;
		else
			ALU_result_mem_wb_o<=ALU_result_mem_wb_i;
	end
	
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			pc_jump_mem_wb_o<=`zeroword;
		else
			pc_jump_mem_wb_o<=pc_jump_mem_wb_i;
	end
	
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			loaddata_mem_wb_o<=`zeroword;
		else
			loaddata_mem_wb_o<=loaddata_mem_wb_i;
	end
	
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			imme_mem_wb_o<=`zeroword;
		else
			imme_mem_wb_o<=imme_mem_wb_i;
	end
	
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			pc_order_mem_wb_o<=`zeroword;
		else
			pc_order_mem_wb_o<pc_order_mem_wb_i;
	end

endmodule


9.写回阶段(WB_stage)

写回阶段包括4个选择器,最终输出一个数据写回寄存器堆。

代码如下:


module wb_stage(
	input MemtoReg,
	input jal,
	input jalr,
	input lui,
	input U_type,
	input [31:0]ALU_result_wb_i,   
	input [31:0]pc_jump_wb_i,
	input [31:0]loaddata_wb_i,    
	input [31:0]imme_wb_i,
	input [31:0]pc_order_wb_i,
	output [31:0]Wr_reg_data_wb_o

    );
	
	wire [31:0]WB_data;
	wire reg_sel;
	wire [31:0]Wr_reg_data1;
	wire [31:0]Wr_reg_data2;
	
	assign reg_sel=jal | jalr ;
	
	/ALU_result or datamem	
	mux wb_data_mux (
    .data1(loaddata_wb_i), 
    .data2(ALU_result_wb_i), 
    .sel(MemtoReg), 
    .dout(WB_data)
    );
	
	
Wr_data_sel
	mux jalr_mux (
    .data1(pc_order_wb_i), 
    .data2(WB_data), 
    .sel(reg_sel), 
    .dout(Wr_reg_data2)
    );
	
	mux lui_mux (
    .data1(imme_wb_i), 
    .data2(pc_jump_wb_i), 
    .sel(lui), 
    .dout(Wr_reg_data1)
    );
	
	mux wr_reg_mux (
    .data1(Wr_reg_data1), 
    .data2(Wr_reg_data2), 
    .sel(U_type), 
    .dout(Wr_reg_data_wb_o)
    );
	
	
	
	

endmodule


三、数据通路顶层模块

将以上五个子模块以及四个流水线寄存器组进行实例化,将模块与模块之间的信号进行连接,即得到数据通路部分。

代码如下:

module datapath(
	input 	clk,
	input   rst_n,
	input   [31:0]instr,


	input   MemtoReg,
	input   ALUSrc,
	input   RegWrite,
	input   lui,
	input   U_type,
	input   jal,
	input   jalr,
	input   beq,
	input   bne,
	input   blt,
	input   bge,
	input   bltu,
	input   bgeu,
	input   [3:0]ALUctl,
	
	input [31:0]loaddata,
	output  [7:0]rom_addr,
	output [31:0]Wr_mem_data,
	output [31:0]ALU_result,
	output [6:0]opcode,
	output [2:0]func3,
	output func7
	
);

	wire [31:0]pc_if_i;
	wire [31:0]pc_if_o;

	wire [31:0]pc_if_id_o;
	wire [31:0]instr_if_id_o;

	
	wire [31:0]imme_id_o;
	wire [31:0]Rd_data1_id_o;
	wire [31:0]Rd_data2_id_o;
	
	wire [31:0]pc_id_ex_o;
	wire [31:0]imme_id_ex_o;
	wire [31:0]Rd_data1_id_ex_o;
	wire [31:0]Rd_data2_id_ex_o;
	
	wire [31:0] ALU_result_ex_o;
	wire [31:0] pc_jump_o;
	wire [31:0] Rd_data2_ex_o;
	wire [31:0] imme_ex_o;
	wire [31:0] pc_order_ex_o;
	
	
	wire [31:0] ALU_result_ex_mem_o;
	wire [31:0] pc_jump_ex_mem_o;
	wire [31:0] Rd_data2_ex_mem_o;
	wire [31:0] imme_ex_mem_o;
	wire [31:0] pc_order_mem_wb_o;
	
	
	wire [31:0] ALU_result_mem_wb_o;
	wire [31:0] pc_jump_mem_wb_o;
	wire [31:0] loaddata_mem_wb_o;
	wire [31:0] imme_mem_wb_o;
	wire [31:0] pc_order_mem_wb_o;

	
	wire [31:0]Wr_reg_data_wb_o;
	
	
	
	
if_stage if_stage_inst (
    .clk(clk), 
    .rst_n(rst_n), 
    .pc_if_i(pc_if_i), 
    .pc_if_o(pc_if_o), 
    .rom_addr(rom_addr)
    );

if_id_regs  if_id_regs_inst(
    .clk(clk), 
    .rst_n(rst_n), 
    .pc_if_id_i(pc_if_o), 
    .instr_if_id_i(instr), 
    .pc_if_id_o(pc_if_id_o), 
    .instr_if_id_o(instr_if_id_o)
    );

id_stage id_stage_inst (
    .clk(clk), 
    .rst_n(rst_n), 
    .RegWrite_id_i(), 
    .Wr_reg_data_id_i(Wr_reg_data_wb_o), 
    .instr_id_i(instr_if_id_o), 
    .opcode_id_o(opcode), 
    .func3_id_o(func3), 
    .func7_id_o(func7), 
    .imme_id_o(imme_id_o), 
    .Rd_data1_id_o(Rd_data1_id_o), 
    .Rd_data2_id_o(Rd_data2_id_o)
    );

id_ex_regs id_ex_regs_inst (
    .clk(clk), 
    .rst_n(rst_n), 
    .pc_id_ex_i(pc_if_id_o), 
    .imme_id_ex_i(imme_id_o), 
    .Rd_data1_id_ex_i(Rd_data1_id_o), 
    .Rd_data2_id_ex_i(Rd_data2_id_o), 
    .pc_id_ex_o(pc_id_ex_o), 
    .imme_id_ex_o(imme_id_ex_o), 
    .Rd_data1_id_ex_o(Rd_data1_id_ex_o), 
    .Rd_data2_id_ex_o(Rd_data2_id_ex_o)
    );

ex_stage ex_stage_inst (
    .ALUctl_ex_i(), 
    .beq_ex_i(), 
    .bne_ex_i(), 
    .blt_ex_i(), 
    .bge_ex_i(), 
    .bltu_ex_i(), 
    .bgeu_ex_i(), 
    .jal_ex_i(), 
    .jalr_ex_i(), 
    .ALUSrc_ex_i(), 
    .pc_ex_i(), 
    .imme_ex_i(), 
    .Rd_data1_ex_i(Rd_data1_id_ex_o), 
    .Rd_data2_ex_i(Rd_data2_id_ex_o), 
    .ALU_result_ex_o(ALU_result_ex_o), 
    .pc_new_ex_o(pc_if_i), 
    .pc_jump_o(pc_jump_o), 
    .Rd_data2_ex_o(Rd_data2_ex_o), 
    .imme_ex_o(imme_ex_o), 
    .pc_order_ex_o(pc_order_ex_o)
    );
	
ex_mem_regs ex_mem_regs_inst (
    .clk(clk), 
    .rst_n(rst_n), 
    .ALU_result_ex_mem_i(ALU_result_ex_o), 
    .pc_jump_ex_mem_i(pc_jump_o), 
    .Rd_data2_ex_mem_i(Rd_data2_ex_o), 
    .imme_ex_mem_i(imme_ex_o), 
    .pc_order_ex_mem_i(pc_order_ex_o), 
    .ALU_result_ex_mem_o(ALU_result_ex_mem_o), 
    .pc_jump_ex_mem_o(pc_jump_ex_mem_o), 
    .Rd_data2_ex_mem_o(Rd_data2_ex_mem_o), 
    .imme_ex_mem_o(imme_ex_mem_o), 
    .pc_order_ex_mem_o(pc_order_ex_mem_o)
    );

mem_wb_regs mem_wb_regs_inst (
    .ALU_result_mem_wb_i(ALU_result_ex_mem_o), 
    .pc_jump_mem_wb_i(pc_jump_ex_mem_o), 
    .loaddata_mem_wb_i(loaddata), 
    .imme_mem_wb_i(imme_ex_mem_o), 
    .pc_order_mem_wb_i(pc_order_ex_mem_o), 
    .ALU_result_mem_wb_o(ALU_result_mem_wb_o), 
    .pc_jump_mem_wb_o(pc_jump_mem_wb_o), 
    .loaddata_mem_wb_o(loaddata_mem_wb_o), 
    .imme_mem_wb_o(imme_mem_wb_o), 
    .pc_order_mem_wb_o(pc_order_mem_wb_o)
    );
	
wb_stage wb_stage_inst (
    .MemtoReg(), 
    .jal(), 
    .jalr(), 
    .lui(), 
    .U_type(), 
    .ALU_result_wb_i(ALU_result_mem_wb_o), 
    .pc_jump_wb_i(pc_jump_mem_wb_o), 
    .loaddata_wb_i(loaddata_mem_wb_o), 
    .imme_wb_i(imme_mem_wb_o), 
    .pc_order_wb_i(pc_order_mem_wb_o), 
    .Wr_reg_data_wb_o(Wr_reg_data_wb_o)
    );
	
endmodule

总结

以上就是五级流水线处理器的数据通路部分的设计思路,总结下来就是,将CPU执行指令的逻辑分为五个部分,中间添加流水线寄存器组,一方面打断组合逻辑以提升CPU的时钟频率,另一方面保证信号在各级之间流通。
本篇文章仅仅介绍了数据通路部分,可以看到在数据通路的代码中,一些控制信号还是空的,因此下一篇文章将对控制器进行流水线的设计。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐