自己动手写CPU(4)——部分逻辑和移位指令的实现

代码模块

1. 宏定义defines.v

相对于ORI指令新添加与具体指令相关的宏定义

// 宏定义模块
// *****************  全局的宏定义  ***************
`define RstEnable       1'b1          // 复位信号有效
`define RstDisable      1'b0          // 复位信号无效
`define ZeroWord        32'h00000000  // 32位的数值0
`define WriteEnable     1'b1          // 使能写
`define WriteDisable    1'b0          // 禁止写
`define ReadEnable      1'b1          // 使能读
`define ReadDisable     1'b0          // 禁止读
`define AluOpBus        7:0           // 译码阶段的输出aluop_o的宽度  子类型
`define AluSelBus       2:0           // 译码阶段的输出alusel_o的宽度 类型
`define InstValid       1'b0          // 指令有效
`define InstInvalid     1'b1          // 指令无效
`define True_v          1'b1          // 逻辑真
`define False_v         1'b0          // 逻辑假
`define ChipEnable      1'b1          // 芯片使能
`define ChipDisable     1'b0          // 芯片禁止 


// ***************  与具体指令有关的宏定义  ***************
`define EXE_NOP         6'b000000 

`define EXE_ORI         6'b001101     // 指令ori的指令码    
`define EXE_ANDI        6'b001100
`define EXE_XORI        6'b001110
`define EXE_LUI         6'b001111

`define EXE_SPECIAL_INST  6'b000000
`define EXE_AND         6'b100100     // 指令的功能码
`define EXE_OR          6'b100101
`define EXE_XOR         6'b100110
`define EXE_NOR         6'b100111 

`define EXE_SLL         6'b000000
`define EXE_SLLV        6'b000100
`define EXE_SRL         6'b000010
`define EXE_SRLV        6'b000110
`define EXE_SRA         6'b000011
`define EXE_SRAV        6'b000111

  

//AluOp   子类型
`define EXE_OR_OP       8'b00100101   // or_func
`define EXE_NOP_OP      8'b00000000
`define EXE_AND_OP         8'b00100100
`define EXE_XOR_OP         8'b00100110
`define EXE_NOR_OP         8'b00100111 

`define EXE_SLL_OP         8'b00000000
`define EXE_SLLV_OP        8'b00000100
`define EXE_SRL_OP         8'b00000010
`define EXE_SRLV_OP        8'b00000110
`define EXE_SRA_OP         8'b00000011
`define EXE_SRAV_OP        8'b00000111

//AluSel  类型
`define EXE_RES_LOGIC   3'b001        // 逻辑运算
`define EXE_RES_SHIFT   3'b010        // 移位运算
`define EXE_RES_NOP     3'b000


// ***************  与指令存储器ROM有关的宏定义  ***************
`define InstAddrBus     31:0          // ROM的地址总线宽度
`define InstBus         31:0          // ROM数的据总线宽度
`define InstMemNum      131071
`define InstMemNumLog2  17


// **************  与通用寄存器Regfile有关的宏定义  ***************
`define RegAddrBus      4:0           // Regfile模块的地址线宽度
`define RegBus          31:0          // Regfile模块的数据线宽度
`define RegWidth        32
`define DoubleRegWidth  64
`define DoubleRegBus    63:0
`define RegNum          32            // 通用寄存器的数量
`define RegNumLog2      5             // 寻址通用寄存器使用的地址位数
`define NOPRegAddr      5'b00000

2. 译码id

添加逻辑指令OR、AND、XOR、NOR、ANDI、XORI、LUI和移位指令SLL、SRL、SRA、SLLV、SRLV、SRAV的译码分支

// 对指令进行译码,得到最终运算的类型、子类型、源操作数1、源操作数2、要写入的目的寄存器地址信息等
`include "defines.v"

