製作單週期CPU(代碼)

直接上代碼

SCPU

module SCPU( CLK, Reset, CurPC, instcode );

    input CLK; // 時鐘信號
    input Reset; // 置零信號
    output [31:0] CurPC; // 當前指令的地址
    output [31:0] instcode; //  指令寄存器中獲取的指令碼
    wire ExtSel; // 位擴展信號,1爲符號擴展,0爲0擴展
    wire PCWre; // PC工作信號,0不更改,1更改
    wire InsMemRW; // 指令寄存器信號,0爲寫,1爲讀
    wire RegDst; // 指令讀取時判斷是rt還是rd進入寄存器組的寫數據端,0爲rt,1爲rd
    wire RegWre; // 寄存器組是否需要寫功能,0爲無寫功能,1爲些功能
    wire [2:0] ALUOp; // ALU8種運算功能選擇
    wire PCSrc; // PC正常+4還是要跳轉,0爲正常+4,1爲跳轉
    wire ALUSrcA; // 寄存器組Data1的輸出,0爲寄存器本身輸出,1爲指令碼的最後16位立即數
    wire ALUSrcB; // 寄存器組Data2的輸出,0位本身的輸出,1爲擴展後的立即數
    wire RD; // 讀數據存儲器功能,0時讀取
    wire WR; // 寫數據存儲器功能,1時寫
    wire DBDataSrc; // 決定將什麼數據傳入寄存器組Write Data端,0爲ALU結果,1爲存儲器
    wire [4:0] WriteRegAddr; // 寄存器組Write Reg輸入端
    wire [31:0] Reg1Out; // 寄存器組rs寄存器的值
    wire [31:0] Reg2Out; // 寄存器組rt寄存器的值,也是存儲器的DataIn輸入端
    wire [31:0] WriteData; // 寄存器組Write Data輸入端的值
    wire [31:0] ALU_Input_A; // ALU的A輸入端
    wire [31:0] ALU_Input_B; // ALU的B輸入端
    wire [31:0] ALU_Out; // ALU的result輸出,也是存儲器的地址輸入端
    wire zero; // ALU的zero輸出
    wire [31:0] MemOut; // 存儲器的輸出
    wire [31:0] Ext_Imm; // 位擴展後的立即數

    // PC( CLK, Reset, PCWre, PCSrc, immediate, Address)
    PC my_PC( CLK, Reset, PCWre, PCSrc, Ext_Imm, CurPC );

    // ALU( Reg1, Reg2, ALUOp, result, zero )
    ALU my_ALU( ALU_Input_A, ALU_Input_B, ALUOp, ALU_Out, zero );

    // DataMemory( DAddr, CLK, RD, WR, DataIn, DataOut)
    DataMemory my_DataMemory( ALU_Out, CLK, RD, WR, Reg2Out, MemOut );

    // Sign_Zero_Extend( Imm_Number, ExtSel, Result )
    Sign_Zero_Extend my_Sign_Zero_Extend( instcode[15:0], ExtSel, Ext_Imm );

    // Mux_TwoToOneReg( Select, DataIn1, DataIn2, DataOut )
    Mux_TwoToOneReg my_Mux_TwoToOneReg( RegDst, instcode[20:16], instcode[15:11], WriteRegAddr );

    // Mux_TwoToOne( Select, DataIn1, DataIn2, DataOut )
    Mux_TwoToOne_ForInputA my_Mux_TwoToOne_For_ALU_InputA( ALUSrcA, Reg1Out, instcode[10:6], ALU_Input_A );

    Mux_TwoToOne my_Mux_TwoToOne_For_ALU_InputB( ALUSrcB, Reg2Out, Ext_Imm, ALU_Input_B );

    Mux_TwoToOne my_Mux_TwoToOne_For_RegisterFile_WriteData( DBDataSrc, ALU_Out, MemOut, WriteData );

    // RegisterFile( RegWre, CLK, Reg1, Reg2, WriteReg, WriteData, DataOut1, DataOut2 )
    RegisterFile my_RegisterFile( RegWre, CLK, instcode[25:21], instcode[20:16], WriteRegAddr, WriteData, Reg1Out, Reg2Out );

    // ControlUnit( ExtSel, PCWre, InsMemRW, RegDst, RegWre, ALUOp, PCSrc, ALUSrcB, RD, WR, DBDataSrc, opCode, zero )
    ControlUnit my_ControlUnit( ExtSel, PCWre, InsMemRW, RegDst, RegWre, ALUOp, PCSrc, ALUSrcA, ALUSrcB, RD, WR, DBDataSrc, instcode[31:26], zero );

    // InstructionMemory( CurPC, instMemRW, instcode )
    InstructionMemory my_InstructionMemory( CurPC, InsMemRW, instcode );
