计算机组成原理 实验五 单周期CPU设计与实现——十条指令CPU

CPU部件实现之ALU、寄存器堆、PC、RAM

1 实验目的和要求

基于先修课程,根据系统设计思想,使用硬件描述语言设计实现一款兼容MIPS32指令集子集的处理器(CPU),完成单周期CPU设计,或多周期CPU设计,或5级流水线CPU设计(递进式、难度依次提升。必须至少完成单周期CPU的设计工作),并将设计的CPU,下载至FPGA开发板上运行。以此贯穿数字逻辑、计算机组成原理、计算机体系结构课程,实现从逻辑门至完整CPU处理器的设计。

1.1 实验目的

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

1.2 实验要求

设计一个多周期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
功能:rt<-rs | (zero-extend)immediate

移位指令:
(7)sll rd, rt,sa

011000 未用 rt(5位) rd(5位) sa reserved
功能:rd<-rt<<(zero-extend)sa,左移sa位 ,(zero-extend)sa

比较指令:
(8) slt rd, rs, rt 带符号数

100110 rs(5位) rt(5位) rd(5位) reserved
功能:if (rs<rt) rd =1 else rd=0

(9) sltiu rt, rs,immediate 不带符号

100111 rs(5位) rt(5位) immediate(16位)
功能:if (rs <(zero-extend)immediate) rt =1 else rt=0

存储器读写指令:
(10)sw rt, immediate(rs)

110000 rs(5位) rt(5位) immediate(16位)
功能:memory[rs+ (sign-extend)immediate]<-rt。即将rt寄存器的内容保存到r寄存器内容和立即数符号扩展后的数相加作为地址的内存单元中。

(11)lw rt, immediate(rs)

110001 rs(5位) rt(5位) immediate(16位)
功能:rt <- memory[rs +(sign-extend)immediate]。即读取rs寄存器内容和立即数符号扩展后的数相加作为地址的内存单元中的数,然后保存到rt寄存器中。

分支指令:
(12)beq rs,rt, immediate

110100 rs(5位) rt(5位) immediate(16位)
功能:if(rs=rt) pc <-pc + 4 + (sign-extend)immediate <<2 else pc <-pc + 4

(13)bltz rs,immediate

110110 rs(5位) 00000 immediate
功能:if(rs<0) pc←pc + 4 + (sign-extend)immediate <<2 else pc ←pc + 4

跳转指令:
(14)j addr

111000 addr[27:2]
功能:pc <-{(pc+4)[31:28],addr[27:2],2’b00},跳转。

(15)jr rs

111001 rs(5位) 未用 未用 reserved
功能:pc <- rs,跳转。

调用子程序指令:
(16)jal addr

111010 addr[27:2]
功能:调用子程序,pc <- {(pc+4)[31:28],addr[27:2],2’b00};$31<-pc+4,返回地址设置;子程序返回,需用指令 jr $31。跳转地址的形成同 j addr 指令。

停机指令:
(17)halt (停机指令)

111111 00000000000000000000000000(26位)
不改变pc的值,pc保持不变。

2 实验内容

2.1 多周期CPU设计

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

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

多周期CPU指令处理过程:
多周期CPU指令处理过程
MIPS指令的三种格式:

在这里插入图片描述

op:为操作码; rs:为第1个源操作数寄存器,寄存器地址(编号)是00000~ 11111,00~1F;
rt:为第2个源操作数寄存器,或目的操作数寄存器,寄存器地址(同上); rd:为目的操作数寄存器,寄存器地址(同上);
sa:为位移量(shift amt),移位指令用于指定移多少位; funct:为功能码,在寄存器类型指令中(R类型)用来指定指令的功能;
immediate:为16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载(Load)/数据保存(Store)指令的数据地址字节偏移量和分支指令中相对程序计数器(PC)的有符号偏移量;
address:为地址。

多周期CPU状态转移图

在这里插入图片描述

