自己动手写CPU——单周期ORI指令的实现

写在最前面,后续的一些指令我还在慢慢学,在这个框架上继续增加,有兴趣可以私信我,代码可以互相分享学习

ORI指令说明

ORI指令格式
这是一个I型指令,ORI指令的指令码为6’b00_1101
ORI指令作用:将16位立即数immediate进行无符号扩展至32位,然后与索引为rs的通用寄存器里的值进行“或”运算,运算结果保存到索引为rt的通用寄存器中
扩展1:立即数扩展

n位立即数扩展到3位。符号扩展是将n位立即数的最高为复制到扩展后的32位数据的高(32-n)位,无符号扩展是将扩展后的32位数据的高(32-n)位都置0
16位立即数: 0x8000、0x8000
符号扩展: 0xFFFF8000、0x00001000
无符号扩展: 0x00008000、0x00001000

扩展2:寄存器

MIPS架构定义了32个通用寄存器,使用$0、$1、…、%31表示,索引5位。其中%0一般用作常量0。

寄存器名字约定命名用途
$0zero总是为0
$1at留作汇编器生成一些合法指令
$2、$3v0、v1用来存放程序返回值
$4-$7a0-a3调用子程序时,使用这4个寄存器传输前4个非浮点参数
$8-$15t0-t7临时寄存器 、子程序使用时可以不用存储和恢复
$16-$23s0-s7子程序寄存器变量,改变这些寄存器值的子程序必须存储旧的值并在退出前恢复,对调用程序来说值不变
$24、$25t7、t9临时寄存器,子程序使用时可以不用存储和恢复
$26、$27 k 0 、 k0、 k0k1由异常处理程序使用
28 或 28或 28gpgp全局指针
29 或 29或 29spsp堆栈指针
30 或 30或 30fpfp子程序可以用来作堆栈帧指针
$31ra存放程序返回地址

单周期系统结构框图

1

模块划分以及功能代码实现

1. 宏定义define
// *****************  全局的宏定义  ***************
`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_ORI         6'b001101     // 指令ori的指令码
`define EXE_NOP         6'b000000     // 

//AluOp   子类型
`define EXE_OR_OP       8'b00100101   // or_func
`define EXE_NOP_OP      8'b00000000
//AluSel  类型
`define EXE_RES_LOGIC   3'b001        // 逻辑运算
`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. 取指pc
// 取出指令存储器中的指令、PC增值,准备取下一条指令
`include "defines.v"
module pc_reg (
  output  reg [`InstAddrBus]  pc,     // 要读取的指令地址
  output  reg                 ce,     // 指令存储器使能信号
  input   wire                clk,    // 时钟信号
  input   wire                rst     // 复位信号 
);
  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'h00000000;        // 指令存储器禁用的时候,pc为0
    end else begin
      pc <= pc + 4'h4;        // 指令存储器使能的时候,pc值每时钟周期+4字节
    end
  end
  
endmodule
3. 译码id
// 对指令进行译码,得到最终运算的类型、子类型、源操作数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];    // 高6位决定具体操作
  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];      // rd,目的寄存器编号
      wreg_o    <=  `WriteDisable; 
      instvalid <=  `InstInvalid;       // 指令无效
      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_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
        default: begin
        end  
      endcase // case
    end //if
  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      
4. 通用寄存器RegFile
// 寄存器堆栈,实现了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 ((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

/*
注:
读操作时组合逻辑电路,输入的要读取的寄存器地址raddr1和raddr2发生变化,立刻得到新地址对应的寄存器的值,这样可以保证在译码阶段取得要读取的寄存器的值
写操作时时序逻辑电路,发生在时钟上升沿
*/


5. 执行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;
  
  // ****************************************************************************
  // ***************第一阶段:依据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
        default:    begin
          logicout  <=  `ZeroWord;
        end
      endcase // case
    end // if
  end // always
  // ****************************************************************************
  // **********第二阶段:依据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
      default:        begin
        wdata_o <=  `ZeroWord;
      end
    endcase // case
  end // always  

