项目地址
大概描述
分享一个GPU服务器租赁平台(价格十分优惠,有4090,3090,A6000等)
参考《自己动手写CPU》这本书,这本书算是手把手教学写CPU,比较适合初学者。这里完成五级流水结构的处理器,实现70条左右的指令,基本实现全部整数指令,开发工具是Vivado。
设计思想
设计的处理器是五级流水处理器,取指,译码,执行,访存,回写。
(1)取指: 取出指令存储器中的指令,PC值递增,准备取下一条指令。
(2)译码:对指令进行译码,依据译码结果,从32个通用寄存器中取出源操作数,有的 指令要求两个源操作数都是寄存器的值,比如or指令,有的指令要求其中一 个源操作数是指令中立即数的扩展,比如ori指令,所以这里有两个复用器, 用于依据指令要求,确定参与运算的操作数,最终确定的两个操作数会送到执 行阶段。
(3)执行:依据译码阶段送入的源操作数、操作码,进行运算,对于ori指令而言, 就是进行逻辑“或”运算,运算结果传递到访存阶段。
(4)访存:对于ori指令,在访存阶段没有任何操作,直接将运算结果向下传递到回写阶 段。
(5)回写:将运算结果保存到目的寄存器。
指令执行周期
| 指令类别 | 指令名 | 周期 |
|---|---|---|
| 除法指令 | div,divu | 36 |
| 乘累加指令 | madd,maddu | 2 |
| 乘累减指令 | msub,msubu | 2 |
| 其余指令 | … | 1 |
设计内容

(1)取指阶段
PC模块;给出指令地址,其中实现指令指针寄存器PC,该寄存器的值就是指令地址,
对应 pc_reg.v文件。
IF/ID模块:实现取指与译码阶段之间的寄存器,将取指阶段的结果(取得的指令、 指令地址等信息)在下一个时钟传递到译码阶段,对应if_id.v文件。
(2)译码阶段
ID模块:对指令进行译码,译码结果包括运算类型、运算所需的源操作数、要 写入的目的寄存器地址等,对应 idv文件。
Regfile模块:实现了32个32位通用整数寄存器,可以同时进行两个寄存器的读操 作和一个寄存器的写操作,对应Regfile.v文件。
ID/EX模块:实现译码与执行阶段之间的寄存器,将译码阶段的结果在下一个时钟周 期传递到执行阶段,对应 id_ex.v。
(3)执行阶段
EX模块:依据译码阶段的结果,进行指定的运算,给出运算结果。对应.ex.v文件。 DIV模块:进行除法运算的模块,对应 div.v文件。
EX/MEM 模块:实现执行与访存阶段之间的寄存器,将执行阶段的结果在下一个时 钟周期传递到访存阶段,对应 ex_mem.v文件。
(4)访存阶段
MEM模块:如果是加载、存储指令,那么会对数据存储器进行访问。此外,还会在 该模块进行异常判断。对应mem.v文件。
MEM/WB模块:实现访存与回写阶段之间的寄存器,将访存阶段的结果在下一个时 钟周期传递到回写阶段,对应mem_wb.v文件。
(5)回写阶段
HILO模块:实现寄存器 HI、LO,在乘法、除法指令的处理过程中会使用到这两个 寄存器。
设计处理器的结构和方法
1.ctrl 模块定义

2.data_ram模块定义

3.div模块定义


4.ex模块定义


5.ex_mem模块定义

6.hilo_reg模块定义

7.id模块定义


8.id_ex模块定义


9.if_id模块定义

10.inst_rom模块定义

11.mem模块定义

12.mem_wb模块定义

13.openmips模块定义

14.openmips_min_sopc模块定义

15.pc_reg模块定义

16.Regfile模块定义

处理器的操作过程
-
逻辑以及移位指令操作过程

逻辑以及移位指令结构图
逻辑以及移位指令数据流图 -
移动指令操作过程

移动指令结构图

移动指令数据流图
- 算术指令操作过程

算术指令数据流图

除法状态转换图

试除法流程图

暂停流水结构图
-
转移指令操作过程

转移指令结构图

转移指令数据流图
- 加载存储指令

加载存储指令结构图

