【使用verilog、五级流水和MIPS指令集设计CPU】

项目地址

github仓库地址

大概描述

分享一个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

设计内容

(https://img-blog.csdnimg.cn/3cbf7c79007041608ff7df4a93824ad2.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZGVhcnpjcw==,size_20,color_FFFFFF,t_70,g_se,x_16)

(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模块定义
在这里插入图片描述

处理器的操作过程

  1. 逻辑以及移位指令操作过程
    在这里插入图片描述
    逻辑以及移位指令结构图

    在这里插入图片描述
    逻辑以及移位指令数据流图

  2. 移动指令操作过程
    在这里插入图片描述
    移动指令结构图

在这里插入图片描述
移动指令数据流图

  1. 算术指令操作过程

在这里插入图片描述

算术指令数据流图

在这里插入图片描述

除法状态转换图

在这里插入图片描述

试除法流程图

在这里插入图片描述

暂停流水结构图
  1. 转移指令操作过程
    在这里插入图片描述

    转移指令结构图

在这里插入图片描述

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

在这里插入图片描述

加载存储指令结构图 

在这里插入图片描述

加载存储指令数据流图 

代码

  1. 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
  1. 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
  1. 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
  1. 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
  1. 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;
                                 
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dearzcs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值