endmodule

PC

module PC( CLK, Reset, PCWre, PCSrc, immediate, Address);  
    input CLK, Reset, PCWre, PCSrc;  
    input [31:0] immediate;  
    output reg [31:0] Address;    

     /*initial begin  
         Address = 0;  
     end*/  

     always @(posedge CLK or negedge Reset)  
         begin  
              if (Reset == 0) begin  
                    Address = 0;  
                end  
                else if (PCWre) begin 
                    if (PCSrc)
                        Address = Address + 4 + immediate * 4; // 需要跳轉,是按當前指令地址的下一個地址爲參考來跳轉的
                    else Address = Address + 4;                // 不需要跳轉,一個指令是32位,一個指令存儲器存儲長度是8位,所以需要4個存儲單元,所以+4
                end  
          end  

endmodule  

ALU

module ALU( Reg1, Reg2, ALUOp, result, zero );

    input [31:0] Reg1;
    input [31:0] Reg2;
    input [2:0] ALUOp;
    output reg [31:0] result;
    output zero;

   assign zero = ( result == 0 ) ? 1 : 0;
   always@( ALUOp or Reg1 or Reg2 ) begin
      case (ALUOp)
         3'b000 : result = Reg1 + Reg2; // Y = A + B
         3'b001 : result = Reg1 - Reg2; // Y = A - B
         3'b010 : result = (Reg1 < Reg2) ? 1 : 0; // Y=(A < B)? 1 : 0
         3'b011 : result = Reg2 << Reg1; // Y = B << A
         3'b100 : result = Reg1 & Reg2; // Y = A & B
         3'b101 : result = Reg1 | Reg2; // Y = A | B
         3'b110 : result = Reg1 ^ Reg2; // Y = A ^ B
         3'b111 : result = Reg1 ^~ Reg2; // Y = A ^~ B
     endcase
   end
endmodule

DataMemory

module DataMemory( DAddr, CLK, RD, WR, DataIn, DataOut);

    input [31:0] DAddr;
    input CLK;
    input RD;
    input WR;
    input [31:0] DataIn;
    output reg [31:0] DataOut;
    reg [7:0] dataMemory [0:60];

    always@( RD or DAddr ) begin // 讀,隨時的
        if (RD == 0) begin
            DataOut[7:0] = dataMemory[DAddr + 3];
            DataOut[15:8] = dataMemory[DAddr + 2];
            DataOut[23:16] = dataMemory[DAddr + 1];
            DataOut[31:24] = dataMemory[DAddr];
        end
    end

    always@( negedge CLK ) begin // 寫,時鐘下降沿觸發
        if (WR == 0) begin
            dataMemory[DAddr + 3] <= DataIn[7:0];
            dataMemory[DAddr + 2] <= DataIn[15:8];
            dataMemory[DAddr + 1] <= DataIn[23:16];
            dataMemory[DAddr] <= DataIn[31:24];
        end
    end

endmodule

Sign_Zero_Extend

