目录
4.1 ori指令的编码
4.2 流水线模型的建立
4.2.1 流水线的简单模型
4.2.2 原始的OpenMIPS五级流水线结构
4.2.3 一些宏定义
全局的宏定义
和具体指令相关的宏定义
和通用寄存器regfile相关的宏定义
4.2.4 取指令阶段的实现
PC模块
IF_ID模块
4.2.5 译码阶段的实现
Regfile模块
ID模块
ID_EX模块
4.2.6 执行阶段的实现
EX模块
EX_MEM模块
4.2.7 访存阶段的实现
MEM模块
MEM_WB模块
4.2.8 回写阶段的实现
4.2.9 顶层模块OpenMIPS的实现
参考书籍
自己动手写CPU 雷思磊
4.1 ori指令的编码
ori指令的编码格式如下:

rs :源寄存器索引。
rt :目的寄存器索引。
immediate:16-bit的常数。
功能:ori指令把符号扩展的immediate与rs索引的寄存器的值相OR存入rt索引的寄存器中。
4.2 流水线模型的建立
4.2.1 流水线的简单模型

寄存器之间有连接没有环路的电路叫做流水线。
4.2.2 原始的OpenMIPS五级流水线结构

取指:取出指令存储器中的指令,PC值递增指向下一个指令。
译码:对指令进行译码,根据译码结果取出寄存器或其他运算数据。
执行:依据译码阶段送入的源操作数,操作码进行运算,结果传递到访存阶段。
访存:对ori指令来说没有操作,直接将运算结果传递到回写阶段。
回写:将运算结果保存到目的寄存器。
对于结构图如下:

4.2.3 一些宏定义
全局的宏定义

和具体指令相关的宏定义

和通用寄存器regfile相关的宏定义

4.2.4 取指令阶段的实现
取指令阶段取出指令存储器中的指令,同时PC值增加指向下一个指令,该阶段由PC、IF_ID两个模块实现[REF 4.2.2]。
PC模块
功能描述:
PC的功能是给出指令地址。
接口描述:

Verilog代码描述:
module pc_reg ( input wire clk, input wire rst, output reg[`InstAddrBus] pc, //InstAddrBus为指令地址的宽度 output reg ce // chip enable ); always @ (posedge clk) ce <= rst == `RstEnable ? `ChipDisable : ChipEnable; always @ (posedge clk) if (ce == `ChipDisable) pc <= 32'b0; else pc <= pc + 4'h4; endmodule
RTL模型:

IF_ID模块(Instruction Fetching Instruction Decoding)
功能描述:
IF_ID模块的作用是暂时保存取指令阶段取得的指令,及其对应的指令地址,并在下一个时钟传递到译码阶段。
接口描述:

Verilog代码描述:
module example ( input wire clk, input wire rst, //来自取指令阶段的信号 input wire[`InstAddrBus] if_pc, //InstAddrBus指令地址宽度 input wire[`InstBus] if_inst, //对应译码阶段的信号 output reg[`InstAddrBus] id_pc, output reg[`InstBus] id_inst ); always @ (posedge clk) if (rst == `RstEnable) begin id_pc <= `ZeroWord; id_inst <= `ZeroWord; end else begin id_pc <= if_pc; id_inst <= if_inst; end endmodule
RTL模型:

4.2.5 译码阶段的实现
在译码阶段,CPU会对取到的指令进行译码,即:给出要进行运算的运算类型,以及参与运算的操作数。译码阶段由模块Regfile、ID(Instruction Decoding)和ID_EX(EXecution)构成。
Regfile模块
功能描述:
Regfile实现了32个32-bit通用整数寄存器,可以同时进行两个寄存器的读操作和一个寄存器的写操作。
接口描述:


Verilog描述:
module regfile ( input wire clk, input wire rst, //写端口,当we使能时,将wdata写入地址为waddr的寄存器中 input wire we, // 写使能(WriteEnable) input wire[`RegAddrBus] waddr, //被写入数据的地址 input wire[`RegBus] wdata, //被写入的数据 //读端口1,当re1使能,将地址为raddr1的寄存器数据输出到rdata1上 input wire re1, input wire[`RegAddrBus] raddr1, output reg[`RegBus] rdata1, //读端口2,同上 input wire re2, input wire[`RegAddrBus] raddr2, output reg[`RegBus] rdata2 ); /** 定义了32个32-bit的寄存器 **/ reg[`RegBus] regs[0:`RegNum-1]; /** 实现了写端口操作,当rst没有使能时,若we使能且waddr并非0地址,那么将数据wdata写入waddr对应的寄存器中。 **/ always @ (posedge clk) begin if (rst == `RstDisable) begin if((we == `WriteEnable) && (waddr != `RegNumLog2'h0)) begin regs[waddr] <= wdata; end end end /** 实现了端口1读操作 **/ always @ (*) begin if(rst == `RstEnable) begin // 若rst使能,那么将会输出数据0。 rdata1 <= `ZeroWord; end else if(raddr1 == `RegNumLog2'h0) begin // 若raddr1引用$0 reg,那么直接输出0。 rdata1 <= `ZeroWord; end else if((raddr1 == waddr) && (we == `WriteEnable) // 若要读取的寄存器是要写入的数据的寄存器,那么直接复制 && (re1 == `ReadEnable)) begin rdata1 <= wdata; end else if(re1 == `ReadEnable) begin // 读取使能且读取地址有效非特殊地址,复制相应数据。 rdata1 <= regs[raddr1]; end else begin //无效情况,输出0 rdata1 <= `ZeroWord; end end /** 实现了端口2读操作 **/ always @ (*) begin if(rst == `RstEnable) begin rdata2 <= `ZeroWord; end else if(raddr2 == `RegNumLog2'h0) begin rdata2 <= `ZeroWord; end else if((raddr2 == waddr) && (we == `WriteEnable) && (re2 == `ReadEnable)) begin rdata2 <= wdata; end else if(re2 == `ReadEnable) begin rdata2 <= regs[raddr2]; end else begin rdata2 <= `ZeroWord; end end endmodule
RTL模型:

ID模块
功能描述:
ID模块的作用是对指令进行译码,得到最终运算的类型、子类型、源操作数1、源操作数2、目的寄存器地址等信息。
运算类型:逻辑运算、位移运算、算数运算等。
算数子类型:对运算类型更进一步的说明,例如逻辑运算-与等。
接口描述:


Verilog描述:
`include "defines.v"
module id(
input wire rst,
input wire[`InstAddrBus] pc_i, //译码阶段指令对应的地址
input wire[`InstBus] inst_i, //译码阶段的指令
input wire[`RegBus] reg1_data_i, //从regfile输入的第一个寄存器端口的输入
input wire[`RegBus] reg2_data_i, //从regfile输入的第二个寄存器端口的输入
//送到regfile的信息,以便获取指令执行时所需要的数据
output reg reg1_read_o,
output reg reg2_read_o,
output reg[`RegAddrBus] reg1_addr_o,
output reg[`RegAddrBus] reg2_addr_o,
//送到执行阶段的信息
output reg[`AluOpBus] aluop_o,
output reg[`AluSelBus] alusel_o,
output reg[`RegBus] reg1_o,
output reg[`RegBus] reg2_o,
output reg[`RegAddrBus] wd_o, //Written Destination Out,译码阶段将要写入目的的寄存器地址。
output reg wreg_o //译码阶段是否有要写入的目的地址。
);
//获取指令的指令码和功能码
//对于ori指令只需要通过判断26-31bit的值即可确定是否为ori
wire[5:0] op = inst_i[31:26];
wire[4:0] op2 = inst_i[10:6];
wire[5:0] op3 = inst_i[5:0];
wire[4:0] op4 = inst_i[20:16];
//保存指令执行需要的立即数
reg[`RegBus] imm;
//指示指令是否有效
reg instvalid;
/**
第一部分--对指令译码
**/
always @ (*) begin
if (rst == `RstEnable) begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= `NOPRegAddr;
wreg_o <= `WriteDisable;
instvalid <= `InstValid;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= `NOPRegAddr;
reg2_addr_o <= `NOPRegAddr;
imm <= 32'h0;
end else begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= inst_i[15:11];
wreg_o <= `WriteDisable;
instvalid <= `InstInvalid;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= inst_i[25:21]; //默认通过Regfile读端口1读取的寄存器地址
reg2_addr_o <= inst_i[20:16]; //默认通过Regfile读端口2读取的寄存器地址
imm <= `ZeroWord;
case (op)
`EXE_ORI: //根据op的值判断是否为ori指令
begin //ORI指令
wreg_o <= `WriteEnable; // ori需要将结果写入目的寄存器,所以wreg_o为WriteEnable
aluop_o <= `EXE_OR_OP; //算数类型
alusel_o <= `EXE_RES_LOGIC; //子运算类型
reg1_read_o <= 1'b1; // rs,需要读取
reg2_read_o <= 1'b0; // rt,不需要读取。
imm <= {16'h0, inst_i[15:0]}; //指令执行需要的立即数
wd_o <= inst_i[20:16]; // rt的寄存器地址
instvalid <= `InstValid; //指令有效
end
default:
begin end
endcase //case op
end //if
end //always
/**
第二部分--确定进行运算的源操作数1
**/
always @ (*) begin
if(rst == `RstEnable) begin
reg1_o <= `ZeroWord;
end else if(reg1_read_o == 1'b1) begin //regfile读端口1的输出值
reg1_o <= reg1_data_i;
end else if(reg1_read_o == 1'b0) begin //立即数
reg1_o <= imm;
end else begin
reg1_o <= `ZeroWord;
end
end
/**
第三部分--确定进行运算的源操作数2
**/
always @ (*) begin
if(rst == `RstEnable) begin
reg2_o <= `ZeroWord;
end else if(reg2_read_o == 1'b1) begin //regfile读端口2的输出值
reg2_o <= reg2_data_i;
end else if(reg2_read_o == 1'b0) begin //立即数
reg2_o <= imm;
end else begin
reg2_o <= `ZeroWord;
end
end
endmodule
ID_EX模块
功能描述:
将ID模块对指令的译码结果缓存,在下一个时钟沿传递到流水线执行模块。
接口描述:

Verilog描述
`include "defines.h"
module id_ex (
input wire clk,
input wire rst,
//从译码阶段传递的信息
input wire[`AluOpBus] id_aluop;
input wire[`AluSelBus] id_alusel;
input wire[`RegBus] id_reg1,
input wire[`RegBus] id_reg2,
input wire[`RegAddrBus] id_wd,
input wire id_wreg,
//传递到执行阶段的信息
output reg[`AluOpBus] ex_aluop,
output reg[`AluSelBus] ex_alusel,
output reg[`RegBus] ex_reg1,
output reg[`RegBus] ex_reg2,
output reg[`RegBus] ex_wd,
output reg ex_wreg,
);
always @ (posedge clk) begin
if (rst == `RstEnable) 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 begin
ex_aluop <= id_aluop;
ex_alusel <= id_alusel;
ex_reg1 <= id_reg1;
ex_reg2 <= id_reg2;
ex_wd <= id_wd;
ex_wreg <= id_wreg;
end
end
endmodule
4.2.6 执行阶段的实现
现在指令已经进入流水线的执行阶段,在此期间将根据译码的结果,对数据进行运算,该阶段包含EX、EX_MEM两个模块。
EX模块
功能描述:
EX从ID_EX模块得到指令的译码信息,然后根据这些信息进行运算。
接口描述:

Verilog描述
`include "defines.v" module example ( input wire rst, //译码阶段送到执行阶段的信息 input wire[`AluOpBus] aluop_i, input wire[`AluSelBus] alusel_i, input wire[`RegBus] reg1_i, input wire[`RegBus] reg2_i, input wire[`RegAddrBus] wd_i, input wire wreg_i, //执行的结果 output reg[`RegAddrBus] wd_o, output reg wreg_o, output reg[`RegBus] wdata_o ); //保存逻辑运算的结果 reg[`RegBus] logicout; //根据aluop_i指示的算子类型进行运算。 always @ (*) begin if (rst == `RstEnable) logicout <= `ZeroWord; else begin case (aluop_i) `EXE_OR_OP: begin logicout <= reg1_i | reg2_i; end default : begin logicout <= `ZeroWord; end endcase end end //根据alusel_i指示的运算类型,选择一个运算结果作为最终结果。 always @ (*) begin wd_o <= wd_i; wreg_o <= wreg_i; case (alusel_i) `EXE_RES_LOGIC: begin wdata_o <= logicout; end default: begin wdata_o <= `ZeroWord; end endcase end endmodule
EX_MEM模块
接口描述
EX模块的输出连接到EX_MEM模块,EX_MEM模块的作用是将执行阶段取得的运算结果在下一个时钟传递到流水线访存阶段。
接口描述

Verilog描述
module ex_mem ( input wire clk, input wire rst, //来自执行阶段的信息 input wire[`RegAddrBus] ex_wd, input wire ex_wreg, input wire[`RegBus] ex_wdata, //送到访存阶段的信息 output reg[`RegAddrBus] mem_wd, output reg mem_wreg, output reg[`RegBus] mem_wdata ); always @ (posedge clk) begin if (rst == `RstEnable) begin mem_wd <= `NOPRegAddr; mem_wreg <= `WriteDisable; mem_wdata <= `ZeroWord; end else begin mem_wd <= ex_wd; mem_wreg <= ex_wreg; mem_wdata <= ex_wdata; end end endmodule
4.2.7 访存阶段的实现
访存阶段包括MEM、MEM_WB两个模块。
MEM模块
功能描述:
由于ori指令不需要访问数据存储器,所以在访存阶段不做任何事,只是简单将执行阶段的结果写到回写阶段传递即可。
接口描述:

Verilog描述:
module mem ( input wire rst, //来自执行阶段的信息 input wire[`RegAddrBus] wd_i, input wire wreg_i, input wire[`RegBus] wdata_i, //访存阶段的结果 output reg[`RegAddrBus] wd_o, output reg wreg_o, output reg[`RegBus] wdata_o ); always @ (*) begin if (rst == `RstEnable) begin wd_o <= `NOPRegAddr; wreg_o <= `WriteDisable; wdata_o <= `ZeroWord; end else begin wd_o <= wd_i; wreg_o <= wreg_i; wdata_o <= wdata_i; end end endmodule
MEM_WB模块
功能描述:
该模块将访存阶段的运算结果在下一个时钟传递到回写阶段。
接口描述:


Verilog描述:
module mem_wb ( input wire clk, input wire rst, //访存阶段的结果 input wire[`RegAddrBus] mem_wd, input wire mem_wreg, input wire[`RegBus] mem_wdata, //送到回写阶段的信息 output reg[`RegAddrBus] wb_wd, output reg wb_wreg, output reg[`RegBus] wb_wdata ); always @ (posedge clk) begin if (rst == `RstEnable) begin wd_wd <= NOPRegAddr; wb_wreg <= `WriteDisable; wb_wdata <= `ZeroWord; end else begin wb_wd <= mem_wd; wb_reg <= mem_wreg; wb_wdata <= mem_wdata; end end endmodule
4.2.8 回写阶段的实现
MEM_WB模块的输出连接到Regfile模块将指令的运算结果写入目的寄存器以实现回写阶段。
4.2.9 顶层模块OpenMIPS的实现
功能描述:
顶层模块OpenMIPS在文件openmips.v中实现,主要对上面实现的流水线各阶段的模块进行连接。
接口描述:

Verilog模型:
`include "defines.v" module openmips( input wire clk, input wire rst, input wire[`RegBus] rom_data_i, output wire[`RegBus] rom_addr_o, output wire rom_ce_o ); //命名规范 : 模块名称_功能_输入输出 //表示该条线为某个模块某个功能的输入/输出端口连接线。 wire[`InstAddrBus] pc; wire[`InstAddrBus] id_pc_i; wire[`InstBus] id_inst_i; //连接译码阶段ID模块的输出与ID/EX模块的输入 wire[`AluOpBus] id_aluop_o; wire[`AluSelBus] id_alusel_o; wire[`RegBus] id_reg1_o; wire[`RegBus] id_reg2_o; wire id_wreg_o; wire[`RegAddrBus] id_wd_o; //连接ID/EX模块的输出与执行阶段EX模块的输入 wire[`AluOpBus] ex_aluop_i; wire[`AluSelBus] ex_alusel_i; wire[`RegBus] ex_reg1_i; wire[`RegBus] ex_reg2_i; wire ex_wreg_i; wire[`RegAddrBus] ex_wd_i; //连接执行阶段EX模块的输出与EX/MEM模块的输入 wire ex_wreg_o; wire[`RegAddrBus] ex_wd_o; wire[`RegBus] ex_wdata_o; //连接EX/MEM模块的输出与访存阶段MEM模块的输入 wire mem_wreg_i; wire[`RegAddrBus] mem_wd_i; wire[`RegBus] mem_wdata_i; //连接访存阶段MEM模块的输出与MEM/WB模块的输入 wire mem_wreg_o; wire[`RegAddrBus] mem_wd_o; wire[`RegBus] mem_wdata_o; //连接MEM/WB模块的输出与回写阶段的输入 wire wb_wreg_i; wire[`RegAddrBus] wb_wd_i; wire[`RegBus] wb_wdata_i; //连接译码阶段ID模块与通用寄存器Regfile模块 wire reg1_read; wire reg2_read; wire[`RegBus] reg1_data; wire[`RegBus] reg2_data; wire[`RegAddrBus] reg1_addr; wire[`RegAddrBus] reg2_addr; //pc_reg例化 pc_reg pc_reg0( .clk(clk), .rst(rst), .pc(pc), .ce(rom_ce_o) ); assign rom_addr_o = pc; //IF/ID模块例化 if_id if_id0( .clk(clk), .rst(rst), .if_pc(pc), .if_inst(rom_data_i), .id_pc(id_pc_i), .id_inst(id_inst_i) ); //译码阶段ID模块 id id0( .rst(rst), .pc_i(id_pc_i), .inst_i(id_inst_i), .reg1_data_i(reg1_data), .reg2_data_i(reg2_data), //送到regfile的信息 .reg1_read_o(reg1_read), .reg2_read_o(reg2_read), .reg1_addr_o(reg1_addr), .reg2_addr_o(reg2_addr), //送到ID/EX模块的信息 .aluop_o(id_aluop_o), .alusel_o(id_alusel_o), .reg1_o(id_reg1_o), .reg2_o(id_reg2_o), .wd_o(id_wd_o), .wreg_o(id_wreg_o) ); //通用寄存器Regfile例化 regfile regfile1( .clk (clk), .rst (rst), .we (wb_wreg_i), .waddr (wb_wd_i), .wdata (wb_wdata_i), .re1 (reg1_read), .raddr1 (reg1_addr), .rdata1 (reg1_data), .re2 (reg2_read), .raddr2 (reg2_addr), .rdata2 (reg2_data) ); //ID/EX模块 id_ex id_ex0( .clk(clk), .rst(rst), //从译码阶段ID模块传递的信息 .id_aluop(id_aluop_o), .id_alusel(id_alusel_o), .id_reg1(id_reg1_o), .id_reg2(id_reg2_o), .id_wd(id_wd_o), .id_wreg(id_wreg_o), //传递到执行阶段EX模块的信息 .ex_aluop(ex_aluop_i), .ex_alusel(ex_alusel_i), .ex_reg1(ex_reg1_i), .ex_reg2(ex_reg2_i), .ex_wd(ex_wd_i), .ex_wreg(ex_wreg_i) ); //EX模块 ex ex0( .rst(rst), //送到执行阶段EX模块的信息 .aluop_i(ex_aluop_i), .alusel_i(ex_alusel_i), .reg1_i(ex_reg1_i), .reg2_i(ex_reg2_i), .wd_i(ex_wd_i), .wreg_i(ex_wreg_i), //EX模块的输出到EX/MEM模块信息 .wd_o(ex_wd_o), .wreg_o(ex_wreg_o), .wdata_o(ex_wdata_o) ); //EX/MEM模块 ex_mem ex_mem0( .clk(clk), .rst(rst), //来自执行阶段EX模块的信息 .ex_wd(ex_wd_o), .ex_wreg(ex_wreg_o), .ex_wdata(ex_wdata_o), //送到访存阶段MEM模块的信息 .mem_wd(mem_wd_i), .mem_wreg(mem_wreg_i), .mem_wdata(mem_wdata_i) ); //MEM模块例化 mem mem0( .rst(rst), //来自EX/MEM模块的信息 .wd_i(mem_wd_i), .wreg_i(mem_wreg_i), .wdata_i(mem_wdata_i), //送到MEM/WB模块的信息 .wd_o(mem_wd_o), .wreg_o(mem_wreg_o), .wdata_o(mem_wdata_o) ); //MEM/WB模块 mem_wb mem_wb0( .clk(clk), .rst(rst), //来自访存阶段MEM模块的信息 .mem_wd(mem_wd_o), .mem_wreg(mem_wreg_o), .mem_wdata(mem_wdata_o), //送到回写阶段的信息 .wb_wd(wb_wd_i), .wb_wreg(wb_wreg_i), .wb_wdata(wb_wdata_i) ); endmodule
浙公网安备 33010602011771号