多週期CPU設計(Verilog) (更新:2017/5/29)

注:單週期CPU設計請移步我的另一篇博文:
單週期CPU設計(Verilog)


一、 實驗目的

(1) 認識和掌握多週期數據通路原理及其設計方法;
(2) 掌握多週期CPU的實現方法,代碼實現方法;
(3) 編寫一個編譯器,將MIPS彙編程序編譯爲二進制機器碼;
(4) 掌握多週期CPU的測試方法。

二、 實驗內容

設計一個多週期CPU,該CPU至少能實現以下指令功能操作。需設計的指令與格式如下:
(說明:操作碼按照以下規定使用,都給每類指令預留擴展空間,後續實驗相同。)
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

三、 實驗原理

多週期CPU指的是將整個CPU的執行過程分成幾個階段,每個階段用一個時鐘去完成,然後開始下一條指令的執行,而每種指令執行時所用的時鐘數不盡相同,這就是所謂的多週期CPU。CPU在處理指令時,一般需要經過以下幾個階段:

(1) 取指令(IF):根據程序計數器pc中的指令地址,從存儲器中取出一條指令,同時,pc根據指令字長度自動遞增產生下一條指令所需要的指令地址,但遇到“地址轉移”指令時,則控制器把“轉移地址”送入pc,當然得到的“地址”需要做些變換才送入pc。
(2) 指令譯碼(ID):對取指令操作中得到的指令進行分析並譯碼,確定這條指令需要完成的操作,從而產生相應的操作控制信號,用於驅動執行狀態中的各種操作。
(3) 指令執行(EXE):根據指令譯碼得到的操作控制信號,具體地執行指令動作,然後轉移到結果寫回狀態。
(4) 存儲器訪問(MEM):所有需要訪問存儲器的操作都將在這個步驟中執行,該步驟給出存儲器的數據地址,把數據寫入到存儲器中數據地址所指定的存儲單元或者從存儲器中得到數據地址單元中的數據。
(5) 結果寫回(WB):指令執行的結果或者訪問存儲器中得到的數據寫回相應的目的寄存器中。

實驗中就按照這五個階段進行設計,這樣一條指令的執行最長需要五個(小)時鐘週期才能完成,但具體情況怎樣?要根據該條指令的情況而定,有些指令不需要五個時鐘週期的,這就是多週期的CPU。

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

特別提示,圖上增加IR指令寄存器,目的是使指令代碼保持穩定,pc寫使能控制信號PCWre,是確保pc適時修改,原因都是和多週期工作的CPU有關。ADR、BDR、ALUoutDR、DBDR四個寄存器不需要寫使能信號,其作用是切分數據通路,將大組合邏輯切分爲若干個小組合邏輯,大延遲變爲多個分段小延遲。

這裏寫圖片描述

這裏寫圖片描述

相關部件及引腳說明:

  • 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

這裏寫圖片描述
這裏寫圖片描述

四、實驗設備

PC機一臺,BASYS 3 實驗板一塊,Xilinx Vivado 開發軟件一套。

五、實驗分析與設計

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

六、實驗心得

這裏寫圖片描述

七、代碼

代碼放的次序有點亂,實現模塊的順序可以參見我的實驗過程設計,也就是第五部分!

// 頂層模塊的實現
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

以上內容皆爲本人觀點,歡迎大家提出批評和指導,我們一起探討!


發佈了146 篇原創文章 · 獲贊 268 · 訪問量 53萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章