多週期CPU實驗

一、實驗內容和要求

  設計一個多週期CPU,該CPU至少能實現以下指令功能操作。需設計的指令與格式如下:


==>算術運算指令
(1)add rd, rs, rt

000000 rs(5位) rt(5位) rd(5位) reserved

  功能:rd<-rs + rt
(2)sub rd, rs, rt

000001 rs(5位) rt(5位) rd(5位) reserved

  功能:rd<-rs - rt
(3)addi rt, rs, immediate

000010 rs(5位) rt(5位) immediate(16位)

  功能:rt<-rs + (sign-extend)immediate

==>算術運算指令
(4)or rd, rs, rt

010000 rs(5位) rt(5位) rd(5位) reserved

  功能:rd<-rs | rt
(5)and rd, rs, rt

010001 rs(5位) rt(5位) rd(5位) reserved

  功能:rd<-rs & rt
(6)ori rt, rs, immediate

010010 rs(5位) rt(5位) immediate(16位)

  功能:rt<-rs | (zero-extend)immediate

==>移位指令
(7)sll rd, rs,sa

011000 rs(5位) 未用 rd(5位) sa reserved

  功能:rd<-rs<<(zero-extend)sa,左移sa位 ,(zero-extend)sa

==>傳送指令
(8)move rd, rs

100000 rs(5位) 00000 rd(5位) reserved

  功能:rd<-rs + $0

==>比較指令
(9) slt rd, rs, rt

100111 rs(5位) rt(5位) rd(5位) reserved

  功能:如果(rs < rt),則rd=1; 否則 rd=0

==>存儲器讀寫指令
(10)sw rt, immediate(rs)

110000 rs(5位) rt(5位) immediate(16位)

  功能:memory[rs+ (sign-extend)immediate]<-rt
(11)lw rt, immediate(rs)

110001 rs(5位) rt(5位) immediate(16位)

  功能:rt <- memory[rs + (sign-extend)immediate]

==>分支指令
(12)beq rs,rt, immediate (說明:immediate是從pc+4開始和轉移到的指令之間間隔條數)

110100 rs(5位) rt(5位) immediate(16位)

  功能:if(rs=rt) pc <-pc + 4 + (sign-extend)immediate <<2

==>跳轉指令
(13)j addr

111000 addr[27..2]

  功能:pc <{pc[31..28],addr[27..2],0,0},轉移
(14)jr rs

111001 rs(5位) 未用 未用 reserved

  功能:pc <- rs,轉移

==>調用子程序指令
(15)jal addr

111010 addr[27..2]

功能:調用子程序,pc <- {pc[31..28],addr[27..2],0,0};31<pc+4jr 31。

==>停機指令
(16)halt (停機指令)

111111 00000000000000000000000000(26位)

  功能:pc <{pc[31..28],addr[27..2],0,0},轉移


二、實驗原理

  多週期CPU指的是將整個CPU的執行過程分成幾個階段,每個階段用一個時鐘去完成,然後開始下一條指令的執行,而每種指令執行時所用的時鐘數不盡相同,這就是所謂的多週期CPU。CPU在處理指令時,一般需要經過以下幾個階段:
(1) 取指令(IF):根據程序計數器pc中的指令地址,從存儲器中取出一條指令,同時,pc根據指令字長度自動遞增產生下一條指令所需要的指令地址,但遇到“地址轉移”指令時,則控制器把“轉移地址”送入pc,當然得到的“地址”需要做些變換才送入pc。
(2) 指令譯碼(ID):對取指令操作中得到的指令進行分析並譯碼,確定這條指令需要完成的操作,從而產生相應的操作控制信號,用於驅動執行狀態中的各種操作。
(3) 指令執行(EXE):根據指令譯碼得到的操作控制信號,具體地執行指令動作,然後轉移到結果寫回狀態。
(4) 存儲器訪問(MEM):所有需要訪問存儲器的操作都將在這個步驟中執行,該步驟給出存儲器的數據地址,把數據寫入到存儲器中數據地址所指定的存儲單元或者從存儲器中得到數據地址單元中的數據。
(5) 結果寫回(WB):指令執行的結果或者訪問存儲器中得到的數據寫回相應的目的寄存器中。
  實驗中就按照這五個階段進行設計,這樣一條指令的執行最長需要五個(小)時鐘週期才能完成,但具體情況怎樣?要根據該條指令的情況而定,有些指令不需要五個時鐘週期的,這就是多週期的CPU。
  