module id (
  // 输出到regfile的信息
  output  reg                   reg1_read_o,    // regfile模块第一个读寄存器端口的读使能信号
  output  reg                   reg2_read_o,    // regfile模块第二个读寄存器端口的读使能信号
  output  reg   [`RegAddrBus]   reg1_addr_o,    // regfile模块第一个读寄存器端口的读地址信号
  output  reg   [`RegAddrBus]   reg2_addr_o,    // regfile模块第二个读寄存器端口的读地址信号
  // 送到执行阶段的信息
  output  reg   [`AluOpBus]     aluop_o,        // 译码阶段的指令要进行的运算的子类型
  output  reg   [`AluSelBus]    alusel_o,       // 译码阶段的指令要进行的运算的类型
  output  reg   [`RegBus]       reg1_o,         // 译码阶段的指令要进行的运算的源操作数1
  output  reg   [`RegBus]       reg2_o,         // 译码阶段的指令要进行的运算的源操作数2
  output  reg   [`RegAddrBus]   wd_o,           // 译码阶段的指令要写入的目的寄存器地址
  output  reg                   wreg_o,         // 译码阶段的指令是否有要写入的目的寄存器
  
  input   wire                  rst,            // 复位
  input   wire  [`InstAddrBus]  pc_i,           // 译码阶段指令对应的地址
  input   wire  [`InstBus]      inst_i,         // 译码阶段的指令
  // 读取的regfile的值
  input   wire  [`RegBus]       reg1_data_i,    // 从regfile输入的第一个读寄存器端口的输出
  input   wire  [`RegBus]       reg2_data_i     // 从regfile输入的第一个读寄存器端口的输出  
);

  // 取得指令的指令码,功能码
  // 对于ori指令只需通过判断26-31bits的值,即可判断是否是ori指令,6'b00_1101
  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 <=  `InstValid;       // 指令有效
      reg1_read_o <=  1'b0;
      reg2_read_o <=  1'b0;
      reg1_addr_o <=  inst_i[25:21];  // 默认通过regfile读端口1读取的寄存器地址,rs,源寄存器地址
      reg2_addr_o <=  inst_i[20:16];  // 默认通过regfile读端口2读取的寄存器地址,rt,源寄存器地址
      imm         <=  `ZeroWord;
      
      case (op)
        `EXE_SPECIAL_INST:  begin
          case  (op2)                 // [10:6]sa为0
            5'b00000: begin
              case (op3)              // [5:0]func
                `EXE_OR:  begin                   // or指令
                  wreg_o      <=  `WriteEnable;
                  aluop_o     <=  `EXE_OR_OP;     // 8'b 0010_0101
                  alusel_o    <=  `EXE_RES_LOGIC;
                  reg1_read_o <=  1'b1;
                  reg2_read_o <=  1'b1;
                  instvalid   <=  `InstValid;
                end
                `EXE_AND: begin                   // and指令
                  wreg_o      <=  `WriteEnable;
                  aluop_o     <=  `EXE_AND_OP;    // 8'b 0010_0100
                  alusel_o    <=  `EXE_RES_LOGIC;
                  reg1_read_o <=  1'b1;
                  reg2_read_o <=  1'b1;
                  instvalid   <=  `InstValid;
                end
                `EXE_XOR: begin                   // xor指令
                  wreg_o      <=  `WriteEnable;
                  aluop_o     <=  `EXE_XOR_OP ;   // 8'b 0010_0110
                  alusel_o    <=  `EXE_RES_LOGIC;
                  reg1_read_o <=  1'b1;
                  reg2_read_o <=  1'b1;
                  instvalid   <=  `InstValid;
                end
                `EXE_NOR: begin                   // nor指令
                  wreg_o      <=  `WriteEnable;
                  aluop_o     <=  `EXE_NOR_OP;    // 8'b 0010_0111
                  alusel_o    <=  `EXE_RES_LOGIC;
                  reg1_read_o <=  1'b1;
                  reg2_read_o <=  1'b1;
                  instvalid   <=  `InstValid;
                end
                `EXE_SLLV:  begin                 // sllv指令
                  wreg_o      <=  `WriteEnable;
                  aluop_o     <=  `EXE_SLL_OP;    // 8'b 0000_0100
                  alusel_o    <=  `EXE_RES_SHIFT; 
                  reg1_read_o <=  1'b1;
                  reg2_read_o <=  1'b1;
                  instvalid   <=  `InstValid;
                end
                `EXE_SRLV:  begin                 // srlv指令
                  wreg_o      <=  `WriteEnable;
                  aluop_o     <=  `EXE_SRL_OP;    // 8'b 0000_0110
                  alusel_o    <=  `EXE_RES_SHIFT;
                  reg1_read_o <=  1'b1;
                  reg2_read_o <=  1'b1;
                  instvalid   <=  `InstValid;
                end
                `EXE_SRAV:  begin                 // srav指令
                  wreg_o      <=  `WriteEnable;
                  aluop_o 	   <=  `EXE_SRA_OP;    	// 8'b 0000_0111
                  alusel_o    <=  `EXE_RES_SHIFT;
                  reg1_read_o <=  1'b1;
                  reg2_read_o <=  1'b1;
                  instvalid   <=  `InstValid;
                end
                default:    begin
                end    
              endcase   // case (op3)
            end
            default:  begin
            end
          endcase   // case(op2)
        end  
        `EXE_ORI: begin               // 依据op的值判断指令是否为ori指令
          // ori指令需要将结果写入目的寄存器
          wreg_o      <=  `WriteEnable;
          // 运算子类型是逻辑'or'运算
          aluop_o     <=  `EXE_OR_OP;
          // 运算类型是逻辑运算
          alusel_o    <= `EXE_RES_LOGIC;
          // 需要通过regfile的读端口1读取寄存器
          reg1_read_o <=  `WriteEnable;
          // 不需要通过regfile的读端口2读取寄存器
          reg2_read_o <=  `WriteDisable;
          // 指令执行需要的立即数,无符号扩展
          imm         <=  {16'h0, inst_i[15:0]};
          // 指令执行要写的目的寄存器地址
          wd_o        <=  inst_i[20:16];
          // ori指令是有效指令
          instvalid   <=  `InstValid;
        end
        `EXE_ANDI:  begin                         // andi指令
          wreg_o      <=  `WriteEnable;
          aluop_o     <=  `EXE_AND_OP;            // 8'b 0000_1100
          alusel_o    <=  `EXE_RES_LOGIC;
          reg1_read_o <=  1'b1;
          reg2_read_o <=  1'b0;
          imm         <=  {16'h0, inst_i[15:0]};
          wd_o        <=  inst_i[20:16];
          instvalid   <=  `InstValid;
        end
        `EXE_XORI:  begin                         // xori指令
          wreg_o      <=  `WriteEnable;
          aluop_o     <=  `EXE_XOR_OP;            // 8'b 0010_0100
          alusel_o    <=  `EXE_RES_LOGIC;         
          reg1_read_o <=  1'b1;
          reg2_read_o <=  1'b0;
          imm         <=  {16'h0, inst_i[15:0]};
          wd_o        <=  inst_i[20:16];
          instvalid   <=  `InstValid;
        end
        `EXE_LUI:   begin                         // lui指令
          wreg_o      <=  `WriteEnable;
          aluop_o     <=  `EXE_OR_OP;             // 8'b 0010_0101
          alusel_o    <=  `EXE_RES_LOGIC;
          reg1_read_o <=  1'b1;
          reg2_read_o <=  1'b0;
          imm         <=  {inst_i[15:0], 16'h0};
          wd_o        <=  inst_i[20:16];
          instvalid   <=  `InstValid;
        end
        default:    begin
        end
      endcase // case(op)
      if (inst_i[31:21] == 11'b000_0000_0000) begin
        if (op3 == `EXE_SLL)  begin               // sll指令
          wreg_o      <=  `WriteEnable;
          aluop_o     <=  `EXE_SLL_OP;            // 8'b 0000_0000
          alusel_o    <=  `EXE_RES_SHIFT;
          reg1_read_o <=  1'b0;
          reg2_read_o <=  1'b1;
          imm[4:0]    <=  inst_i[10:6];
          wd_o        <=  inst_i[15:11];
          instvalid   <=`InstValid;
        end else if (op3 == `EXE_SRL) begin       // srl指令
          wreg_o      <=  `WriteEnable;
          aluop_o     <=  `EXE_SRL_OP;            // 8'b 0000_0010
          alusel_o    <=  `EXE_RES_SHIFT;
          reg1_read_o <=  1'b0;
          reg2_read_o <=  1'b1;
          imm[4:0]    <=  inst_i[10:6];          
          wd_o        <=  inst_i[15:11];
          instvalid   <=  `InstValid;
        end else if (op3 == `EXE_SRA) begin       // sra指令
          wreg_o      <=  `WriteEnable;
          aluop_o     <=  `EXE_SRA_OP;            // 8'b 0000_0011
          alusel_o    <=  `EXE_RES_SHIFT;
          reg1_read_o <=  1'b0;
          reg2_read_o <=  1'b1;
          imm         <=  inst_i[10:6];
          wd_o        <=  inst_i[15:11];
          instvalid   <=  `InstValid;
        end
      end
    end //else
  end //always
  // ****************************************************************************
  // ********************第二阶段:确定进行运算的操作数1 ******************************
  // ****************************************************************************   
  always @ (*) begin
    if (rst == `RstEnable) begin
      reg1_o  <=  `ZeroWord;
    end else if (reg1_read_o == 1'b1) begin
      reg1_o  <=  reg1_data_i;    // regfile读端口1的输出值
    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
      reg2_o  <=  reg2_data_i;    // regfile读端口2的输出值
    end else if (reg2_read_o == 1'b0) begin
      reg2_o  <=  imm;            // 立即数
    end else begin
      reg2_o  <=  `ZeroWord;
    end
  end    

endmodule
  
3. 执行ex

为逻辑运算和移位运算添加新的分支

// 从ID模块得到运算类型alusel_i、运算子类型aluse_i、源操作数reg1_i、源操作数reg2_i、要写入的目的寄存器地址wd_i
// EX模块依据这些数据进行运算
`include "defines.v"

module ex (
  // 执行的结果
  output  reg   [`RegAddrBus]   wd_o,         // 最终要写入的目的寄存器地址
  output  reg                   wreg_o,       // 最终是否有要写入的目的寄存器
  output  reg   [`RegBus]       wdata_o,      // 最终要写入目的寄存器的值
  
  input   wire                  rst,
  // 译码阶段送到执行阶段的信息
  input   wire  [`AluOpBus]     aluop_i,      // 执行阶段运算子类型
  input   wire  [`AluSelBus]    alusel_i,     // 执行阶段运算类型
  input   wire  [`RegBus]       reg1_i,       // 执行阶段源操作数1
  input   wire  [`RegBus]       reg2_i,       // 执行阶段源操作数2
  input   wire  [`RegAddrBus]   wd_i,         // 执行阶段要写入的目的寄存器地址
  input   wire                  wreg_i        // 执行阶段是否有要写入的目的寄存器
);
  // 保存逻辑运算的结果
  reg   [`RegBus]   logicout;
  reg   [`RegBus]   shiftres;
  
  // ****************************************************************************
  // ***************第一阶段:依据aluop_i指示的运算子类型进行运算*********************
  // ****************************************************************************
  // ********** 逻辑运算 **********
  always @ (*) begin
    if (rst == `RstEnable) begin
      logicout  <=  `ZeroWord;
    end else begin
      case (aluop_i)
        `EXE_OR_OP:   begin
          logicout  <=  reg1_i | reg2_i;
        end
        `EXE_AND_OP:  begin
          logicout  <=  reg1_i & reg2_i;
        end
        `EXE_NOR_OP:  begin
          logicout  <=  ~(reg1_i | reg2_i);
        end
        `EXE_XOR_OP:  begin
          logicout  <=  reg1_i ^ reg2_i;
        end
        default:      begin
          logicout  <=  `ZeroWord;
        end
      endcase // case
    end // if
  end // always
  // ********** 移位运算 **********
  always @ (*) begin
    if (rst == `RstEnable) begin
      shiftres  <=  `ZeroWord;
    end else begin
      case (aluop_i)
        `EXE_SLL: begin
          shiftres  <=  reg2_i << reg1_i[4:0];
        end
        `EXE_SRL: begin
          shiftres  <=  reg2_i >> reg1_i[4:0];
        end
        `EXE_SRA: begin
          shiftres  <=  ({32{reg2_i[31]}} << (6'd32-{1'b0, reg1_i[4:0]})) | reg2_i >> reg1_i[4:0];
        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;   // wdata_o中存放运算结果
      end
      `EXE_RES_SHIFT: begin
        wdata_o <=  shiftres;
      end
      default:        begin
        wdata_o <=  `ZeroWord;
      end
    endcase // case
  end // always  

endmodule

4. 通用寄存器RegFile

重新修改读端口2的取数顺序,解决取操作数2不准确的情况

// 寄存器堆栈,实现了32个32位通用整数寄存器,可以同时进行两个寄存器的读操作和一个寄存器的写操作
`include	"defines.v"

module regfile (
  // 写端口
  input   wire                  we,          // 写使能信号
  input   wire  [`RegAddrBus]   waddr,       // 要写入的寄存器地址
  input  reg   [`RegBus]       wdata,       // 要写入的数据
  
  // 读端口1
  input   wire                  re1,          // 第一个读寄存器端口读使能信号   
  input   wire  [`RegAddrBus]   raddr1,       // 第一个读寄存器端口要读取的寄存器的地址
  output  reg   [`RegBus]       rdata1,       // 第一个读寄存器端口输出的寄存器值
  // 读端口2
  input   wire                  re2,          // 第二个读寄存器端口读使能信号
  input   wire  [`RegAddrBus]   raddr2,       // 第二个读寄存器端口要读取的寄存器的地址
  output  reg   [`RegBus]       rdata2,       // 第二个读寄存器端口输出的寄存器值
  
  input   wire                  clk,
  input   wire                  rst
);

// **********************第一阶段:定义32个32位寄存器************************
reg     [`RegBus]     regs[0:`RegNum-1];

// **************************第二阶段:写操作******************************
  always @ (posedge clk) begin
    if (rst == `RstDisable) begin  // 复位信号无效
      if ((we ==`WriteEnable) && (waddr != `RegNumLog2'h0)) begin  // 写使能&&写目的寄存器≠$0
        regs[waddr] <= wdata;
      end
    end
  end
  
// *************************第三阶段:读端口1的读操作******************************
  always @ (*) begin
    if (rst == `RstEnable) begin
      rdata1  <= `ZeroWord;     // 复位信号有效,输出0
    end else if (raddr1 == `RegNumLog2'h0) begin    // 读目的寄存器为$0,输出0
      rdata1  <= `ZeroWord;
    end else if (re1 == `ReadEnable) begin    // 输出要读取的目的寄存器地址对应的值
      rdata1  <= regs[raddr1];
    end else if ((raddr1 == waddr) && (we == `WriteEnable) && (re1 == `ReadEnable)) begin    // 读目的寄存器是写目的寄存器&&写使能&&读使能,输出写入的值
      rdata1  <= wdata;
    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 (re2 == `ReadEnable) begin
      rdata2  <= regs[raddr2];
    end else if ((raddr2 == waddr) && (we == `WriteEnable) && (re2 == `ReadEnable)) begin
      rdata2  <= wdata;
    end else begin
      rdata2  <= `ZeroWord;
    end
  end
endmodule


测试模块

测试逻辑指令

在这里插入图片描述

仿真波形
逻辑1

测试移位指令

在这里插入图片描述

仿真波形
移位1
移位2

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值