状态的转移有的是无条件的,例如从sIF状态转移到sID就是无条件的;有些是有条件的,例如sEXE状态之后不止一个状态,到底转向哪个状态由该指令功能,即指令操作码决定。每个状态代表一个时钟周期。

控制信号作用表:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

相关部件及引脚说明: 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,则zero=1;否则zero=0
sign,运算结果标志,结果最高位为0,则sign=0,正数;否则,sign=1,负数

在这里插入图片描述

2.2 多周期CPU实现

多周期CPU模块划分图
在这里插入图片描述

本实验将CPU划分为14个模块。将一些数据选择的模块并入到其所需要的功能模块中,没有完全依据多周期CPU数据通路图进行划分,否则需要过多模块,划分的太过冗余。

3 模块功能及核心代码:

1. pcAdd

模块功能:根据控制信号PCSrc,计算获得下一个pc以及控制信号Reset重置。

核心代码:

always@(*)
    begin
        if(!RST) begin
            nextPC = 0;
        end
        else begin
            pc = curPC + 4;
            case(PCSrc)
                2'b00: nextPC = curPC + 4;
                2'b01: nextPC = curPC + 4 + immediate * 4;
                2'b10: nextPC = rs;
                2'b11: nextPC = {pc[31:28],addr,2'b00};
            endcase
        end

2. PC

模块功能:根据控制信号PCWre,判断pc是否改变以及根据Reset信号判断是否重置。

核心代码:

always@(posedge CLK or negedge RST)
    begin
        if(!RST) // Reset == 0, PC = 0
            begin
                curPC <= 0;
            end
        else 
            begin
                if(PCWre) // PCWre == 1
                    begin 
                        curPC <= nextPC;
                    end
                else    // PCWre == 0, halt
                    begin
                        curPC <= curPC;
                    end
            end
    end

3. InsMEM

模块功能:依据当前pc和信号量InsMemRW,读取指令寄存器中,相对应地址的指令。

核心代码:

initial 
    begin//加载数据到存储器ROM
        $readmemh("D:\\Modelsim\\examples\\CPU2.0\\romdata.txt", rom);
    end
    
    always@(IAddr or InsMemRW)//敏感信号
    begin
        if(InsMemRW)
            begin//大端
                IDataOut[7:0] = rom[IAddr + 3];
                IDataOut[15:8] = rom[IAddr + 2];
                IDataOut[23:16] = rom[IAddr + 1];
                IDataOut[31:24] = rom[IAddr];
            end 
end

4. IR

模块功能:为了使指令代码保持稳定。

核心代码:
initial begin
IRInstruction = 0;
end

always@(posedge CLK)//上升沿
begin
    if(IRWre) begin
        IRInstruction <= instruction;
    end

end

5. InstructionCut

模块功能:对指令进行分割,获得相对应的指令信息。

核心代码:

always@(instruction) 
    begin
        op = instruction[31:26];
        rs = instruction[25:21];
        rt = instruction[20:16];
        rd = instruction[15:11];
        sa = instruction[10:6];
        immediate = instruction[15:0];
        addr = instruction[25:0];
    end

6. ControlUnit

模块功能:控制单元,依据指令的操作码(op)、标记符(ZERO)以及当前CPU状态,依据表三
控制信号、指令以及执行状态之间的相互关系,输出相匹配控制信号量。

核心代码:

reg [2:0] state, nextState;    
    parameter [2:0] iniState = 3'b111,
                    sIF = 3'b000,
                    sID = 3'b001,
                    sEXE = 3'b010,
                    sMEM = 3'b100,
                    sWB = 3'b011;
    initial begin
        state = iniState;
        PCWre = 0;  
        InsMemRW = 0;  
        IRWre = 0;  
        RegWre = 0;    
        ExtSel = 0;  
        PCSrc = 2'b00;  
        RegDst = 2'b11;
        ALUOp = 0;  
        ExtSel = 0;
        WrRegDSrc = 0;
        ALUSrcA = 0;
        ALUSrcB = 0;
        DBDataSrc = 0;
        mRD = 0;
        mWR = 0;
    end
    always@(posedge CLK) begin
        if(!RST) begin
            state <= sIF;
        end else begin
            state <= nextState;
        end
    end
    always@(state or op or zero) begin
        // 
        case(state)
            iniState : nextState = sIF;
            sIF: nextState = sID;
            sID: begin
                case(op[5:3])
                    3'b111: nextState = sIF;    //
                    default: nextState = sEXE;
                endcase
            end
            sEXE: begin 
                if((op == 6'b110100) || (op == 6'b110110)) begin
                    //beq,bltz
                    nextState = sIF;
                end else if(op == 6'b110000 || op == 6'b110001) begin
                    //sw,lw
                    nextState = sMEM;
                end else begin
                    nextState = sWB;
                end
            end
            sMEM: begin
                if(op == 6'b110000) begin
                    //sw
                    nextState = sIF;
                end else begin
                    //lw
                    nextState = sWB;
                end
            end
            sWB: nextState = sIF;
        endcase
        // PCWre and InsMemRW 
        if(nextState == sIF && op != 6'b111111 && state != iniState) begin
            // halt
            PCWre = 1;
            InsMemRW = 1;  
        end else begin
            PCWre = 0;
            InsMemRW = 0;  
        end
        // IRWre
        if(state == sIF || nextState == sID) begin
            IRWre = 1;
        end else begin
            IRWre = 0;
        end
        // ALUSrcA
        if(op == 6'b011000) begin
            // sll
            ALUSrcA = 1;
        end else begin
            ALUSrcA = 0;
        end
        
        // ALUSrcB
        if(op == 6'b000010 || op == 6'b010010 || op == 6'b110000 || op == 6'b110001 || op == 6'b100111) begin
           // addi,ori,sw,lw,sltiu
           ALUSrcB = 1;
        end else begin
           ALUSrcB = 0;
        end
        // DBDataSrc
        if(op == 6'b110001) begin
            // lw
            DBDataSrc = 1;
        end else begin
            DBDataSrc = 0;
        end
        // RegWre and WrRegDSrc and RegDst
        if((state == sWB && op != 6'b110100 && op != 6'b110000 && op != 6'b110110) || (op == 6'b111010 && state == sID)) begin
            RegWre = 1;
            if(op == 6'b111010) begin
                // jal
                WrRegDSrc = 0;
                RegDst = 2'b00;
            end else begin
                WrRegDSrc = 1;
                if(op == 6'b000010 || op == 6'b010010 || op == 6'b100111 || op == 6'b110001) begin
                    // addi, ori, sltiu, lw
                    RegDst = 2'b01;
                end else begin
                    // add, sub, or, and, slt, sll
                    RegDst = 2'b10;
                end
            end
        end else begin
            RegWre = 0;
        end       
        // InsMemRW
        if(op != 6'b111111)
            InsMemRW = 1; 
        // mRD 
        mRD = (op == 6'b110001) ? 1 : 0; // lw
        // mWR
        mWR = (state == sMEM && op == 6'b110000) ? 1 : 0; // sw
        // ExtSel
        ExtSel = (op == 6'b000010 || op == 6'b110001 || op == 6'b110000 || op == 6'b110100 || op == 6'b110110) ? 1 : 0; // addi?lw?sw?beq?bltz 
        // PCSrc
        if(op == 6'b111001) begin
            // jr
            PCSrc = 2'b10;
        end else if((op == 6'b110100 && zero) || (op == 6'b110110 && !zero)) begin
            // beq ? bltz??
            PCSrc = 2'b01;
        end else if(op == 6'b111010 || op == 6'b111000) begin
            // j,jal
            PCSrc = 2'b11;
        end else begin
            PCSrc = 2'b00;
        end
        // ALUOp
        case(op)
            6'b000010: ALUOp = 3'b000;  // addi
            6'b010010: ALUOp = 3'b101;  // ori
            6'b010000: ALUOp = 3'b101;  // or
            6'b000001: ALUOp = 3'b001;  // sub
            6'b010001: ALUOp = 3'b110;  // and
            6'b011000: ALUOp = 3'b100;  // sll
            6'b110100: ALUOp = 3'b001;  // beq
            6'b100110: ALUOp = 3'b011;  // slt
            6'b100111: ALUOp = 3'b010;  // sltiu
            6'b110110: ALUOp = 3'b001;  // bltz
            6'b110001: ALUOp = 3'b000;  //sw
            6'b110000: ALUOp = 3'b000;  //lw
        endcase
    end

7. RegisterFile

模块功能:寄存器组,通过控制单元输出的控制信号,进行相对应的读或写操作。

核心代码:

reg [31:0] regFile[0:31]; //  ????????reg??
    integer i;
    initial begin
        for (i = 0; i < 32; i = i+ 1) regFile[i] <= 0;  
    end
    always@(ReadReg1 or ReadReg2) 
    begin
        ReadData1 = regFile[ReadReg1];
        ReadData2 = regFile[ReadReg2];
        //$display("regfile %d %d\n", ReadReg1, ReadReg2);
    end
    always@(negedge CLK)
    begin
        //$0??0??????????????0
        if(RegWre && WriteReg)
            begin
                regFile[WriteReg] <= WriteData;
            end
    end  
    always@(*)
    begin
        case(RegDst)
            2'b00: WriteReg = 31;
            2'b01: WriteReg = ReadReg2;
            2'b10: WriteReg = rd;
        endcase
end

8. SignZeroExtend

模块功能:根据指令相关的控制信号ExtSel,对立即数进行扩展。

核心代码:

assign extendImmediate[15:0] = immediate;
assign extendImmediate[31:16] = ExtSel ? (immediate[15] ? 16'hffff : 16'h0000) : 16'h0000;

9. ALU

模块功能:算术逻辑单元,对两个输入依据ALUOp进行相对应的运算。

核心代码:

reg [31:0] A;
    reg [31:0] B;
    initial begin
        result = 0;
        zero = 0;
    end
    always@(ReadData1 or ReadData2 or ALUSrcA or ALUSrcB or ALUOp) 
    begin
        A = (ALUSrcA == 0) ? ReadData1 : sa;
        B = (ALUSrcB == 0) ? ReadData2 : extend;
        case(ALUOp)
            3'b000: result = A + B;
            3'b001: result = A - B;
            3'b010: result = (A < B) ? 1 : 0;
            3'b011: result = (((ReadData1 < ReadData2) && (ReadData1[31] == ReadData2[31] )) ||( ( ReadData1[31] ==1 && ReadData2[31] == 0))) ? 1:0;
            3'b100: result = B << A;
            3'b101: result = A | B;
            3'b110: result = A & B;
            3'b111: result = A ^ B;
        endcase
        zero = (result == 0) ? 1 : 0;
    end

10. DataMEM

模块功能:数据存储器,通过控制信号,对数据寄存器进行读或者写操作,并且此处模块额外合并了输出DB的数据选择器,此模块同时输出写回寄存器组的数据DB。

核心代码:

initial begin 
        DB <= 16'b0;
    end
     reg [7:0] ram [0:31];     //     
    always@(mRD or DAddr or DBDataSrc)
    begin
        //
        DataOut[7:0] = mRD ? ram[DAddr + 3] : 8'bz; //    
        DataOut[15:8] = mRD ? ram[DAddr + 2] : 8'bz;     
        DataOut[23:16] = mRD ? ram[DAddr + 1] : 8'bz;     
        DataOut[31:24] = mRD ? ram[DAddr] : 8'bz;
    
        DB = (DBDataSrc == 0) ? DAddr : DataOut;
    end 
    always@(mWR or DAddr)
    begin   
        //
        if(mWR)
            begin
                ram[DAddr] = DataIn[31:24];    
                ram[DAddr + 1] = DataIn[23:16];
                ram[DAddr + 2] = DataIn[15:8];     
                ram[DAddr + 3] = DataIn[7:0];    
            end
    end

11. 寄存器ADR、BDR、ALUoutDR、DBDR

模块功能:切分数据通路,将大组合逻辑切分为若干个小组合逻辑,大延迟变为多个分段小延迟。

核心代码:

module TempReg(
        input CLK,
        input [31:0] IData,
        output reg[31:0] OData
    );
    
    initial begin 
        OData = 0;
    end
    always@(posedge CLK) begin
        OData <= IData;
    end
endmodule

12. 顶层模块:MultiCycleCPU

模块功能:在顶层模块中将各个已实现的底层模块进行实列,并且用verilog语言将各个模块用线连接起来。

ControlUnit ControlUnit(.CLK(CLK),
                            .RST(RST),
                            .zero(zero),
                            .op(op),
                            .IRWre(IRWre),
                            .PCWre(PCWre),
                            .ExtSel(ExtSel),
                            .InsMemRW(InsMemRW),
                            .WrRegDSrc(WrRegDSrc),
                            .RegDst(RegDst),
                            .RegWre(RegWre),
                            .ALUSrcA(ALUSrcA),
                            .ALUSrcB(ALUSrcB),
                            .PCSrc(PCSrc),
                            .ALUOp(ALUOp),
                            .mRD(mRD),
                            .mWR(mWR),
                            .DBDataSrc(DBDataSrc));
    pcAdd pcAdd(.RST(RST),
                .PCSrc(PCSrc),
                .immediate(extend),
                .addr(addr),
                .curPC(curPC),
                .rs(A),
                .nextPC(nextPC));
    PC PC(.CLK(CLK),
          .RST(RST),
          .PCWre(PCWre),
          .nextPC(nextPC),
          .curPC(curPC));             
    InsMEM InsMEM(.IAddr(curPC), 
                  .InsMemRW(InsMemRW), 
                  .IDataOut(instruction));
    IR IR(.instruction(instruction),
          .CLK(CLK),
          .IRWre(IRWre),
          .IRInstruction(IRInstruction));
    InstructionCut InstructionCut(.instruction(IRInstruction),
                                  .op(op),
                                  .rs(rs),
                                  .rt(rt),
                                  .rd(rd),
                                  .sa(sa),
                                  .immediate(immediate),
                                  .addr(addr));
    SignZeroExtend SignZeroExtend(.immediate(immediate),
                                  .ExtSel(ExtSel),
                                  .extendImmediate(extend));
    RegisterFile RegisterFile(.CLK(CLK),
                              .ReadReg1(rs),
                              .ReadReg2(rt),
                              .rd(rd),
                              .WriteData(WrRegDSrc ? dataDB : curPC + 4),
                              .RegDst(RegDst),
                              .RegWre(RegWre),
                              .ReadData1(A),
                              .ReadData2(B),
                              .WriteReg(WriteReg)); 
    TempReg ADR(.CLK(CLK),
                .IData(A),
                .OData(dataA));
    TempReg BDR(.CLK(CLK),
                .IData(B),
                .OData(dataB));    
    ALU alu(.ALUSrcA(ALUSrcA),
            .ALUSrcB(ALUSrcB),
            .ReadData1(dataA),
            .ReadData2(dataB),
            .sa(sa),
            .extend(extend),
            .ALUOp(ALUOp),
            .zero(zero),
            .result(result));
                            
    TempReg ALUoutDR(.CLK(CLK),
                     .IData(result),
                     .OData(dataResult));
    DataMEM DataMEM(.mRD(mRD),
                    .mWR(mWR),
                    .DBDataSrc(DBDataSrc),
                    .DAddr(result),
                    .DataIn(dataB),
                    .DataOut(DataOut),
                    .DB(DB));        
    TempReg DBDR(.CLK(CLK),
                 .IData(DB),
                 .OData(dataDB));

4 实验结果与分析

测试程序段:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

将指令代码初直接写入始化到指令存储器中。初始化PC的值,也就是以上程序段首地址PC=0x00000000,以上程序段从0x00000000地址开始存放。运行ModelSim进行仿真,看波形。使用表3进行测试CPU正确性,将其中的指令写入一个文件romData.txt。在模块InsMEM中进行读入。

initial 
    begin//加载数据到存储器ROM
        $readmemh("D:\\Modelsim\\examples\\CPU2.0\\romdata.txt", rom);
End

在这里插入图片描述

分析:加法操作指令,将0号寄存器的数据(0)与立即数8相加,并且将运算结果(8)写回1号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:或操作指令,将0号寄存器的数据(0)与立即数2进行或操作,并且将运算结果(2)写回2号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:或操作指令,将2号寄存器的数据(2)与1号寄存器的数据(8)进行或操作,并且将运算结果(10)写回3号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:减操作指令,将3号寄存器的数据(10)与1号寄存器的数据(8)进行减操作,并且将运算结果(2)写回4号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:与操作指令,将4号寄存器的数据(2)与2号寄存器的数据(2)进行与操作,并且将运算结果(2)写回5号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:移位操作指令,将5号寄存器的数据(2)左移2位,并且将运算结果(8)写回5号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:分支指令,将5号寄存器的数据(8)与1号寄存器的数据(8)进行比较,如果二者相等则跳转,跳转然后执行上一条指令(地址:0x00000014),否则继续顺序执行。本条指令将发生跳转,因为$5与$1所存储的数据相等。因此5号寄存器的数据(8)将左移2位,并且将运算结果(32)写回5号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:调用子程序指令,调用指令寄存器中地址0x00000040所存储的指令,同时将寄存器组中的31号寄存器存储当前PC+4的值(0x00000020)。指令需要取指令、指令译码两个步骤,不需要指令执行、存储器访问和数据写回,因此两个周期即可完成。

在这里插入图片描述

分析:比较指令,将$12寄存器的数据(2)与$1寄存器的数据(8)比较,若$12的数据小于$1寄存器的数据,则将1写入$8,否则写入0,本指令将1写入$8。不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:加操作指令,将$0寄存器的数据(0)与立即数(-2)相加,将结果(-2)写入$13,。指令需要取指令、指令译码、指令执行和数据写回四个步骤,不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:比较指令,将$8寄存器的数据(1)与$13寄存器的数据(-2)比较,若$8的数据小于$13寄存器的数据,则将1写入$9,否则写入0,本指令将0写入$9。不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:比较指令,将$9寄存器的数据(0)与立即数(2)比较,若$9的数据小于2,则将1写入$10,否则写入0,本指令将1写入$10。不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:比较指令,将$10寄存器的数据(1)与立即数(0)比较,若$10的数据小于0,则将1写入$10,否则写入0,本指令将0写入$11。不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:加操作指令,将$13寄存器的数据(-2)与立即数(1)相加,将结果(-1)写入$13,。指令需要取指令、指令译码、指令执行和数据写回四个步骤,不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:分支指令,将13号寄存器的数据(-1)与0进行比较,如果小于则跳转,跳转然后执行上一条指令(地址:0x00000034),否则继续顺序执行。本条指令将发生跳转,因为$13所存储的数据(-1)小于0。因此$13的数据将(-1)加1,并且将运算结果(0)写回寄存器$13。不需要存储器访问,因此四个周期即可完成,在最后一个时钟周期的下降沿写入数据。

在这里插入图片描述

分析:跳转指令,跳转到地址0x0000004C。指令需要取指令和指令译码,不需要指令执行、存储器访问和数据写回,因此两个周期即可完成。

在这里插入图片描述

分析:停机指令,即PC将不再改变。

5 项目文件

百度网盘:
链接:https://pan.baidu.com/s/1-rBp2vUFfPayk6L_MKL_FA
提取码:7u8q
在这里插入图片描述

InsMEM.v文件中需要自己更改绝对路径

6 后记

既来之 则赞之
若有疑问,欢迎评论
本文仅供参考,务必独立思考

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