這裏寫圖片描述

  MIPS32的指令的三種格式:
R類型:

這裏寫圖片描述

I類型:

這裏寫圖片描述

J類型:

這裏寫圖片描述

其中,
op:爲操作碼;
rs:爲第1個源操作數寄存器,寄存器地址(編號)是00000~11111,00~1F;
rt:爲第2個源操作數寄存器,或目的操作數寄存器,寄存器地址(同上);
rd:爲目的操作數寄存器,寄存器地址(同上);
sa:爲位移量(shift amt),移位指令用於指定移多少位;
func:爲功能碼,在寄存器類型指令中(R類型)用來指定指令的功能;
immediate:爲16位立即數,用作無符號的邏輯操作數、有符號的算術操作數、數據加載(Laod)/數據保存(Store)指令的數據地址字節偏移量和分支指令中相對程序計數器(PC)的有符號偏移量;
address:地址。

這裏寫圖片描述

  狀態的轉移有的是無條件的,例如從IF狀態轉移到ID 和 EXE狀態就是無條件的;有些是有條件的,例如ID 或 EXE狀態之後不止一個狀態,到底轉向哪個狀態由該指令功能,即指令操作決定。每個狀態代一個時鐘週期。

這裏寫圖片描述

  圖3是多週期CPU控制部件的電路結構,三個D觸發器用於保存當前狀態,是時序邏輯電路,RST用於初始化狀態“000“,另外兩個部分都是組合邏輯電路,一個用於產生下一個階段的狀態,另一個用於產生每個階段的控制信號。從圖上可看出,下個狀態取決於指令操作碼和當前狀態;而每個階段的控制信號取決於指令操作碼、當前狀態和反映運算結果的狀態zero標誌等。

這裏寫圖片描述

  圖4是一個簡單的基本上能夠在單週期上完成所要求設計的指令功能的數據通路和必要的控制線路圖。其中指令和數據各存儲在不同存儲器中,即有指令存儲器和數據存儲器。訪問存儲器時,先給出地址,然後由讀/寫信號控制(1-寫,0-讀。當然,也可以由時鐘信號控制,但必須在圖上畫出來)。對於寄存器組,讀操作時,給出寄存器地址(編號),輸出端就直接輸出相應數據;而在寫操作時,在 WE使能信號爲1時,在時鐘邊沿觸發寫入。圖中控制信號功能如表1所示,表2是ALU運算功能表。


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


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

相關部件及引腳說明:
Instruction Memory:指令存儲器,
  Iaddr,指令地址輸入端口
  DataIn,存儲器數據輸入端口
  DataOut,存儲器數據輸出端口
  RW,指令存儲器讀寫控制信號,爲1寫,爲0讀
Data Memory:數據存儲器,
  Daddr,數據地址輸入端口
  DataIn,存儲器數據輸入端口
  DataOut,存儲器數據輸出端口
  RW,數據存儲器讀寫控制信號,爲1寫,爲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

這裏寫圖片描述

三、實現代碼

1、各個部分代碼實現

  首先,根據各條指令和信號的關係,實現ControlUnit。這次的ControlUnit的設計和上次略有不同。由於一條指令分多個週期實現,各個信號因狀態的不同而不同。所以,這次的實現要設計一個狀態機:狀態轉換和信號量變換。根據不同信號的作用,可以列出下列真值表:

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
  
  依據此表格,設計ControlUnit模塊如下。

`timescale 1ns / 1ps
`include "defines.v"

