多周期CPU设计(Verilog) (更新:2017/5/29)

注:单周期CPU设计请移步我的另一篇博文:
单周期CPU设计(Verilog)


一、 实验目的

(1) 认识和掌握多周期数据通路原理及其设计方法;
(2) 掌握多周期CPU的实现方法,代码实现方法;
(3) 编写一个编译器,将MIPS汇编程序编译为二进制机器码;
(4) 掌握多周期CPU的测试方法。

二、 实验内容

设计一个多周期CPU,该CPU至少能实现以下指令功能操作。需设计的指令与格式如下:
(说明:操作码按照以下规定使用,都给每类指令预留扩展空间,后续实验相同。)
多周期CPU设计(Verilog) (更新:2017/5/29)_第1张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第2张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第3张图片
这里写图片描述

三、 实验原理

多周期CPU指的是将整个CPU的执行过程分成几个阶段,每个阶段用一个时钟去完成,然后开始下一条指令的执行,而每种指令执行时所用的时钟数不尽相同,这就是所谓的多周期CPU。CPU在处理指令时,一般需要经过以下几个阶段:

(1) 取指令(IF):根据程序计数器pc中的指令地址,从存储器中取出一条指令,同时,pc根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入pc,当然得到的“地址”需要做些变换才送入pc。
(2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。
(3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。
(4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。
(5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。

实验中就按照这五个阶段进行设计,这样一条指令的执行最长需要五个(小)时钟周期才能完成,但具体情况怎样?要根据该条指令的情况而定,有些指令不需要五个时钟周期的,这就是多周期的CPU。

多周期CPU设计(Verilog) (更新:2017/5/29)_第4张图片

多周期CPU设计(Verilog) (更新:2017/5/29)_第5张图片

多周期CPU设计(Verilog) (更新:2017/5/29)_第6张图片

多周期CPU设计(Verilog) (更新:2017/5/29)_第7张图片

多周期CPU设计(Verilog) (更新:2017/5/29)_第8张图片

特别提示,图上增加IR指令寄存器,目的是使指令代码保持稳定,pc写使能控制信号PCWre,是确保pc适时修改,原因都是和多周期工作的CPU有关。ADR、BDR、ALUoutDR、DBDR四个寄存器不需要写使能信号,其作用是切分数据通路,将大组合逻辑切分为若干个小组合逻辑,大延迟变为多个分段小延迟。

多周期CPU设计(Verilog) (更新:2017/5/29)_第9张图片

多周期CPU设计(Verilog) (更新:2017/5/29)_第10张图片

相关部件及引脚说明:

  • Instruction Memory:指令存储器
    • Iaddr,指令地址输入端口
    • DataIn,存储器数据输入端口
    • DataOut,存储器数据输出端口
    • RW,指令存储器读写控制信号,为0写,为1读
  • Data Memory:数据存储器
    • Daddr,数据地址输入端口
    • DataIn,存储器数据输入端口
    • DataOut,存储器数据输出端口
    • /RD,数据存储器读控制信号,为0读
    • /WR,数据存储器写控制信号,为0写
  • Register File:寄存器组
    • Read Reg1,rs寄存器地址输入端口
    • Read Reg2,rt寄存器地址输入端口
    • Write Reg,将数据写入的寄存器,其地址输入端口(rt、rd)
    • Write Data,写入寄存器的数据输入端口
    • Read Data1,rs寄存器数据输出端口
    • Read Data2,rt寄存器数据输出端口
    • WE,写使能信号,为1时,在时钟上升沿写入
  • IR: 指令寄存器,用于存放正在执行的指令代码
  • ALU: 算术逻辑单元
    • result,ALU运算结果
    • zero,运算结果标志,结果为0输出1,否则输出0

多周期CPU设计(Verilog) (更新:2017/5/29)_第11张图片
这里写图片描述

四、实验设备

PC机一台,BASYS 3 实验板一块,Xilinx Vivado 开发软件一套。

五、实验分析与设计

多周期CPU设计(Verilog) (更新:2017/5/29)_第12张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第13张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第14张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第15张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第16张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第17张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第18张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第19张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第20张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第21张图片
多周期CPU设计(Verilog) (更新:2017/5/29)_第22张图片

六、实验心得

多周期CPU设计(Verilog) (更新:2017/5/29)_第23张图片

七、代码

代码放的次序有点乱,实现模块的顺序可以参见我的实验过程设计,也就是第五部分!

// 顶层模块的实现
module Main(CLK, RST, outside_pc, ins, now_pc);
  input CLK, RST;
  input [31:0] outside_pc;
  output [31:0] ins, now_pc;
  parameter endReg = 5'b11111; // 31号寄存器

  // 数据通路
  wire [31:0] pc, pc0, pc4, i_IR, instruction, pcChoose3, pcChoose1, extendData, ALUResult, WriteData, ReadData1, ReadData2, DataOut;
  wire [31:0] o_ADR, o_BDR, o_ALUout, i_ALUM2DR,i_ALUData1,i_ALUData2;
  wire zero;
  // 控制信号
  wire [2:0] ALUOp;
  wire [1:0] ExtSel, RegDst, PCSrc;
  wire PCWre, IRWre, InsMemRW, WrRegData, RegWre, ALUSrcB, DataMemRW, DBDataSrc;

  // 数据选择输出
  wire [4:0] fiveChooseData;
  wire [31:0] InputWriteData1;

  // 引脚输出
  assign ins = instruction;
  assign now_pc = pc0;

  PC PC(CLK, pc, PCWre, RST, outside_pc, pc0); // 添加了外部pc
  PCAddFour PCAddFour(pc0, pc4);
  InstructionMEM InstructionMEM(pc0, InsMemRW, i_IR); // 添加了外部pc
  IR IR(i_IR, CLK, IRWre, instruction);
  PCJump PCJump(pc0, instruction[25:0], pcChoose3);
  DataSelector_3to1 DataSelector_3to1(endReg, instruction[20:16], instruction[15:11], RegDst, fiveChooseData);
  RegFile RegFile(instruction[25:21], instruction[20:16], fiveChooseData, WriteData, RegWre, CLK, ReadData1, ReadData2);
  ADR ADR(ReadData1, CLK, o_ADR);
  BDR BDR(ReadData2, CLK, o_BDR);
  SignExtend SignExtend(instruction[15:0], ExtSel, extendData);
  DataSelector_2to1_sa DataSelector_2to1_1(o_ADR, instruction[10:6] , ALUSrcA, i_ALUData1);
  DataSelector_2to1 DataSelector_2to1_2(o_BDR, extendData, ALUSrcB, i_ALUData2);
  ALU ALU(i_ALUData1, i_ALUData2, ALUOp, zero, ALUResult);
  ALUoutDR ALUoutDR(ALUResult, CLK, o_ALUout);
  DataMEM DataMEM(o_BDR, o_ALUout, DataMemRW, DataOut);
  DataSelector_2to1 DataSelector_2to1_3(ALUResult, DataOut, DBDataSrc, i_ALUM2DR);
  DBDR DBDR(i_ALUM2DR, CLK, InputWriteData1);
  DataSelector_2to1 DataSelector_2to1_4(pc4, InputWriteData1, WrRegData, WriteData);
  PCAddImm PCAddImm(pc4, extendData, pcChoose1);
  DataSelector_4to1 DataSelector_4to1(pc4, pcChoose1, ReadData1, pcChoose3, PCSrc, pc);
  ControlUnit ControlUnit(instruction[31:26], CLK, RST, zero, PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcA, ALUSrcB, DataMemRW, DBDataSrc, ExtSel, RegDst, PCSrc, ALUOp);
endmodule
// PC模块的实现
// @param clk 时钟信号
// @param pcWre信号
// @param reset信号
// @param i_pc 输入的pc值
// @param o_pc 输出的pc值
// @param outside_pc ???
module PC(clk, i_pc, pcWre, reset, outside_pc, o_pc);
  input wire clk, pcWre, reset;
  input wire [31:0] i_pc, outside_pc;
  output reg [31:0] o_pc;
  always @(pcWre or reset) begin // 这里和单周期不太一样,存在延迟的问题,只有当pcWre改变的时候或者reset改变的时候再检测
   // reset
    if (reset) begin
      o_pc = outside_pc;
    end else if (pcWre) begin
      o_pc = i_pc;
    end else if (!pcWre) begin 
        o_pc = o_pc;
     end
  end
endmodule
// 实现PC递增
// @param i_pc 输入的pc值
// @param o_pc 输出的pc值
module PCAddFour(i_pc, o_pc);
  input wire [31:0] i_pc;
  output wire [31:0] o_pc;
  assign o_pc[31:0] = i_pc[31:0] + 4;
endmodule
// 指令存储单元的实现
// @param InsMemRW 指令存储单元信号
// @param addr pc上指令的地址
// @param outside_pc 获取初始化的pc
// @param instruction 取得的指令
module InstructionMEM (addr, InsMemRW, instruction);
    input InsMemRW;
    input [31:0] addr;
    output reg [31:0] instruction;
    // 8位内存单元,每条指令的二进制代码占四个内存单元
    reg [7:0] mem [0:127];
     initial begin
     //$readmemb("D:/Xilinx/VivadoProject/MulticycleCPU/instructions.txt", mem); 
         mem[0]=8'b11100000;
         mem[1]=8'b00000000;
         mem[2]=8'b00000000;
         mem[3]=8'b00000010;
         mem[4]=8'b11100111;
         mem[5]=8'b11100000;
         mem[6]=8'b00000000;
         mem[7]=8'b00000000;
         mem[8]=8'b00001000;
         mem[9]=8'b00000001;
         mem[10]=8'b00000000;
         mem[11]=8'b00000100;
         mem[12]=8'b00001000;
         mem[13]=8'b00000010;
         mem[14]=8'b00000000;
         mem[15]=8'b00001000;
         mem[16]=8'b11000000;
         mem[17]=8'b01000010;
         mem[18]=8'b00000000;
         mem[19]=8'b00000000;
         mem[20]=8'b00000000;
         mem[21]=8'b01000001;
         mem[22]=8'b00011000;
         mem[23]=8'b00000000;
         mem[24]=8'b00000100;
         mem[25]=8'b01100001;
         mem[26]=8'b00011000;
         mem[27]=8'b00000000;
         mem[28]=8'b11010000;
         mem[29]=8'b01000011;
         mem[30]=8'b11111111;
         mem[31]=8'b11111110;
         mem[32]=8'b01001000;
         mem[33]=8'b00100001;
         mem[34]=8'b00000000;
         mem[35]=8'b00000001;
         mem[36]=8'b01000000;
         mem[37]=8'b01000001;
         mem[38]=8'b00011000;
         mem[39]=8'b00000000;
         mem[40]=8'b00000000;
         mem[41]=8'b01000000;
         mem[42]=8'b00011000;
         mem[43]=8'b00000000;
         mem[44]=8'b01000100;
         mem[45]=8'b01100010;
         mem[46]=8'b00001000;
         mem[47]=8'b00000000;
         mem[48]=8'b01100000;
         mem[49]=8'b00000010;
         mem[50]=8'b00001000;
         mem[51]=8'b10000000;
         mem[52]=8'b10011000;
         mem[53]=8'b00100010;
         mem[54]=8'b00110000;
         mem[55]=8'b00000000;
         mem[56]=8'b10011000;
         mem[57]=8'b01000001;
         mem[58]=8'b00111000;
         mem[59]=8'b00000000;
         mem[60]=8'b10011100;
         mem[61]=8'b00100110;
         mem[62]=8'b00000000;
         mem[63]=8'b00000001;
         mem[64]=8'b10011100;
         mem[65]=8'b11000111;
         mem[66]=8'b00000000;
         mem[67]=8'b00000001;
         mem[68]=8'b11101000;
         mem[69]=8'b00000000;
         mem[70]=8'b00000000;
         mem[71]=8'b00000001;
         mem[72]=8'b11000100;
         mem[73]=8'b01000100;
         mem[74]=8'b00000000;
         mem[75]=8'b00000000;
         mem[76]=8'b11111100;
         mem[77]=8'b00000000;
         mem[78]=8'b00000000;
         mem[79]=8'b00000000;
        instruction = 0;
     end
    always @(addr or InsMemRW)
        if (InsMemRW) begin
          instruction[31:24] = mem[addr];
          instruction[23:16] = mem[addr+1];
          instruction[15:8] = mem[addr+2];
          instruction[7:0] = mem[addr+3];
        end
endmodule
// 用于临时存储指令的二进制形式
// @param i_data 输入的数据
// @param clk 时钟信号
// @param IRWre 输入IR的控制信号
// @param o_data 输出的数据
module IR(i_data, clk, IRWre, o_data);
  input clk, IRWre;
  input [31:0] i_data;
  output reg[31:0] o_data;
  always @(negedge clk) begin // 存在延迟的问题,所以用下降沿触发,对数据传输没有什么影响
    if (IRWre) begin
        o_data = i_data;
     end
  end
endmodule
// pc跳转调用子程序
// @param pc 执行该指令时pc的值
// @param i_addr 输入的地址
// @param o_addr 输出的地址

module PCJump(pc, i_addr, o_addr);
  input [31:0] pc;
  input [25:0] i_addr;
  output reg[31:0] o_addr;
  reg [27:0] mid; // 用于存放中间值
  // 输出地址的前四位来自pc[31:28],中间26位来自i_addr[27:2], 后两位是0
  always @(i_addr) begin
     mid = i_addr << 2;
     o_addr <= {pc[31:28], mid[27:0]};
  end
endmodule
// 三选一数据选择器的实现
// @param A 输入1
// @param B 输入2
// @param C 输入3
// @param Control 选择器的控制信号
// @param Result 选择的结果
module DataSelector_3to1(A, B, C, Control, Result);
  input [4:0] A, B, C;
  input [1:0] Control;
  output reg[4:0] Result;
  always @(Control or A or B or C) begin
    case(Control)
        2'b00:Result = A;
        2'b01:Result = B;
        2'b10:Result = C;
        default: Result = 0;
     endcase
  end
endmodule
// 寄存器组的实现
// @param rs 输入数据源1所在的寄存器号
// @param rt 输入数据源2所在的寄存器号
// @param rd 结果存储的寄存器号
// @param i_data 输入的数据
// @param RegWre 输入寄存器组的控制信号
// @param clk 时钟信号
// @param o_data_1 输出数据1
// @param o_data_2 输出数据2
module RegFile (rs, rt, rd, i_data, RegWre, clk, o_data_1, o_data_2);
  input [4:0] rs, rt, rd;
  input [31:0] i_data;
  input RegWre, clk;
  output [31:0] o_data_1, o_data_2;
  reg [31:0] register [0:31];
  initial begin
   // 只需要确定零号寄存器的值就好,$0恒等于0
    register[0] = 0;
  end
  assign o_data_1 = register[rs];
  assign o_data_2 = register[rt];
  always @(i_data or rd) begin
   // rd != 0 是确保零号寄存器不会改变的作用
    if ((rd != 0) && (RegWre == 1)) begin
      register[rd] = i_data;
    end
  end
endmodule
// 切割数据通路
// @param i_data 输入的数据
// @param o_data 输出的数据
// @param clk 时钟信号
module ADR(i_data, clk, o_data);
  input clk;
  input [31:0] i_data;
  output reg[31:0] o_data;
  always @(posedge clk) begin
    o_data = i_data;
  end
endmodule
// 切割数据通路
// @param i_data 输入的数据
// @param o_data 输出的数据
// @param clk 时钟信号
module BDR(i_data, clk, o_data);
  input clk;
  input [31:0] i_data;
  output reg[31:0] o_data;
  always @(posedge clk) begin
    o_data = i_data;
  end
endmodule
// 符号扩展单元的实现
// @param i_num 输入的数据
// @param ExtSel 控制符号扩展单元的信号
// @param o_num 输出的数据
module SignExtend(i_num, ExtSel, o_num);
  input [15:0] i_num;
  input [1:0] ExtSel;
  output reg[31:0] o_num;
  initial begin
    o_num = 0;
  end
  always @(i_num or ExtSel) begin
     case(ExtSel)
        // ExtSel 为00时,sa位扩展
        2'b00: o_num <= {{27{0}}, i_num[10:6]};
        // ExtSel 为01时,无符号立即数扩展
        2'b01: o_num <= {{16{0}}, i_num[15:0]};
        // ExtSel 为10时,有符号立即数扩展
        2'b10: o_num <= {{16{i_num[15]}}, i_num[15:0]};
        // 其它情况默认有符号立即数扩展
        default: o_num <= {{16{i_num[15]}}, i_num[15:0]}; // 默认符号扩展
    endcase
  end
endmodule
// ALU A输入端口前二选一选择器实现
// @param A 输入1
// @param B 输入2
// @param Control 选择器的控制信号
// @param Result 结果
module DataSelector_2to1_sa(A, B, Control, Result);
  input [31:0] A;
  input [4:0] B;
  input Control;
  output [31:0] Result;
  assign Result = (Control == 1'b0 ? A : {{27{0}}, B[4:0]});
endmodule
// 二选一数据选择器实现
// @param A 输入1
// @param B 输入2
// @param Control 选择器的控制信号
// @param Result 结果
module DataSelector_2to1(A, B, Control, Result);
  input [31:0] A, B;
  input Control;
  output [31:0] Result;
  assign Result = (Control == 1'b0 ? A : B);
endmodule
module ALU(A, B, ALUOp, zero, result);
  input [31:0] A, B;
  input [2:0] ALUOp;
  output zero;
  output reg [31:0] result;
  initial begin
        result = 0;
  end
  assign zero = (result? 0 : 1);
  always @(A or B or ALUOp) begin
    case(ALUOp)
      3'b000: result = A + B;
      3'b001: result = A - B;
      3'b010: begin
          if (A < B &&(( A[31] == 0 && B[31]==0)  || (A[31] == 1 && B[31]==1)))  result = 1;
          else if (A[31] == 0 && B[31]==1)  result = 0;
          else if (A[31] == 1 && B[31]==0)  result = 1;
          else result = 0; 
      end
      3'b011: result = (A < B ? 1 : 0);
      3'b100: result = B << A;
      3'b101: result = A | B;
      3'b110: result = A & B;
      3'b111: result = (~A & B) | (A & ~B);
      default: result = 0;
    endcase
  end
endmodule
// 切割数据通路
// @param i_data 输入的数据
// @param o_data 输出的数据
// @param clk 时钟信号
module ALUoutDR(i_data, clk, o_data);
  input clk;
  input [31:0] i_data;
  output reg[31:0] o_data;
  always @(posedge clk) begin
    o_data = i_data;
  end
endmodule
// 数据存储器的实现
// @param i_data 输入的数据
// @param addr 输入的地址
// @param DataMemRW 输入数据存储器的信号,用1代表/WR信号,用0代表/RD信号,我将这两个信号合为一个
// @param o_data 读取的数据
module DataMEM (i_data, addr, DataMemRW, o_data);
    input [31:0] i_data;
    input [31:0] addr;
    input DataMemRW;
    output reg [31:0] o_data;
    reg [7:0] memory [0:63];
     initial begin
        o_data = 0;
     end
      // 使用大端方式储存,这里有更改(不需要乘4)
    always @(addr or i_data or DataMemRW) begin
      if (DataMemRW) begin // 1 为 /WR
          memory[addr] = i_data[31:24];
          memory[addr+1] = i_data[23:16];
          memory[addr+2] = i_data[15:8];
          memory[addr+3] = i_data[7:0];
      end else begin // 0 为 /RD
          o_data[31:24] = memory[addr];
          o_data[23:16] = memory[addr+1];
          o_data[15:8] = memory[addr+2];
          o_data[7:0] = memory[addr+3];
      end
    end
endmodule 
// 切割数据通路
// @param i_data 输入的数据
// @param o_data 输出的数据
// @param clk 时钟信号
module DBDR(i_data, clk, o_data);
  input clk;
  input [31:0] i_data;
  output reg[31:0] o_data;
  always @(posedge clk) begin
    o_data = i_data;
  end
endmodule
// PC 加立即数
// @param now_pc  当前pc值
// @param o_pc 输出pc值
// @param imm 立即数
module PCAddImm(now_pc, imm, o_pc);
  input [31:0] now_pc, imm;
  output [31:0] o_pc;
  // 内存单元是以字节为单位的,32位地址大小为4个字节,所以pc=pc+imm*4
  assign o_pc = now_pc + (imm << 2);
endmodule
// 四选一数据选择器的实现
// @param A 输入1
// @param B 输入2
// @param C 输入3
// @param D 输入4
// @param Control 数据选择器的控制信号
// @param Result 选择的结果
module DataSelector_4to1(A, B, C, D, Control, Result);
  input [31:0] A, B, C, D;
  input [1:0]Control;
  output reg[31:0] Result;
  always @(Control or A or B or C or D) begin
    case(Control)
        2'b00: Result = A;
        2'b01: Result = B;
        2'b10: Result = C;
        2'b11: Result = D;
        default: Result = 0;
     endcase
  end
endmodule
// 控制单元CU的实现
// @param opcode 操作码
// @param zero 输入的zero信号
// @param clk 时钟信号
// @param reset 重置信号
// @param PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcB, DataMemRW, ALUM2Reg,ExtSel, RegOut, PCSrc,ALUOp 控制信号
module ControlUnit(opcode, clk, reset, zero, PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcA, ALUSrcB, DataMemRW, ALUM2Reg, ExtSel, RegOut, PCSrc, ALUOp);
    input [5:0]opcode;
    input zero, clk, reset;
    output PCWre, InsMemRW, IRWre, WrRegData, RegWre,ALUSrcA, ALUSrcB, DataMemRW, ALUM2Reg;
    output [1:0]ExtSel, RegOut, PCSrc;
    output [2:0]ALUOp;

    wire [2:0]i_state, o_state;

    DFlipFlop DFlipFlop(i_state, reset, clk, o_state);
    NextState NextState(o_state, opcode, i_state);
    OutputFunc OutputFunc(o_state, opcode, zero, PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcA, ALUSrcB, DataMemRW, ALUM2Reg, ExtSel, RegOut, PCSrc, ALUOp);

endmodule
// D触发器的实现
// @param i_state 输入的状态,也就是下一个状态
// @param reset 重置信号
// @param clk 时钟信号
// @param o_state 输出的状态
module DFlipFlop(i_state, reset, clk, o_state);
    input [2:0]i_state;
    input reset, clk;
    output reg[2:0]o_state;
    always @(posedge clk) begin
        if (reset) o_state = 3'b000;
        else o_state = i_state;
    end
endmodule
// NextState模块的实现
// @param i_state 输入的状态
// @param opcode 输入的操作码
// @param next_state 下一状态
module NextState(i_state, opcode, next_state);
    input [2:0]i_state;
    input [5:0]opcode;
    output reg[2:0]next_state;
    parameter [2:0] IF = 3'b000, // IF状态
                     ID = 3'b001, // ID状态
                     aEXE = 3'b110, // 第一条分支的EXE状态
                     bEXE = 3'b101, // 第二条分支的EXE状态
                     cEXE = 3'b010, // 第三条分支的EXE状态
                     MEM = 3'b011, // MEM状态
                     aWB = 3'b111, // 第一个分支的WB状态
                     cWB = 3'b100; // 第三个分支的WB状态
    always @(i_state or opcode) begin
        case (i_state)
            IF: next_state = ID;
            ID: begin
                case (opcode[5:3])
                    3'b110: begin
                        if (opcode == 6'b110100) next_state = bEXE; // beq指令
                        else next_state = cEXE; // sw, lw指令
                    end
                    3'b111: next_state = IF; // j, jal, jr, halt指令
                    default: next_state = aEXE; // add, sub等指令
                endcase
            end
            aEXE: next_state = aWB;
            bEXE: next_state = IF;
            cEXE: next_state = MEM;
            MEM: begin
                if (opcode == 6'b110001) next_state = cWB; // lw指令
                else next_state = IF; // sw指令
            end
            aWB: next_state = IF;
            cWB: next_state = IF;
            default: next_state = IF;
        endcase
    end
endmodule
// NextState模块的实现
// @param i_state 输入的状态
// @param opcode 输入的操作码
// @param next_state 下一状态
module NextState(i_state, opcode, next_state);
    input [2:0]i_state;
    input [5:0]opcode;
    output reg[2:0]next_state;
    parameter [2:0] IF = 3'b000, // IF状态
                     ID = 3'b001, // ID状态
                     aEXE = 3'b110, // 第一条分支的EXE状态
                     bEXE = 3'b101, // 第二条分支的EXE状态
                     cEXE = 3'b010, // 第三条分支的EXE状态
                     MEM = 3'b011, // MEM状态
                     aWB = 3'b111, // 第一个分支的WB状态
                     cWB = 3'b100; // 第三个分支的WB状态
    always @(i_state or opcode) begin
        case (i_state)
            IF: next_state = ID;
            ID: begin
                case (opcode[5:3])
                    3'b110: begin
                        if (opcode == 6'b110100) next_state = bEXE; // beq指令
                        else next_state = cEXE; // sw, lw指令
                    end
                    3'b111: next_state = IF; // j, jal, jr, halt指令
                    default: next_state = aEXE; // add, sub等指令
                endcase
            end
            aEXE: next_state = aWB;
            bEXE: next_state = IF;
            cEXE: next_state = MEM;
            MEM: begin
                if (opcode == 6'b110001) next_state = cWB; // lw指令
                else next_state = IF; // sw指令
            end
            aWB: next_state = IF;
            cWB: next_state = IF;
            default: next_state = IF;
        endcase
    end
endmodule
// 输出函数模块的实现
// @param state 当前状态
// @param opcode 操作码
// @param PCWre PC的控制信号
// @param InsMemRW 指令存储器的控制信号
// @param IRWre IR的控制信号
// @param WrRegData 控制寄存器组写数据端口的数据选择器
// @param RegWre 寄存器组的控制信号
// @param ALUSrcA 控制ALU的A输入端口的数据选择器
// @param ALUSrcB 控制ALU的B输入端口的数据选择器
// @param DataMemRW 数据存储器的控制信号
// @param DBDataSrc 控制数据存储器输出端口的数据选择器
// @param ExtSel 符号扩展单元的控制信号
// @param RegDst 控制寄存器组写寄存器端口的数据选择器
// @param PCSrc 四选一选择器的控制信号
// @param ALUOp ALU的控制信号
module OutputFunc(state, opcode, zero, PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcA, ALUSrcB, DataMemRW, DBDataSrc, ExtSel, RegDst, PCSrc, ALUOp);
    input [2:0]state;
    input [5:0]opcode;
    input zero;
    output reg PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcA, ALUSrcB, DataMemRW, DBDataSrc;
    output reg[1:0]ExtSel, RegDst, PCSrc;
    output reg[2:0]ALUOp;
    parameter [2:0] IF = 3'b000, // IF状态
                     ID = 3'b001, // ID状态
                     aEXE = 3'b110, // 第一支路的EXE状态
                     bEXE = 3'b101, // 第二支路的EXE状态
                     cEXE = 3'b010, // 第三支路的EXE状态
                     MEM = 3'b011, // MEM状态
                     aWB = 3'b111, // 第一支路的WB状态
                     cWB = 3'b100; // 第三支路的WB状态
    parameter [5:0] addi = 6'b000010,
                     ori = 6'b010010,
                     sll = 6'b011000,
                     add = 6'b000000,
                     sub = 6'b000001,
                     slt = 6'b100110,
                     slti = 6'b100111,
                     sw = 6'b110000,
                     lw = 6'b110001,
                     beq = 6'b110100,
                     j = 6'b111000,
                     jr = 6'b111001,
                     Or = 6'b010000,
                     And = 6'b010001,
                     jal = 6'b111010,
                     halt = 6'b111111;

    always @(state) begin
        // 对PCWre定值
        if (state == IF && opcode != halt) PCWre = 1;
        else PCWre = 0;
        // 对InsMemRW定值
        InsMemRW = 1;
        // 对IRWre定值
        if (state == IF) IRWre = 1;
        else IRWre = 0;
        // 对WrRegData定值
        if (state == aWB || state == cWB) WrRegData = 1;
        else WrRegData = 0;
        // 对RegWre定值
        if (state == aWB || state == cWB || opcode == jal) RegWre = 1;
        else RegWre = 0;
        // 对ALUSrcA定值
        if (opcode == sll) ALUSrcA = 1;
        else ALUSrcA = 0;
        // 对ALUSrcB定值
        if (opcode == addi || opcode == ori || opcode == slti|| opcode == sw || opcode == lw) ALUSrcB = 1;
        else ALUSrcB = 0;
        // 对DataMemRW定值
        if (state == MEM && opcode == sw) DataMemRW = 1;
        else DataMemRW = 0;
        // 对 DBDataSrc定值
        if (state == cWB) DBDataSrc = 1;
        else DBDataSrc = 0;
        // 对ExtSel定值
        if (opcode == ori) ExtSel = 2'b01;
        else if (opcode == sll) ExtSel = 2'b00;
        else ExtSel = 2'b10;
        // 对RegDst定值
        if (opcode == jal) RegDst = 2'b00;
        else if (opcode == addi || opcode == ori || opcode == lw) RegDst = 2'b01;
        else RegDst = 2'b10;
        // 对PCSrc定值
        case(opcode)
            j: PCSrc = 2'b11;
            jal: PCSrc = 2'b11;
            jr: PCSrc = 2'b10;
            beq: begin
                if (zero) PCSrc = 2'b01;
                else PCSrc = 2'b00;
            end
            default: PCSrc = 2'b00;
        endcase
        // 对ALUOp定值
        case(opcode)
            sub: ALUOp = 3'b001;
            Or: ALUOp = 3'b101;
            And: ALUOp = 3'b110;
            ori: ALUOp = 3'b101;
            slt: ALUOp = 3'b010;
            slti: ALUOp = 3'b010;
            sll: ALUOp = 3'b100;
            beq: ALUOp = 3'b001;
            default: ALUOp = 3'b000;
        endcase
        // 防止在IF阶段写数据
        if (state == IF) begin
            RegWre = 0;
            DataMemRW = 0;
        end
    end

endmodule
module cpu_sim;

    // Inputs
    reg CLK;
    reg RST;
    reg [31:0] outside_pc;

    // Outputs
    wire [31:0] ins, now_pc;

    // Instantiate the Unit Under Test (UUT)
    Main uut (
        .CLK(CLK), 
        .RST(RST), 
        .outside_pc(outside_pc), 
        .ins(ins),
        .now_pc(now_pc)
    );

    initial begin
        // Initialize Inputs
      CLK = 0;
      RST = 1;
      outside_pc = 0; // 这里设置外部pc
      #50; // 刚开始设置pc为0
          CLK = !CLK;
      #50;
          RST = 0;
      forever #50 begin // 产生时钟信号
          CLK = !CLK;
      end
    end

以上内容皆为本人观点,欢迎大家提出批评和指导,我们一起探讨!


你可能感兴趣的:(Verilog)