endmodule
6.访存mem
// mem访存模块
`include "defines.v"

module mem (
  output  reg   [`RegAddrBus]   wd_o,     // 传到回写阶段的寄存器地址
  output  reg                   wreg_o,   // 是否有写入
  output  reg   [`RegBus]       wdata_o,  // 传到回写阶段的数据
  
  input   wire                  rst,
  input   wire  [`RegAddrBus]   wd_i,
  input   wire                  wreg_i,
  input   wire  [`RegBus]       wdata_i
);

  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


7. 回写wb
// 将输入的执行阶段的结果直接作为输出
`include "defines.v"

module wb (
  // 访存阶段的结果
  output  reg   [`RegAddrBus]   wd_o,       // 访存阶段的指令最终要写入的目的寄存器地址
  output  reg                   wreg_o,     // 访存阶段的指令最终是否有要写入的目的寄存器
  output  reg   [`RegBus]       wdata_o,    // 访存阶段的指令最终要写入目的寄存器的值
  
  input   wire                  rst,
  // 来自执行阶段的信息
  input   wire  [`RegAddrBus]   wd_i,
  input   wire                  wreg_i,
  input   wire  [`RegBus]       wdata_i  
);

  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 
8 .顶模块OpenMIPS
// 顶层模块
`include "defines.v"

module openmips (
  output  wire    [`RegBus]   rom_addr_o,     // 输出到指令存储器的地址
  output  wire                rom_ce_o,       // 指令寄存器使能信号
  
  input   wire    [`RegBus]   rom_data_i,     // 从指令寄存器取得的指令
  input   wire                rst,
  input   wire                clk
);

  // ****************************************************************************
  // **************************第一阶段:物理连线声明******************************
  // **************************************************************************** 
  
  // 连接ID模块和EX模块输入的变量
  wire  [`AluOpBus]     id_aluop;
  wire  [`AluSelBus]    id_alusel;
  wire  [`RegBus]       id_reg1;
  wire  [`RegBus]       id_reg2;
  wire                  id_wreg;
  wire  [`RegAddrBus]   id_wd;
  
  // 连接译码阶段ID模块和通用寄存器Regfile模块的变量
  wire                  reg1_read;
  wire                  reg2_read;
  wire  [`RegBus]       reg1_data;
  wire  [`RegBus]       reg2_data;
  wire  [`RegAddrBus]   reg1_addr;
  wire  [`RegAddrBus]   reg2_addr;

  // 连接EX模块输出和MEM模块的变量
  wire  [`RegAddrBus]   ex_wd;
  wire                  ex_wreg;
  wire  [`RegBus]       ex_wdata;  
    
  // 连接MEM模块输出和WB模块的变量
  wire  [`RegAddrBus]   mem_wd;
  wire                  mem_wreg;
  wire  [`RegBus]       mem_wdata;
  
  // 连接WB模块和RegFIle模块输入的变量
  wire  [`RegAddrBus]   wb_wd;
  wire                  wb_wreg;
  wire  [`RegBus]       wb_wdata;
  
  // ****************************************************************************
  // **********************第二阶段:对各个模块进行实例化*****************************
  // ****************************************************************************
  // pc_reg例化
  pc_reg  pc_reg0 (
          .pc(rom_addr_o), .ce(rom_ce_o),
          
          .clk(clk), .rst(rst)
  );
  // 译码阶段ID模块例化
  id      id0 (
          // 送到RegFile模块的信息
          .reg1_read_o(reg1_read), .reg2_read_o(reg2_read),
          .reg1_addr_o(reg1_addr), .reg2_addr_o(reg2_addr),
          // 送到EX模块的信息
          .aluop_o(id_aluop), .alusel_o(id_alusel),
          .reg1_o(id_reg1), .reg2_o(id_reg2),
          .wd_o(id_wd), .wreg_o(id_wreg),
          
          .rst(rst), .pc_i(rom_addr_o), .inst_i(rom_data_i),
          // 来自RegFile模块的输出
          .reg1_data_i(reg1_data), .reg2_data_i(reg2_data)
  );
  // 通用寄存器Regfile模块例化
  regfile regfile0 (
          .we(wb_wreg), .waddr(wb_wd), .wdata(wb_wdata),
          .re1(reg1_read), .raddr1(reg1_addr), .rdata1(reg1_data),
          .re2(reg2_read), .raddr2(reg2_addr), .rdata2(reg2_data),
          
          .clk(clk), .rst(rst)
  );
  // EX模块例化
  ex      ex0 (
          // 输出到MEM模块的信息
          .wd_o(ex_wd), .wreg_o(ex_wreg), .wdata_o(ex_wdata),
          
          .rst(rst),
          // 从ID模块传递过来的信息
          .aluop_i(id_aluop), .alusel_i(id_alusel),
          .reg1_i(id_reg1), .reg2_i(id_reg2),
          .wd_i(id_wd), .wreg_i(id_wreg)
  );
  
  // MEM模块例化
  mem     mem0 (
          // 输出到WB模块的信息
          .wd_o(mem_wd), .wreg_o(mem_wreg), .wdata_o(mem_wdata),
          
          .rst(rst),
          // 从EX模块接收到的信息
          .wd_i(ex_wd), .wreg_i(ex_wreg), .wdata_i(ex_wdata)
  );
  
  // WB模块例化
  wb     wb0 (
          // 输出的信息
          .wd_o(wb_wd), .wreg_o(wb_wreg), .wdata_o(wb_wdata),
          
          .rst(rst),
          // 从EX模块接收到的信息
          .wd_i(ex_wd), .wreg_i(ex_wreg), .wdata_i(ex_wdata)
  );