module ControlUnit(
    input wire[5:0] opCode,
    input wire zero,
    input wire clk,
    input wire reset,
    output reg PCWre,
    output reg InsMemRW,
    output reg IRWre,
    output reg WrRegData,
    output reg RegWre,
    output reg ALUSrcB,
    output reg ALUM2Reg,
    output reg DataMemRW,
    output reg[1:0] ExtSel,
    output reg[1:0] RegOut,
    output reg[1:0] PCSrc,
    output reg[2:0] ALUOp,
    output reg[2:0] State
    );

    reg[2:0] Nowstate, Nextstate;

    initial begin
        PCWre = 0;
        InsMemRW = 0;
        IRWre = 0;
        WrRegData = 0;
        RegWre = 0;
        ALUSrcB = 0;
        ALUM2Reg = 0;
        DataMemRW = 0;
        ExtSel = 2'b11;
        RegOut = 2'b11;
        PCSrc = 2'b00;
        ALUOp = 3'b000;
        Nowstate = `IF;
        State = Nowstate;
    end

    always @(posedge clk) begin
        if(reset == 0)begin
                Nowstate = `IF;
        end
        else begin
                Nowstate = Nextstate;
        end

        State = Nowstate;
    end

    always@(Nowstate or opCode) begin
        case(Nowstate)

            `IF: Nextstate <= `ID;

            `ID: begin
                case(opCode)
                    `J: Nextstate = `IF;
                    `Jal: Nextstate = `IF;
                    `Jr: Nextstate = `IF;
                    `Halt: Nextstate = `IF;
                    `Beq: Nextstate = `EXE2;
                    `Sw: Nextstate = `EXE3;
                    `Lw: Nextstate = `EXE3;
                    default: Nextstate = `EXE1;
                endcase
            end

            `EXE1: Nextstate = `WB1;

            `EXE2: Nextstate = `IF;

            `EXE3: Nextstate = `MEM;

            `MEM: begin
                if(opCode == `Lw) Nextstate = `WB2;
                else Nextstate = `IF;
            end

            `WB1: Nextstate = `IF;

            `WB2: Nextstate = `IF;

            default: Nextstate = `IF;

        endcase
    end

    always@(Nowstate) begin
        PCWre = (Nowstate == `IF && opCode != `Halt)? 1 : 0; //PCWre = 1 PC值+4
        InsMemRW = 1; //寫指令存儲器
        IRWre = (Nowstate == `IF)? 1 : 0; //IRWre = 1 寫指令寄存器

        RegWre = (Nowstate == `WB1 || Nowstate == `WB2 || (Nowstate == `ID && opCode == `Jal))? 1 : 0; // = 1 寫寄存器(數據)
        WrRegData = (Nowstate == `WB1 || Nowstate == `WB2)? 1 : 0;  //=0 記錄(PC+4)的值;否則記錄計算所得的Result
        ALUSrcB = (opCode == `Addi || opCode == `Ori || opCode == `Sll ||
                    opCode == `Sw || opCode == `Lw)? 1 : 0; //第二個源操作數的來源:寄存器 0r 擴展的立即數
        DataMemRW = (Nowstate == `MEM && opCode == `Sw)? 1 : 0;
        ALUM2Reg = (Nowstate == `WB2)? 1 : 0; //=1 輸出來寄存器Reg,即ALU的計算結果; =0 輸出來自dataMemory

        case(opCode)
            `Ori: ExtSel = 2'b01;
            `Sll: ExtSel = 2'b00;
            default: ExtSel = 2'b10;
        endcase

        case(opCode)
            `Jal: RegOut = 2'b00;
            `Addi: RegOut = 2'b01;
            `Ori: RegOut = 2'b01;
            `Lw: RegOut = 2'b01;
            default: RegOut = 2'b10;
        endcase

        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[2] = (opCode == `Sll || opCode == `Or ||
                    opCode == `And || opCode == `Ori)? 1 : 0;
        ALUOp[1] = (opCode == `Srl || opCode == `Slt ||
                    opCode == `And)? 1 : 0;//有些指令沒有 所以ALU單元並不完整
        ALUOp[0] = (opCode == `Sub || opCode == `Srl || opCode == `Beq ||
                    opCode == `Ori || opCode == `Or)? 1 : 0; //有些指令沒有 所以ALU單元並不完整

        if(Nowstate == `IF) begin
            RegWre = 0;
            DataMemRW = 0;
        end
    end

endmodule

PC模塊:

`timescale 1ns  /   1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name:    PC 
// Function:    store the next instruction address.
//////////////////////////////////////////////////////////////////////////////////
module PC(
    input wire clk, //時鐘信號
    input wire Rst, //重置信號
    input wire PCWre, //PC是否更改
    input wire[1:0] PCSrc, //PC指令更新方式
    input wire[31:0] immediate,  //32位立即數
    input wire[31:0] addr,  //直接跳轉至某地址 相關指令:j,jal
    input wire[31:0] Regaddr,   //存放在寄存器中的地址 相關指令:jr
    output reg[31:0] address    //下一條指令地址
    );

    always @(PCWre or negedge Rst)begin
            if(Rst == 0) begin
                address = 0;    //初始化
            end
            else if(PCWre) begin
                if(PCSrc == 2'b00)//正常跳轉
                    address = address + 4;
                else if(PCSrc == 2'b01)//beq跳轉
                    address = address + 4 + immediate*4;
                else if(PCSrc == 2'b10)//jr指令
                    address = Regaddr;
                else if(PCSrc == 2'b11)//j jal指令
                    address = addr;
            end
            else begin
                address <= address; //Halt指令執行以後,PC不變
            end
        end

endmodule

PCAddr模塊(擴展地址,相關指令j, jr):

`timescale 1ns / 1ps
//////////////////////////////////////////////////////
//j 和 jal 指令會使用
module PCAddr(
    input wire[25:0] in_addr,
    input wire[31:0] PC0,
    output reg[31:0] PC1
    );
    wire[27:0] mid;
    assign mid = in_addr << 2;
    always @(in_addr) begin
        PC1 <= {PC0[31:28], mid[27:0]};//高4位保留,其餘位替換
    end
endmodule

instructionMemory模塊(指令存儲器):

`timescale 1ns / 1ps
////////////////////////////////////////////////////////////////////////////
module instructionMemory(
    input wire[31:0] addr,
    input wire InsMemRW,
    input wire IRWre,
    input wire clk,
    output reg[31:0] ins
    );

    reg[31:0] ins_out;
    reg[7:0] mem[0:127];

    initial begin
     $readmemb("./instruction/my_store.txt",mem);//系統函數 遇到空格和換行符,自動分割
    end

    always @(addr or InsMemRW) begin
        if(InsMemRW) begin//每條PC相差4,一個word位寬,所以採用此方法存儲
            ins_out[31:24] = mem[addr];
            ins_out[23:16] = mem[addr+1];
            ins_out[15:8] = mem[addr+2];
            ins_out[7:0] = mem[addr+3];
        end
    end

    always @(negedge clk) begin
        if(IRWre) ins <= ins_out;
    end

endmodule

IR模塊(指令解析器,在ID階段執行):

`timescale 1ns / 1ps

module IR(
    input wire clk,
    input wire IRWre,
    input wire[31:0] ins,
    output reg[5:0] opcode,
    output reg[4:0] rs,
    output reg[4:0] rt,
    output reg[4:0] rd,
    output reg[4:0] sa,
    output reg[25:0] addr,
    output reg[15:0] immediate
    );

    always @(posedge clk) begin
        if(IRWre == 1) begin
            opcode = ins[31:26];
            rs = ins[25:21];
            rt = ins[20:16];
            rd = ins[15:11];
            sa = ins[10:6];
            addr = ins[25:0];
            immediate = ins[15:0];
        end
    end

endmodule

RegFile模塊:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name:    regFile 
//  Function:   create 32 registers and initial them, 
//                  then use to store and read some data.
//////////////////////////////////////////////////////////////////////////////////
module RegFile(
    input wire clk,
    input wire RegWre,
    input wire WrRegData,
    input wire[1:0] RegOut,
    input wire[4:0] rs,
    input wire[4:0] rt,
    input wire[4:0] rd,
    input wire[31:0] pc,
    input wire[31:0] data,
    output reg[31:0] Data1,
    output reg[31:0] Data2
    );

    reg[4:0] writeReg;
    reg[31:0] writeData;

    reg[31:0] register[0:31];
    integer i;
    initial begin   //寄存器初始化
        for(i = 0; i < 32; i = i + 1)
            register[i] = 0;
    end

    //寫入數據

    always @(negedge clk) begin
        #1;
        case(RegOut)
            2'b00: writeReg = 5'b11111;
            2'b01: writeReg = rt;
            2'b10: writeReg = rd;
            default: writeReg = 5'b00000;
        endcase
        assign writeData = (WrRegData == 1)? data : pc;
        //讀取數據
        assign Data1 = register[rs];    //讀取寄存器中的值,源操作數1
        assign Data2 = register[rt];    //讀取寄存器中的值,源操作數2
        if(RegWre == 1 && writeReg != 0) register[writeReg] = writeData;
    end

endmodule

無符號和有符號擴展模塊(和第一次相比,學會了使用拼接符):

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
module signZeroExtend(
    input wire[15:0] indata,
    input wire[1:0] ExtSel,
    output reg[31:0] out
    );

    //使用拼接運算符,表達式更加簡潔
    always @(indata or ExtSel) begin
        case(ExtSel)
            2'b00: out <= {{27{1'b0}}, indata[10:6]};// sll指令,擴充sa
            2'b01: out <= {{16{1'b0}}, indata[15:0]};//立即數擴展,零擴展 如ori
            2'b10: out <= {{16{indata[15]}}, indata[15:0]};//立即數擴展,符號位擴展 beq、addi、lw、sw
            default: out <= {{16{indata[15]}}, indata[15:0]}; //其他情況,默認符號位擴展
      endcase
    end

endmodule

ALU模塊:

`timescale 1ns / 1ps
`include "defines.v"


`include "defines.v"
`timescale 1ns  /   1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name:    ALU 
// Function:    implement the logical operation
//////////////////////////////////////////////////////////////////////////////////
module ALU(
    input wire[31:0] ReadData1,     //源操作數1
    input wire[31:0] ReadData2, //源操作數2
    input wire[31:0] ExtData,       //擴展立即數
    input wire ALUSrcB,             //若爲1,則爲立即數
    input wire[2:0] ALUOp,          //操作符
    output reg zero,                //結果標誌
    output reg[31:0] result         //結果
    );


    wire[31:0] inReadData2;
    assign inReadData2 = ALUSrcB? ExtData : ReadData2;

    always@(ReadData1 or ReadData2 or ALUOp or ExtData or inReadData2)
        begin
            case(ALUOp)

                `AaddB: begin
                    result = ReadData1 + inReadData2;
                    zero = (result == 0)? 1 : 0;
                end

                `AsubB: begin
                    result = ReadData1 - inReadData2;
                    zero = (result == 0)? 1 : 0;
                end

                `AcompareB: begin
                    result = (ReadData1 < inReadData2) ? 1 : 0;
                    zero = (result == 0)? 1 : 0;
                end

                `Arshift: begin
                    result = ReadData1 >> inReadData2;
                    zero = (result == 0)? 1 : 0;
                end

                `Alshift: begin
                    result = ReadData1 << inReadData2;
                    zero = (result == 0)? 1 : 0;
                end

                `AorB: begin
                    result = ReadData1 | inReadData2;
                    zero = (result == 0)? 1 : 0;
                end

                `AandB: begin
                    result = ReadData1 & inReadData2;
                    zero = (result == 0)? 1 : 0;
                end

                `AxorB: begin
                    result = ReadData1 ^ inReadData2;
                    zero = (result == 0)? 1 : 0;
                end

            endcase
        end
endmodule

DataMemory模塊:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name:    DataMemory 
// Function:    store and read the data about result
//////////////////////////////////////////////////////////////////////////////////
module DataMemory(
     input wire[31:0] DAddr,    //數據地址
     input wire[31:0] DataIn,   //輸入數據
     input wire DataMemRW,      //=1,寫數據; =0,讀數據
     output reg[31:0] DataOut   //輸出數據
    );
    reg[7:0] memory[0:63];

    //初始化
    integer i;
    initial begin
        for(i = 0; i < 64; i = i + 1) memory[i] = 0;
    end

    always @(DAddr or DataIn or DataMemRW)begin
        if(DataMemRW)begin//寫入數據 大端模式
            memory[DAddr] = DataIn[31:24];
            memory[DAddr+1] = DataIn[23:16];
            memory[DAddr+2] = DataIn[15:8];
            memory[DAddr+3] = DataIn[7:0];
        end
        else begin //讀取數據
            DataOut[31:24] = memory[DAddr];
            DataOut[23:16] = memory[DAddr+1];
            DataOut[15:8] = memory[DAddr+2];
            DataOut[7:0] = memory[DAddr+3];
        end
    end

endmodule

二選一數據選擇模塊(除此之外,也可以分別實現其他幾個數據選擇模塊,但是不如直接在模塊內使用代碼實現簡單,所以選擇只實現其中一個):

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////
module DataSelect2_1(
    input wire[31:0] data1,
    input wire[31:0] data2,
    input wire sign,
    output wire[31:0] result
    );

    assign result = (sign == 1)? data2 : data1;

endmodule

DataDelay模塊(實現數據延遲傳遞):

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////
module Datadelay(
    input wire[31:0] in,
    input wire clk,
    output reg[31:0] out
    );

    always @(negedge clk) begin
        out = in;
    end

endmodule

最後,實現頂層模塊:

`timescale 1ns / 1ps
`include "ALU.v"
`include "ControlUnit.v"
`include "DataMemory.v"
`include "DataSelect2_1.v"
`include "Datadelay.v"
`include "PC.v"
`include "PCAddr.v"
`include "instructionMemory.v"
`include "signZeroExtend.v"
`include "RegFile.v"
//`include "DataDelay2.v"
////////////////////////////////////////////////////////////////////
module MultiCycleCPU(
    input wire clk,
    input wire Rst
    );

    wire[5:0] opCode;   //操作碼
    wire[31:0] Data1;   //來自寄存器的源操作數1
    wire[31:0] Data2;   //來自寄存器的源操作數2
    wire[31:0] Rdata1;//來自ADR的輸出
    wire[31:0] Rdata2;//來自BDR的輸出
    wire[31:0] curPC;   //目前PC地址
    wire[31:0] j_addr;//接跳轉至某地址 相關指令:j,jal
    wire[31:0] Regaddr;//存放在寄存器中的地址 相關指令:jr
    wire[25:0] Insaddr;//指令中存儲的指令
    wire[31:0] Regins;//指令寄存器,一個,暫存指令
    wire[31:0] Result;  //ALU運算結果
    wire[31:0] Result1;//ALUout模塊的輸出

    wire[2:0] ALUOp;    //ALU運算操作碼
    wire[31:0] ExtOut;  //擴展後的立即數
    wire[31:0] DMOut;   //讀取的寄存器中的樹 lw時使用
    wire[31:0] DelayReg1;//延遲前寫入寄存器數據
    wire[31:0] DelayReg2;//延遲後寫入寄存器數據
    wire[15:0] immediate;   //立即數,擴展前
    wire[4:0] rs, rt, rd, sa;   //三個類型的寄存器
    wire zero, PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, IRWre, WrRegData; //controlUnit單元信號   
    wire[1:0] ExtSel;
    wire[1:0] RegOut;
    wire[1:0] PCSrc;
    wire[2:0] State;
    /*
    module PC(
    input wire clk, //時鐘信號
    input wire Rst, //重置信號
    input wire PCWre, //PC是否更改
    input wire[1:0] PCSrc, //PC指令更新方式
    input wire[31:0] immediate,  //32位立即數
    input wire[31:0] addr,  //直接跳轉至某地址 相關指令:j,jal
    input wire[31:0] Regaddr,   //存放在寄存器中的地址 相關指令:jr
    output reg[31:0] address    //下一條指令地址
    );*/
    PC pc( clk, Rst, PCWre, PCSrc, ExtOut, j_addr, Data1, curPC);

    /*
    module instructionMemory(
    input wire[31:0] addr,
    input wire InsMemRW,
    input wire IRWre,
    input wire clk,
    output reg[31:0] ins
    );*/
    instructionMemory insMem(curPC, InsMemRW, IRWre, clk, Regins);

    /*module IR(
    input wire clk,
    input wire IRWre,
    input wire[31:0] ins,
    output reg[5:0] opcode,
    output reg[4:0] rs,
    output reg[4:0] rt,
    output reg[4:0] rd,
    output reg[4:0] sa,
    output reg[25:0] addr,
    output reg[15:0] immediate
    );*/
     IR ir( clk, IRWre, Regins, opCode, rs, rt, rd, sa, Insaddr, immediate);

    /*
    module PCAddr(
    input wire[25:0] in_addr,
    input wire[31:0] PC0,
    output reg[31:0] PC1
    );*/
    PCAddr pcaddr(Insaddr, curPC, j_addr);


    /*module regFile(
    input wire clk,
    input wire RegWre,
    input wire WrRegData,
    input wire[1:0] RegOut,
    input wire[4:0] rs,
    input wire[4:0] rt,
    input wire[4:0] rd,
    input wire[31:0] dataFromALU,
    input wire[31:0] dataFromDataMem,
    output wire[31:0] Data1,
    output wire[31:0] Data2
    );*/
    RegFile regfile(clk, RegWre, WrRegData, RegOut, rs, rt, rd, (curPC+4), DelayReg2, Data1, Data2);

    /*
    module Datadelay(
    input wire[31:0] in,
    input wire clk,
    output wire[31:0] out
    );*/
    Datadelay ADR(Data1, clk, Rdata1);
    Datadelay BDR(Data2, clk, Rdata2);

    /*
    module signZeroExtend(
    input wire[15:0] indata,
    input wire[1:0] ExtSel,
    output reg[31:0] out
    );*/
    signZeroExtend ext(immediate, ExtSel, ExtOut);

    /*
    module ALU(
    input wire[31:0] ReadData1,     //源操作數1
    input wire[31:0] ReadData2, //源操作數2
    input wire[31:0] ExtData,       //擴展立即數
    input wire ALUSrcB,             //若爲1,則爲立即數
    input wire[2:0] ALUOp,          //操作符
    output reg zero,                //結果標誌
    output reg[31:0] result         //結果
    );*/
    ALU alu(Data1, Data2, ExtOut, ALUSrcB, ALUOp, zero, Result);

    Datadelay ALUout(Result, clk, Result1);

    /*module DataMemory(
     input wire[31:0] DAddr,    //數據地址
     input wire[31:0] DataIn,   //輸入數據
     input wire DataMemRW,      //=1,寫數據; =0,讀數據
     output reg[31:0] DataOut   //輸出數據
    );*/
    DataMemory datamemoey(Result1, Data2, DataMemRW, DMOut);

    DataSelect2_1 dataselect(Result, DMOut, ALUM2Reg, DelayReg1);

    Datadelay ALUM2DR(DelayReg1, clk, DelayReg2);

    ControlUnit control(opCode, zero, clk, Rst,PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcB, ALUM2Reg, DataMemRW, ExtSel, RegOut, PCSrc, ALUOp, State);

endmodule

2、測試代碼設計

根據實驗提供的提示,設計測試代碼如下:
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

指令以32位形式,存儲在my_store.txt中,示例如下:
這裏寫圖片描述

3、測試模塊的實現

`timescale 1ns / 1ps
`include "MultiCycleCPU.v"
///////////////////////////////////////////////////////
module Test;
    reg Reset; //初始化地址
    reg clk;
    /*wire[5:0] opCode;
    wire[31:0] Out1;
    wire[31:0] Out2;
    wire[31:0] curPC;
    wire[31:0] Result;
    */
    MultiCycleCPU Multi(
        .clk(clk), 
        .Rst(Reset)/*,
        .opCode(opCode),
        .Out1(Out1),
        .Out2(Out2),
        .curPC(curPC),
        .Result(Result)
        */);

    initial begin
        //Initialize Inputs
        clk = 0;
        Reset = 0;

        #100;
        clk = ~clk;

      forever #100 begin // 產生時鐘信號
                Reset = 1;
            clk = ~clk;
        end
    end

endmodule

四、實驗結果(部分)

  只列出部分,以作參考。
  
這裏寫圖片描述
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章