加载存储指令数据流图
代码
- pc_reg 模块
`include"defines.v"
module pc_reg(
input wire clk,
input wire rst,
input wire[5:0] stall,//来自控制模块的ctrl
input wire branch_flag_i,
input wire[`RegBus] branch_target_address_i,
output reg[`InstAddrBus] pc,
output reg ce
);
always @ (posedge clk)begin
if (rst == `RstEnable)begin
ce <= `ChipDisable;
end else begin
ce <= `ChipEnable;
end
end
always @ (posedge clk) begin
if (ce == `ChipDisable) begin
pc <= 32'h0;//
end else if(stall[0] == `NoStop) begin
if(branch_flag_i == `Branch)begin
pc <= branch_target_address_i;
end else begin
pc <= pc + 4'h4;
end
end
end
endmodule
- if_id 模块
`include"defines.v"
module if_id(
input wire clk,
input wire rst,
input wire[5:0] stall,
// 来自取指阶段的信号,其中宏定义InstBus表示指令宽度,为32
input wire[`InstAddrBus] if_pc,
input wire[`InstBus] if_inst,
//对应译码阶段的信号
output reg[`InstAddrBus] id_pc,
output reg[`InstBus] id_inst
);
always @ (posedge clk)begin
if(rst == `RstEnable) begin
id_pc <= `ZeroWord; // 复位的时候pc为0
id_inst <= `ZeroWord;// 复位的时候指令也为0.实际就是空指令
end else if(stall[1] == `Stop && stall[2] == `NoStop) begin
id_pc <= `ZeroWord;
id_inst <= `ZeroWord;
end else if(stall[1] == `NoStop) begin
id_pc <= if_pc; //其余时刻向下传递取指令阶段的值
id_inst <= if_inst;
end
end
endmodule
- Regfile 模块
`include"defines.v"
module Regfile(
input wire clk,
input wire rst,
//写端口
input wire we,
input wire[`RegAddrBus] waddr,
input wire[`RegBus] wdata,
//读端口
input wire re1,
input wire[`RegAddrBus]raddr1,
output reg[`RegBus] rdata1,
//读端口
input wire re2,
input wire[`RegAddrBus] raddr2,
output reg[`RegBus] rdata2
);
/********************************************** 第一段 : 定义32个32位寄存器 *************************/
reg[`RegBus] regs[0:`RegNum-1];
/********************************************** 第二段 : 写操作 *************************/
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
rdata1 <= `ZeroWord;
end else if(raddr1 == `RegNumLog2'h0)begin
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
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
- if_id 模块
include"defines.v"
module if_id(
input wire clk,
input wire rst,
input wire[5:0] stall,
// 来自取指阶段的信号,其中宏定义InstBus表示指令宽度,为32
input wire[`InstAddrBus] if_pc,
input wire[`InstBus] if_inst,
//对应译码阶段的信号
output reg[`InstAddrBus] id_pc,
output reg[`InstBus] id_inst
);
always @ (posedge clk)begin
if(rst == `RstEnable) begin
id_pc <= `ZeroWord; // 复位的时候pc为0
id_inst <= `ZeroWord;// 复位的时候指令也为0.实际就是空指令
end else if(stall[1] == `Stop && stall[2] == `NoStop) begin
id_pc <= `ZeroWord;
id_inst <= `ZeroWord;
end else if(stall[1] == `NoStop) begin
id_pc <= if_pc; //其余时刻向下传递取指令阶段的值
id_inst <= if_inst;
end
end
endmodule
- id 模块
`include"defines.v"
module id(
input wire rst,
input wire[`InstAddrBus] pc_i,
input wire[`InstBus] inst_i,
// 时延
input wire is_in_delayslot_i,
// 读取的RegFile的值
input wire[`RegBus] reg1_data_i,
input wire[`RegBus] reg2_data_i,
//输出到Regfile的信息
output reg reg1_read_0,
output reg reg2_read_0,
output reg[`RegAddrBus] reg1_addr_0,
output reg[`RegAddrBus] reg2_addr_0,
//处于执行阶段的指令的运算结果
input wire ex_wreg_i,
input wire[`RegBus] ex_wdata_i,
input wire[`RegAddrBus] ex_wd_i,
//处于访存阶段的指令的运算结果
input wire mem_wreg_i,
input wire[`RegBus] mem_wdata_i,
input wire[`RegAddrBus] mem_wd_i,
// 分支时延
output reg next_inst_in_delayslot_0,
output reg branch_flag_0,
output reg[`RegBus] branch_target_address_0,
output reg[`RegBus] link_addr_0,
output reg is_in_delayslot_0,
//送到执行阶段的信息
output reg[`AluOpBus] aluop_0,
output reg[`AluSelBus] alusel_0,
output reg[`RegBus] reg1_0,
output reg[`RegBus] reg2_0,
output reg[`RegAddrBus]wd_0,
output reg wreg_0,
output wire stallreq,
output wire[`RegBus] inst_0
);
//取指令的指令码,功能码
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];
wire[`RegBus] pc_plus_8;
wire[`RegBus] pc_plus_4;
wire[`RegBus] imm_sll2_signedext;
//指示指令是否有效
reg instvalid;
//保存指令执行需要的立即数
reg[`RegBus] imm;
assign pc_plus_8 = pc_i + 8;
assign pc_plus_4 = pc_i + 4;
//对应分支指令中的offset左移两位,再符号拓展到32位
assign imm_sll2_signedext = {
{14{inst_i[15]}}, inst_i[15:0],2'b00};
assign stallreq = `NoStop;
assign inst_0 = inst_i; // inst_0的值就是译码阶段的指令
/************************** 第一段: 对指令进行译码 **************************************/
always @ (*) begin
if(rst == `RstEnable) begin
aluop_0 <= `EXE_NOP_OP;
alusel_0 <= `EXE_RES_NOP;
wd_0 <= `NOPRegAddr;
wreg_0 <= `WriteDisable;
instvalid <= `InstValid;
reg1_read_0 <= 1'b0;
reg2_read_0 <= 1'b0;
reg1_addr_0 <= `NOPRegAddr;
reg2_addr_0 <= `NOPRegAddr;
imm <= 32'h0;
link_addr_0 <= `ZeroWord;
branch_target_address_0 <= `ZeroWord;
branch_flag_0 <= `NotBranch;
next_inst_in_delayslot_0 <= `NotInDelaySlot;
end else begin
aluop_0 <= `EXE_NOP_OP;
alusel_0 <= `EXE_RES_NOP;
wd_0 <= inst_i[15:11];
wreg_0 <= `WriteDisable;
instvalid <= `InstInvalid;
reg1_read_0 <= 1'b0;
reg2_read_0 <= 1'b0;
reg1_addr_0 <= inst_i[25:21]; // 默认通过 Regfile 读端口1读取的寄存器地址
reg2_addr_0 <= inst_i[20:16]; // 默认通过 Regfile 读端口2读取的寄存器地址
imm <= `ZeroWord;
link_addr_0 <= `ZeroWord;
branch_target_address_0 <= `ZeroWord;
branch_flag_0 <= `NotBranch;
next_inst_in_delayslot_0 <= `NotInDelaySlot;
case(op)
`EXE_SPECIAL_INST:begin
case(op2)
5'b00000:begin
case(op3)
`EXE_OR:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_OR_OP;
alusel_0 <= `EXE_RES_LOGIC;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_AND:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_AND_OP;
alusel_0 <= `EXE_RES_LOGIC;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_XOR:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_XOR_OP;
alusel_0 <= `EXE_RES_LOGIC;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_NOR:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_NOR_OP;
alusel_0 <= `EXE_RES_LOGIC;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_DIV:begin
wreg_0 <= `WriteDisable;
aluop_0 <= `EXE_DIV_OP;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_DIVU:begin
wreg_0 <= `WriteDisable;
aluop_0 <= `EXE_DIVU_OP;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_JR:begin
wreg_0 <= `WriteDisable;
aluop_0 <= `EXE_JR_OP;
alusel_0 <= `EXE_RES_JUMP_BRANCH;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b0;
link_addr_0 <= `ZeroWord;
branch_target_address_0 <= reg1_0;
branch_flag_0 <= `Branch;
next_inst_in_delayslot_0 <= `InDelaySlot;
instvalid <= `InstValid;
end
`EXE_JALR:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_JALR_OP;
alusel_0 <= `EXE_RES_JUMP_BRANCH;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b0;
wd_0 <= inst_i[15:11];
link_addr_0 <= pc_plus_8;
branch_target_address_0 <= reg1_0;
branch_flag_0 <= `Branch;
next_inst_in_delayslot_0 <= `InDelaySlot;
instvalid <= `InstValid;
end
`EXE_SLLV:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_SLL_OP;
alusel_0 <= `EXE_RES_SHIFT;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SRLV:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_SRL_OP;
alusel_0 <= `EXE_RES_SHIFT;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SRAV:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_SRA_OP;
alusel_0 <= `EXE_RES_SHIFT;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SYNC:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_NOP_OP;
alusel_0 <= `EXE_RES_NOP;
reg1_read_0 <= 1'b0;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_MFHI:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_MFHI_OP;
alusel_0 <= `EXE_RES_MOVE;
reg1_read_0 <= 1'b0;
reg2_read_0 <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MFLO:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_MFLO_OP;
alusel_0 <= `EXE_RES_MOVE;
reg1_read_0 <= 1'b0;
reg2_read_0 <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MTHI:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_MTHI_OP;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MTLO:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_MTLO_OP;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MOVN:begin
aluop_0 <= `EXE_MOVN_OP;
alusel_0 <= `EXE_RES_MOVE;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
if(reg2_0!=`ZeroWord)begin
wreg_0 <= `WriteEnable;
end else begin
wreg_0 <= `WriteDisable;
end
end
`EXE_MOVZ:begin
aluop_0 <= `EXE_MOVZ_OP;
alusel_0 <= `EXE_RES_MOVE;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
if(reg2_0==`ZeroWord)begin
wreg_0 <= `WriteEnable;
end else begin
wreg_0 <= `WriteDisable;
end
end
`EXE_SLT:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_SLT_OP;
alusel_0 <= `EXE_RES_ARITHMETIC;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SLTU:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_SLTU_OP;
alusel_0 <= `EXE_RES_ARITHMETIC;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;
end
`EXE_ADD:begin
wreg_0 <= `WriteEnable;
aluop_0 <= `EXE_ADD_OP;
alusel_0 <= `EXE_RES_ARITHMETIC;
reg1_read_0 <= 1'b1;
reg2_read_0 <= 1'b1;
instvalid <= `InstValid;


2万+

被折叠的 条评论
为什么被折叠?