endmodule
9 .指令存储器
// 指令存储器ROM模块
`include "defines.v"

module inst_rom (
  output  reg   [`InstBus]      inst,     // 读出的指令
  
  input   wire                  ce,       // 使能信号
  input   wire  [`InstAddrBus]  addr      // 要读取的指令地址
);

  // 定义一个数组,大小是InstMemNum(131071),元素宽度是InstBus
  reg   [`InstBus]    inst_mem[0:`InstMemNum-1];
  
  // 使用文件inst_rom.data初始化指令存储器
  initial $readmemh ("inst_rom.dat", inst_mem);
  
  // 当复位信号无效时,依据输入的地址,给出指令存储器ROM中对应的元素
  always @ (*) begin
    if (ce == `ChipDisable) begin
      inst   = `ZeroWord;
    end else begin
      inst  <= inst_mem[addr[`InstMemNumLog2+1:2]];    // inst_mem[addr[5+1:2]]  
    end
  end

endmodule
10 .最小SOPC实现
// 最小SOPC模块,仅包括OpenMIPS和指令存储器ROM,例化处理这个两个模块
`include "defines.v"

module OpenMIPS_min_SOPC (
  input wire    clk,
  input wire    rst
);

  // 连接指令存储器
  wire  [`InstAddrBus]    inst_addr;
  wire  [`InstBus]        inst;
  wire                    rom_ce;
  
  // 例化处理器OpenMIPS
  openmips  openmips0 (
            .rom_addr_o(inst_addr), .rom_ce_o(rom_ce),
        
            .rom_data_i(inst),
            .clk(clk), .rst(rst)
  );
  
  // 例化指令存储器
  inst_rom  inst_rom0 (
            .inst(inst),
            
            .ce(rom_ce), .addr(inst_addr)
  );
  
endmodule

测试模块以及仿真波形

// testbench of "OpenMIPS_mini_SOPC_tb"
`include "defines.v"

module OpenMIPS_min_SOPC_tb ();
  reg   clk;
  reg   rst;

  module OpenMIPS_min_SOPC_tb ();
  reg   clk;
  reg   rst;
  
  // 例化最小SOPC
  OpenMIPS_min_SOPC OpenMIPS_min_SOPC0 (
                    .clk(clk), .rst(rst)
  );
  
  initial begin
    clk = 1'b0;
  end
  always #10 clk = ~clk; 
  
  // 最初时刻,复位信号有效,在第195ns,复位信号无效,最小SOPC开始运行
  initial begin
          rst = `RstEnable;
    #195  rst = `RstDisable;
//    #1000 $stop;
  end
  
endmodule

1
2
3

指令存储文件inst_rom.dat

记事本中存放数据:

34011100 // $1 = 0000_0000 | 0000_1100 = 0000_1100
34020020 // $2 = 0000_0000 | 0000_0020 = 0000_0020
3403ff00 // $3 = 0000_0000 | 0000_ff00 = 0000_ff00
3404ffff // $4 = 0000_0000 | 0000_ffff = 0000_ffff
34210020 // $1 = 0000_1100 | 0000_0020 = 0000_1120
34214400 // $1 = 0000_1120 | 0000_4400 = 0000_5520
34210044 // $1 = 0000_5520 | 0000_0040 = 0000_5564

------------------------------------------------------------------------------------------------------------------------------------------------------

遇到的问题以及解决

1. 仿真波形输错_1

部分波形

  • 这里的reg1在执行完第五个周期时变为不定值是什么原因?

修改所有错误后,reg1_i等信号在第五个周期变为不定值都得到纠正

  • 有部分仿真波形从始至终都是蓝色直线?

在openmips.v中的实例化中端口对应没有匹配好,已修改,蓝线消失
ex ex0 (
.wreg_i(id_wreg),
// 错误代码:.wreg_i(ex_wreg),
);

2. 仿真波形出错_2

部分波形_2

  • sim:/OpenMIPS_min_SOPC_tb/OpenMIPS_min_SOPC0/openmips0/wb_wdata 和sim:/OpenMIPS_min_SOPC_tb/OpenMIPS_min_SOPC0/openmips0/regfile0/wdata 两条仿真波形始终都是红色直线?

对于没有初始化的信号,输入信号为高阻态蓝线(Z),输出信号为不定态红线(X)
regfile.v模块中的信号输入输出没有定义好,已修改,红线消失
module regfile (
input reg ['RegBus] wdata, // 要写入的数据
// 错误代码:output reg ['RegBus] wdata,
);

3. 顶层模块段端口定义

两个输出信号rom_addr_o、rom_ce_o需要定义成wire。定义成reg会报错,编译可通过,仿真不通过。代码已修改
在这里插入图片描述

4. 解决取操作数不准确

指令5开始,rdata1本应该从寄存器$1读出上次写入的数据0x0000_1100,因为读写地址都相同,同时满足regfile.v中的读端口1读数据的结果值,实际读出指令5的执行结果0x0000_1120(0x0000_0000 ori ox0000_0020)
流水添加的是数据前推端口,没有这种困扰

解决方法1:
regfile.v读端口1模块使用if-case分支,顺序执行,交换分支顺序,优先判断是否从寄存器直接取到源操作数。当读写地址相同时,两个else都满足,但实际我们想要的是从寄存器中读出来的数据,为了防止被"被短路",需要交换顺序,先判断是否从寄存器取数
1

解决方法2:
在EX模块新添加寄存器模块reg [`RegBus] result;连线到regfile.v模块
若想实现,必须添加在指定位置。
(太乱了,实在看不懂为什么都是哪些值,只能保证正确的值存在result中,但其他信号还会有取错的,我投降)


2021/05/09
再回来看看读取操作数不正确的原因,方法1看不懂,方法2当时跑通测试,但是没有写笔记…
救命,以后我一定好好写笔记,尽量不省略
我还写的没有这种困扰…现在都不知道是啥困扰了,还别说解决

.
.
.
会了
这里是单周期

// *************************第三阶段:读端口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 ((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的操作。因为指令如果是寄存器和立即数进行运算,默认通过读端口1读取寄存器数据
因为是单周期,在一个节拍下就要进行取数、执行、回写,交换之后就能保证,读取的数据是之前写入寄存器的数据,而不是本条指令执行后的运算结果。

流水线我也忘了需不需要处理这个读数据错误

一、 设计目标 设计目的: 设计一个含有36条指令的MIPS单周期处理器,并能将指令准确的执行并烧到试验箱上来验证 设计初衷 1、理解MIPS指令结构,理解MIPS指令集中常用指令的功能和编码,学会对这些指令进行归纳分类。 2、了解熟悉MIPS体系中的处理器结构 3、熟悉并掌握单周期处理器CPU的原理和设计 4、进一步加强Verilog语言进行电路设计的能力 二、实验设备 1、装有xilinx ISE的计算机一台 2、LS-CPU-EXB-002教学系统实验箱一台 三、实验任务 1.、学习 MIPS 指令集,深入理解常用指令的功能和编码,并进行归纳确定处理器各部件的控制码,比如使用何种 ALU 运算,是否寄存器堆等。 2、单周期 CPU 是指一条指令的所有操作在一个时钟周期内执行完。设计中所有寄存器和存储器都是异步读同步的,即读出数据不需要时钟控制,但入数据需时钟控制。 故单周期 CPU 的运作即:在一个时钟周期内,根据 PC 值从指令 ROM 中读出相应的指令,将指令译码后从寄存器堆中读出需要的操作数,送往 ALU 模块,ALU 模块运算得到结果。 如果是 store 指令,则 ALU 运算结果为数据存储的地址,就向数据 RAM 发出请求,在下一个时钟上升沿真正入到数据存储器。 如果是 load 指令,则 ALU 运算结果为数据存储的地址,根据该值从数据存 RAM 中读出数据,送往寄存器堆根据目的寄存器发出请求,在下一个时钟上升沿真正入到寄存器堆中。 如果非 load/store 操作,若有寄存器堆的操作,则直接将 ALU 运算结果送往寄存器堆根据目的寄存器发出请求,在下一个时钟上升沿真正入到寄存器堆中。 如果是分支跳转指令,则是需要将结果入到 pc 寄存器中的。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值