module Sign_Zero_Extend( Imm_Number, ExtSel, Result );

    input [15 :0] Imm_Number;
    input ExtSel;
    output reg [31:0] Result;

    always@( Imm_Number or ExtSel) begin
        if (ExtSel == 0 || Imm_Number[15] == 0)
            Result = { 16'b0000000000000000, Imm_Number };
        else
            Result = { 16'b1111111111111111, Imm_Number };
    end

endmodule

Mux_TwoToOneReg

module Mux_TwoToOneReg( Select, DataIn1, DataIn2, DataOut );

    input Select;
    input [4:0] DataIn1;
    input [4:0] DataIn2;
    output reg [4:0] DataOut;

    always@( Select or DataIn1 or DataIn2 ) begin
        if ( Select == 0 )
            DataOut = DataIn1;
        else
            DataOut = DataIn2;
    end

endmodule

Mux_TwoToOne_ForInputA

module Mux_TwoToOne_ForInputA( Select, DataIn1, sa, DataOut );

    input Select;
    input [31:0] DataIn1;
    input [4:0] sa;
    output reg [31:0] DataOut;

    always@( Select or DataIn1 or sa ) begin
        if ( Select == 0 )
            DataOut = DataIn1;
        else
            DataOut = { 27'b000000000000000000000000000, sa }; // 對sa進行擴展
    end

endmodule

Mux_TwoToOne

module Mux_TwoToOne( Select, DataIn1, DataIn2, DataOut );

    input Select;
    input [31:0] DataIn1;
    input [31:0] DataIn2;
    output reg [31:0] DataOut;

    always@( Select or DataIn1 or DataIn2 ) begin
        if ( Select == 0 )
            DataOut = DataIn1;
        else
            DataOut = DataIn2;
    end

endmodule

RegisterFile

module RegisterFile( RegWre, CLK, Reg1, Reg2, WriteReg, WriteData, DataOut1, DataOut2 );

    input RegWre;
    input CLK;
    input [4:0] Reg1, Reg2;
    input [4:0] WriteReg;
    input [31:0] WriteData;
    output [31:0] DataOut1, DataOut2;
    reg [31:0] registers[1:31];

    assign DataOut1 = ( Reg1 == 0 ) ? 0 : registers[Reg1];
    assign DataOut2 = ( Reg2 == 0 ) ? 0 : registers[Reg2];

    always@( negedge CLK ) begin // 寫操作
        if (( WriteReg != 0 ) && ( RegWre == 1 )) begin
            $display("WriteData: ", WriteData, " WriteReg: ", WriteReg);
            registers[WriteReg] <= WriteData;
        end
    end

endmodule

ControlUnit

module ControlUnit( ExtSel, PCWre, InsMemRW, RegDst, RegWre, ALUOp, PCSrc, ALUSrcA, ALUSrcB, RD, WR, DBDataSrc, opCode, zero );
    output ExtSel;
    output PCWre;
    output InsMemRW;
    output RegDst;
    output RegWre;
    output [2:0] ALUOp;
    output PCSrc;
    output ALUSrcA;
    output ALUSrcB;
    output RD;
    output WR;
    output DBDataSrc;
    input [5:0] opCode;
    input zero;

    assign ExtSel = (opCode == 6'b010000) ? 0 : 1;
    assign PCWre = (opCode == 6'b111111) ? 0 : 1;
    assign InsMemRW = 0;
    assign RegDst = (opCode == 6'b000001 || opCode == 6'b010000 || opCode == 6'b100110 || opCode == 6'b100111 || opCode == 6'b110000) ? 0 : 1;
    assign RegWre = (opCode == 6'b100110 || opCode == 6'b110000) ? 0 : 1;
    assign PCSrc = (opCode == 6'b110000 && zero == 1) ? 1 : 0;
    assign RegWre = (opCode == 6'b100110 || opCode == 6'b110000) ? 0 : 1;
    assign ALUSrcA = (opCode == 6'b011000) ? 1 : 0;
    assign ALUSrcB = (opCode == 6'b000000 || opCode == 6'b000010 || opCode == 6'b010001 || opCode == 6'b010010 || opCode == 6'b110000 || opCode == 6'b011000) ? 0 : 1;
    assign RD = (opCode == 6'b100111) ? 0 : 1;
    assign WR = (opCode == 6'b100110) ? 0 : 1;
    assign DBDataSrc = (opCode == 6'b100111) ? 1 : 0;
    assign ALUOp[0] = (opCode == 6'b000000 || opCode == 6'b000001 || opCode == 6'b010001 || opCode == 6'b100110 || opCode == 6'b100111) ? 0: 1;
    assign ALUOp[1] = (opCode == 6'b000000 || opCode == 6'b000001 || opCode == 6'b000010 || opCode == 6'b010000 || opCode == 6'b010001 || opCode == 6'b010010 || opCode == 6'b100110 || opCode == 6'b100111 || opCode == 6'b110000) ? 0 : 1;
    assign ALUOp[2] = (opCode == 6'b010000 || opCode == 6'b010001 || opCode == 6'b010010) ? 1 : 0;

endmodule

InstructionMemory

這裏readmemb的地址需要修改,具體看你的指令文件存儲位置

module InstructionMemory( CurPC, instMemRW, instcode );

    input [31:0] CurPC;
    input instMemRW;
    output reg [31:0] instcode;
    reg [7:0] InstMemory [255:0];

    initial begin
        $readmemb("H:/Single_CPU/instruction.txt", InstMemory);
    end

    always@(CurPC or instMemRW) begin
        if (instMemRW == 0) begin
            instcode = { InstMemory[CurPC], InstMemory[CurPC + 1], InstMemory[CurPC + 2], InstMemory[CurPC + 3] };
        end
        $display("InstMem PC", CurPC, " INST: ", instcode);
    end

endmodule

SCPU_sim

仿真模塊

module SCPU_sim;

    reg CLK; // 時鐘信號
    reg Reset; // 置零信號
    wire [31:0] CurPC; // 當前指令的地址
    wire [31:0] instcode; //  指令寄存器中獲取的指令碼

    SCPU my_SCPU( .CLK(CLK), 
                  .Reset(Reset),
                  .CurPC(CurPC),
                  .instcode(instcode)
                  );

                  always #30 CLK = !CLK; // 60ns爲一週期
                  initial begin
                    CLK = 1;
                    Reset = 0;
                    #90;
                    Reset = 1;
                  end
endmodule
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章