MIPS指令集单周期CPU兼Verilog学习

1.单周期CPU原理(单个时钟周期内的操作):

        (1)取指,PC+4

        (2)译码

        (3)取操作数,ALU运算

        (4)访存(MEM)

        (5)写回(RegWr)

        将每一级操作抽象为CPU中的若干个模块:

                (1)指令读取模块(指令存储器)

                (2)指令寄存器(IR)

                (3)数据寄存器(rs,rt,rd)

                (4)逻辑运算器件(ALU)

                (5)数据存储器

                (6)控制单元

2.实验要求

        MIPS指令集三种指令:

        R型指令

                汇编代码格式:op rd,rs,rt

                机器中存储:

                

                含义:[rs]+[rt]  \rightarrow [rd]

         I型指令:

                汇编代码格式:op rt,rs,imm16

                机器中存储:

                

                含义:[rs]+imm16 \rightarrow [rt] 

        J型指令:  

                汇编代码格式:op imm26

                机器中存储:

                

                需要注意的是,这里的跳转地址为: PC高四位+imm26+00   ,构成了32的地址

                来看下MIPS指令集:

        来看一下整体的数据通路:


3.代码实现        

         看上去非常复杂,我们把每个元件单拎出来看输入和输出:

            1)符号扩展单元Extender

                   符号扩展单元看起来比较简单,它的作用:

                            (1)将imm16符号扩展至32位后送至ALU

                               

                   ExtOp=\left\{\begin{matrix} 0 &,ZeroExt& \\ 1&,SignExt \end{matrix}\right.

              input1:[15:0] imm16,           input2:ExtOp

              output:[31:0] imm32

        扩展器模块代码如下(extender.v)         

module extender(
    input [15:0] imm16,
    input ExtOp,
    output [31:0] imm32
    );

    //ExtOp 为 0 做零扩展,为 1 做符号扩展
    //imm16是以补码储存在机器中的
 
    assign imm32 = {imm16[15],{16{1'b0}},imm16[14:0]}; 

    
endmodule

     进行一下简单的仿真(extender_tb.v)

module extender_tb();
    reg [15:0] imm16;
    reg ExtOp;
    wire [31:0] imm32;
    
    initial begin
        imm16 = 16'b1111111111111111;
        ExtOp = 1;
    end
    
    extender test(imm16,ExtOp,imm32);
    
endmodule

          11...11经过符号扩展后变成了10...0011...111,观察仿真波形:

 扩展器完成!

           2)pc模块

                  pc的输入输出:input1:pc_new(经过+4的pc 或者 跳转地址)

                                            input2:clk(在板子上是手动的按键/上升沿触发)

                                            input3:reset(pc复位信号,由板子上的SW0给出)

                                            output1:pc_out(输出到InsMem中)

                                            output2:pc_plus(经过+4的pc,这样可以节省一个专门pc+4器件)  

                   pc模块参考代码(pc.v): 注:这里的pc_out设置一个初始值0

`timescale 1ns / 1ps

module pc(
    input [31:0] pc_in,
    input clk,
    input reset,
    output reg[31:0] pc_out,   //输出到mem进行取指
    output [31:0] pc_plus   //经过+4的pc
    );
    
    initial begin
        pc_out = 0;
    end
    
    always @(posedge clk) begin
        if(reset == 0) begin
            pc_out=pc_in;
       end 
       else begin
            pc_out=0;
       end
    end
    
    assign pc_plus = pc_out + 4;
    
endmodule

         pc模块测试用的仿真文件(pc_tb.v):

module pc_tb();
    reg clk;
    reg[31:0] pc_in;
    reg reset;
    wire[31:0] pc_out;
    wire[31:0] pc_plus;
    
    initial begin
        clk=0;
        pc_in=0;
        reset=0;
    end
    
    always @(*) begin
            #5 clk <= ~clk;
        end
    
    pc pc_test(pc_in,clk,reset,pc_out,pc_plus);

endmodule

               pc和自增模块完成!

       3)指令存储器模块(InsMem)

                功能:根据输入的pc,在存储器中找到pc所指向的那条指令后,将其发送给各个部件。

                输入:input1:pc

                           output:经过拆解过的指令

                注:(1)存储器的指令写入要在模块中实现而不是在仿真中实现

                       (2)将存储器设计为32位的寄存器组而不是8位的寄存器组有一个好处:当读入16进制的文件时,8位的寄存器会两个两个数据读,因此在读入单条(正常顺序)                            的指令时,顺序会错乱。比如:

                           

                         这时8位寄存器组的存储内容会为:0111_1000_0101_0110_0011_0100_ 0001_0010

                         这样做指令分割的时候会非常麻烦。

                下面来看下InsMem模块的代码(InsMem.v):


module InsMem(
    input [31:0]addr,   //即pc值
    output [5:0]op,     //31:26
    output [4:0]rs,     //25:21
    output [4:0]rt,     //20:16
    output [4:0]rd,     //15:11
    output [4:0]shamt,  //10:6
    output [5:0]func    //5:0
    
    );
    
    reg [31:0] mem [63:0];  //最多可存64条指令
    initial begin
        $readmemh("D:/meiyong/testdata.txt",mem);
    end
    
    assign op = mem[addr >> 2][31:26];      //这里用的是32位的指令寄存器组,因此pc要除以4
    assign rs = mem[addr >> 2][25:21];
    assign rt = mem[addr >> 2][20:16];
    assign rd = mem[addr >> 2][15:11];
    assign shamt = mem[addr >> 2][10:6];
    assign func = mem[addr >> 2][5:0];
    
    
endmodule

                写个仿真测试一下(test_insmem_tb.v):

module test_insmem_tb( );
    reg [31:0] addr;
    wire [5:0]op;     //31:26
    wire [4:0]rs;     //25:21
    wire [4:0]rt;     //20:16
    wire [4:0]rd;     //15:11
    wire [4:0]shamt;  //10:6
    wire [5:0]func;
    
    InsMem tset_insmem(addr,op,rs,rt,rd,shamt,func);
    
    initial begin
        addr = 0;
    end
    
endmodule

                  检验一下输出波形:

                                                                                      

               注:第一条指令是12345678

        4)数据存储器模块(dataMem)

                数据存储器也跟指令存储器一样采用32位的寄存器组来实现。

                输入:访问/写入地址,写入的数据,写使能信号Wr。

                输出:读出的数据

                数据存储器模块代码(dataMem.v):

`timescale 1ns / 1ps

module dataMem(
    input clk,
    input [31:0] dataAdd,
    input [31:0] dataIn,
    input dataWr,
    output reg[31:0] dataOut
    );
    
    integer i;
    reg [31:0] mem [511:0];
    
    initial begin 
        for(i=0;i<512;i=i+1) begin
            mem[i] <= 0;
        end
    end
    
    always @(posedge clk) begin 
        if(dataWr == 1) begin
            mem[dataAdd>>2] <= dataIn; 
        end
    end
    
    always @(*) begin
        dataOut <= mem[dataAdd>>2]; 
    end
endmodule

        

          5)数据寄存器组模块(Register)

                寄存器组内有32个32位的通用寄存器,通过rs、rt、rd来指定访问的是哪个寄存器。

                输入:input1:clk        input2:rs,rt,rd        input3:distSel(写哪个寄存器,rt还是rd)        input4:RegWr        input5:将写入rd的dataIn

                输出:两条线out1和out2

                数据寄存器模块源代码(Register.v):

module Register(
    input clk,
    input [4:0]rs,
    input [4:0]rt,
    input [4:0]rd,
    input distSel,  //写入rt还是rd,0写rt,1写rd
    input RegWr,
    input [31:0]dataIn,
    output reg[31:0]out1,
    output reg[31:0]out2
    );
    
    reg [31:0] Reg[31:0];   //32个通用寄存器
    
    integer i;
    //对寄存器初始化
    initial begin
        for (i = 0; i < 32; i = i+ 1) Reg[i] <= 0;  
    end
    
    always @(posedge clk) begin
        if(RegWr == 1) begin 
              case(distSel) 
                0: Reg[rt] = dataIn;  //写rt
                1: Reg[rd] = dataIn; //写rd
              endcase       
        end
    end
    
    always @(*) begin
        out1 <= Reg[rs];
        out2 <= Reg[rt];
    end
    
endmodule

                测试用的仿真代码(Reg_test_tb.v): //这里把Register中Reg的初始化改成了 Reg[i] = i ;

module Reg_test_tb( );
    reg clk;
    reg [4:0]rs;
    reg [4:0]rt;
    reg [4:0]rd;
    reg distSel;
    reg RegWr;
    reg [31:0]dataIn;
    wire [31:0]out1;
    wire [31:0]out2;
    
    Register myReg(clk,rs,rt,rd,distSel,RegWr,dataIn,out1,out2);
    
    initial begin
        clk=0;
        rs=5'b00101;
        rt=5'b00110;
        rd=5'b00111;
        distSel=1;   
        RegWr=0;
        dataIn=0;
    end
    
    always @(*)  begin
        #5 clk <= ~clk;
    end
    
endmodule

                out1和out2的波形如下:

         6)运算单元模块(ALU)

                这是和控制单元一样最为复杂的模块,先来看看需要实现多少个函数:

         虽然有20(21)个函数,但是总结起来,ALU部件要用的也就9种操作,因此ALUop码只需要四位(0000~1000)。

ALUop功能涉及到的指令

0000

两数相加add,addi
0001两数相减sub,subi
0010按位与and,andi
0011按位或or,ori
0100按位异或xor,xori
0101逻辑右移(直接移)srl
0110逻辑左移(直接移)sll
0111算术右移(补符号位)sra
1000比较是否相等beq,bne
1001设置高位lui

               注意区分算术移位和逻辑移位。

                逻辑移位:直接移位,空的地方补0。

                算术右移:空出的位全部补符号位。

                运算器模块代码(ALU.v):

`timescale 1ns / 1ps



module ALU(
        input [3:0]ALUop,
        input [31:0]in1,    //可能是rs或者shamt
        input [31:0]in2,    //可能是rt或者imm16
        output reg zero,    //用来指示beq是否相等,1相等,0不相等
        output reg[31:0] res
    );
    
     integer i;
     
    
    always @(*) begin 
        case (ALUop)
            //求和
            4'b0000: begin 
                res = in1 + in2;  
                zero = 0; 
            end
            //求差
            4'b0001: begin
                res = in1 - in2;
            end
            //按位与
            4'b0010: begin
                res = in1 & in2;
            end
            //按位或
            4'b0011: begin 
                res = in1 | in2;
            end
            //按位异或
            4'b0100: begin
                res = in1 ^ in2;
            end
            //直接右移,in1是shamt,in2是rt
            4'b0101: begin
                res = in2 >> in1[4:0]; 
            end
            //直接左移
            4'b0110:begin
                res = in2 << in1[4:0];
            end
            //补符号位右移
            4'b0111: begin
                res = in2 >> in1[4:0];
                if(in2[31]==1'b1) begin
                    for(i=0;i<in1;i=i+1)  res[31-i] = 1;
                end
                else begin
                    for(i=0;i<in1;i=i+1)  res[31-i] = 0;
                end
            end
            //判断相等
            4'b1000: begin
                zero = in1==in2 ? 1'b1 : 1'b0;
            end
            //lui指令:设置rt的高16位
            4'b1001: begin
                res = {in2[31],in2[14:0],{16{0}}};
            end
        endcase
    end
    
endmodule

                测试一下算术右移功能(ALU_test_tb.v):

module ALU_test_tb();
    reg[3:0] ALUop;
    reg[31:0] in1;
    reg[31:0] in2;
    wire zero;
    wire[31:0] res;
    
    initial begin
        ALUop = 4'b0111;
        in1 = 5'b00100;
        in2 = 32'hf1234567;
    end
    
    ALU testALU(ALUop,in1,in2,zero,res);
    
endmodule

      7)pc选择(PCselect)

               该模块用一个四选一的数据选择器来实现,输出的是pc的下一个值,即下一步要运行指的令的地址

               输入的四个信号分别为:

序号功能所服务的指令
0正常进行下一条指令正常指令

1

beq或bne指令生效所跳转到的指令地址beq,bne
2无条件跳转j
3跳转到rs内的值jr

                jr指令:jr rs        含义:rs寄存器中存有下条指令的地址,pc改为rs内存的值。

                PC选择器模块代码(PCselect.v):

`timescale 1ns / 1ps

//本模块用来选择最后的pc
module PCselect(
    input [31:0]addedPC,    //加过4的PC
    input [31:0]ex_imm32,
    input [25:0]jAddr,
    input [31:0]jr_rs,
    input [1:0]sel,
    output reg[31:0]finalPC
);

    always@(*) begin
        case (sel) 
            0: finalPC <= addedPC;  //正常执行下一条指令
            1: finalPC <= addedPC+(ex_imm32<<2); //beq或者bne跳转指令生效
            2: finalPC <= {addedPC[31:28],jAddr[25:0],0,0}; //无条件跳转指令
            3: finalPC <= jr_rs;
        endcase
    end
    
endmodule

      8)控制单元(control)

                输入:指令的op和funct码,ALU的zero(判断rs、rt,0不相等,1相等)

                输出:(1)PC的reset         (2)PCsel的选择信号,选择下条指令地址                                                                                                                                                                                     (3)ALU要进行的操作ALUop         (4)reg的写信号RegWr;选择哪个寄存器reg_distSel;进入寄存器的是ALUresult还是从数据存储器来的信号:regIn_sel                                 (5)ALU的两个输入ALUin1_sel, ALUin2_sel:第一个选rs还是shamt , 第二个选rt还是imm32.                                                                                                                               (6)dataMem的写信号memWr.

                控制模块源代码(control.v):

`timescale 1ns / 1ps
 
module control(
    input [5:0] op,
    input [5:0] funct,
    input zero,
 //   input PC_reset,
    output reg[3:0]ALUop,
    output reg[1:0]PCsel,
    output reg reg_distSel,
    output reg regWr,
    output reg dataWr,
    output reg ALUin1_sel,  //选rs还是shamt,0是rs,1是shamt
    output reg ALUin2_sel,   //选rt还是imm,0是rt,1是imm
    output reg regIn_sel    //选ALUout还是dataMem_out,0是ALUout,1是dataMem_out
    );
    
    
    always @(*) begin
        case(op)
             //r型指令
            6'b000000:begin     
                case(funct)
                    //add
                    6'b100000:begin
     //                   PC_reset <= 0;
                        ALUop <= 4'b0000;
                        PCsel <= 2'b00;
                        reg_distSel <= 1;
                        regWr <= 1;
                        dataWr <= 0;
                        ALUin1_sel <= 0;
                        ALUin2_sel <= 0;
                        regIn_sel <= 0;
                    end 
                    //sub
                    6'b100010:begin
             //           PC_reset <= 0;
                        ALUop <= 4'b0001;
                        PCsel <= 2'b00;
                        reg_distSel <= 1;
                        regWr <= 1;
                        dataWr <= 0;
                        ALUin1_sel <= 0;
                        ALUin2_sel <= 0;
                        regIn_sel <= 0;
                    end
                    //and
                    6'b100100:begin
              //          PC_reset <= 0;
                        ALUop <= 4'b0010;
                        PCsel <= 2'b00;
                        reg_distSel <= 1;
                        regWr <= 1;
                        dataWr <= 0;
                        ALUin1_sel <= 0;
                        ALUin2_sel <= 0;
                        regIn_sel <= 0;
                    end
                    //or
                    6'b100101:begin
         //               PC_reset <= 0;
                        ALUop <= 4'b0011;
                        PCsel <= 2'b00;
                        reg_distSel <= 1;
                        regWr <= 1;
                        dataWr <= 0;
                        ALUin1_sel <= 0;
                        ALUin2_sel <= 0;
                        regIn_sel <= 0;
                    end
                    //xor
                    6'b100110:begin
            //            PC_reset <= 0;
                        ALUop <= 4'b0100;
                        PCsel <= 2'b00;
                        reg_distSel <= 1;
                        regWr <= 1;
                        dataWr <= 0;
                        ALUin1_sel <= 0;
                        ALUin2_sel <= 0;
                        regIn_sel <= 0;
                    end
                    //sll
                    6'b000000:begin
             //           PC_reset <= 0;
                        ALUop <= 4'b0110;
                        PCsel <= 2'b00;
                        reg_distSel <= 1;
                        regWr <= 1;
                        dataWr <= 0;
                        ALUin1_sel <= 1;
                        ALUin2_sel <= 0;
                        regIn_sel <= 0;
                    end
                    //srl
                    6'b000010:begin
          //              PC_reset <= 0;
                        ALUop <= 4'b0101;
                        PCsel <= 2'b00;
                        reg_distSel <= 1;
                        regWr <= 1;
                        dataWr <= 0;
                        ALUin1_sel <= 1;
                        ALUin2_sel <= 0;
                        regIn_sel <= 0;
                    end
                    //sra
                    6'b000011:begin
               //         PC_reset <= 0;
                        ALUop <= 4'b0111;
                        PCsel <= 2'b00;
                        reg_distSel <= 1;
                        regWr <= 1;
                        dataWr <= 0;
                        ALUin1_sel <= 1;
                        ALUin2_sel <= 0;
                        regIn_sel <= 0;
                    end
                    //jr
                    6'b001000:begin
                //        PC_reset <= 0;
                        ALUop <= 4'b0111;   //无所谓
                        PCsel <= 2'b11; //PC选择rs的内容
                        reg_distSel <= 0;
                        regWr <= 0;
                        dataWr <= 0;
                        ALUin1_sel <= 0;
                        ALUin2_sel <= 0;
                        regIn_sel <= 0;
                    end
                endcase
            end
            //下面是I型指令  
            //addi
            6'b001000:begin
     //           PC_reset <= 0;
                ALUop <= 4'b0000;
                PCsel <= 2'b00;
                reg_distSel <= 0;
                regWr <= 1;
                dataWr <= 0;
                ALUin1_sel <= 0;
                ALUin2_sel <= 1;
                regIn_sel <= 0;
            end
            //andi
            6'b001100:begin
     //           PC_reset <= 0;
                ALUop <= 4'b0001;
                PCsel <= 2'b00;
                reg_distSel <= 0;
                regWr <= 1;
                dataWr <= 0;
                ALUin1_sel <= 0;
                ALUin2_sel <= 1;
                regIn_sel <= 0;
            end
            //ori
            6'b001101:begin
          //      PC_reset <= 0;
                ALUop <= 4'b0011;
                PCsel <= 2'b00;
                reg_distSel <= 0;
                regWr <= 1;
                dataWr <= 0;
                ALUin1_sel <= 0;
                ALUin2_sel <= 1;
                regIn_sel <= 0;
            end
            //xori
            6'b001110:begin
       //         PC_reset <= 0;
                ALUop <= 4'b0100;
                PCsel <= 2'b00;
                reg_distSel <= 0;
                regWr <= 1;
                dataWr <= 0;
                ALUin1_sel <= 0;
                ALUin2_sel <= 1;
                regIn_sel <= 0;
            end
            //lw
            6'b100011:begin
       //         PC_reset <= 0;
                ALUop <= 4'b0000;
                PCsel <= 2'b00;
                reg_distSel <= 0;
                regWr <= 1;
                dataWr <= 0;
                ALUin1_sel <= 0;
                ALUin2_sel <= 1;
                regIn_sel <= 1;
            end
            //sw
            6'b101011:begin
       //         PC_reset <= 0;
                ALUop <= 4'b0000;
                PCsel <= 2'b00;
                reg_distSel <= 0;
                regWr <= 0;
                dataWr <= 1;
                ALUin1_sel <= 0;
                ALUin2_sel <= 1;
                regIn_sel <= 0;
            end
            //beq
            6'b000100:begin
         //       PC_reset <= 0;
                ALUop <= 4'b1000;
                reg_distSel <= 0;
                regWr <= 0;
                dataWr <= 0;
                ALUin1_sel <= 0;
                ALUin2_sel <= 1;
                regIn_sel <= 0;
                if(zero == 1) //1表示相等,要跳转
                    PCsel <= 2'b01;
                else 
                    PCsel <= 2'b00;
            end
            //bne
            6'b000101:begin
        //        PC_reset <= 0;
                ALUop <= 4'b1000;
                reg_distSel <= 0;
                regWr <= 0;
                dataWr <= 0;
                ALUin1_sel <= 0;
                ALUin2_sel <= 1;
                regIn_sel <= 0;
                if(zero == 0)
                    PCsel <= 2'b01;
                else 
                    PCsel <= 2'b00;
            end
            //lui,设置rt寄存器的高十六位,后面是16个0
            6'b001111: begin
     //           PC_reset <= 0;
                ALUop <= 4'b1001; 
                PCsel <= 2'b00;  
                reg_distSel <= 0;
                regWr <= 1;
                dataWr <= 0;
                ALUin1_sel <= 0;
                ALUin2_sel <= 1;
                regIn_sel <= 0;
            end
            //j
            6'b000010: begin
      //          PC_reset <= 0;
                ALUop <= 4'b1001;   //无所谓
                reg_distSel <= 0;   //无所谓
                PCsel <= 2'b10;
                regWr <= 0;
                dataWr <= 0;
                ALUin1_sel <= 0;    //无所谓
                ALUin2_sel <= 1;    //无所谓
                regIn_sel <= 0;     //无所谓
            end
        endcase
    end
    
endmodule

            这个模块不设置仿真,因为要需要调用很多其他模块。

        9)顶层仿真

                这个仿真文件是用来统筹所有其他的模块并且进行运行MIPS指令。这里还未写顶层模块

                下面直接贴上仿真代码(main_tb.v):

`timescale 1ns / 1ps

module main_tb( );
    reg clk;  
    wire pc_reset;  wire push;  wire [31:0]pc_out;  wire [31:0]pc_in;  wire[31:0] plused_pc;
    pc my_pc(pc_in,clk,pc_reset,pc_out,plused_pc);
    
    wire [5:0]op;  wire [4:0]rs; wire [4:0]rt;  wire [4:0]rd;  wire [4:0]shamt;  wire [5:0]funct;  wire [31:0]imm32;
    InsMem my_Insmem(pc_out,op,rs,rt,rd,shamt,funct);   extender my_extender({rd,shamt,funct},imm32);
    
    wire ctrl_distReg; wire [31:0]reg_dataIn;  wire RegWr;  wire [31:0]reg_out1;  wire [31:0]reg_out2;
    Register my_Register(clk, rs, rt, rd, ctrl_distReg, RegWr, reg_dataIn, reg_out1, reg_out2);
    
    wire [31:0]ALU_in1;  wire[31:0]ALU_in2;  wire ALUsel_in1;  wire ALUsel_in2;
    dataSel_2 ALU1(reg_out1,shamt,ALUsel_in1,ALU_in1);  dataSel_2 ALU2(reg_out2,imm32,ALUsel_in2,ALU_in2);
    
    wire [3:0]ALUctrl;  wire ALU_zero;  wire [31:0]ALU_out;
    ALU my_ALU(ALUctrl,ALU_in1,ALU_in2,ALU_zero,ALU_out);
    
    wire dataWr;  wire [31:0]dataOut;
    dataMem mt_dataMem(clk,ALU_out,reg_out2,dataWr,dataOut);
    wire regIn_sel_wire;
    dataSel_2 regIn_sel(ALU_out,dataOut,regIn_sel_wire,reg_dataIn);
    
    wire [1:0]pcSel;
    PCselect my_PCselect(plused_pc,imm32,{rs,rt,rd,shamt,funct},reg_out1,pcSel,pc_in);
    
    control my_control(op,funct,ALU_zero,pc_reset,ALUctrl,pcSel,ctrl_distReg,RegWr,dataWr,ALUsel_in1,ALUsel_in2,regIn_sel_wire);
    
   initial begin
        clk=0;

   end


    always @(*) begin
        #5 clk <= ~clk;
    end
       
endmodule

        在testdata.txt中写入几行指令来测试CPU是否能正常工作。

                经过测试后观察波形,这些指令都能够正常运行。

        测试过后,将顶层写成一个模块,然后将程序烧入CPU中。

        顶层模块代码(top.v):

`timescale 1ns / 1ps
//结合所有CPU器件的模块,但还没有通过数码管进行输出
module top(
    input push,
    input clk_7seg,
    input [1:0]SW,
    output [11:0]display,
 //   input clk,
    input pc_reset
//    output  effective_clk
    
    );
    wire [31:0]pc_out;
    wire [31:0]pc_in;
    wire [31:0]plused_pc;
    wire [5:0]op;
    wire  [4:0]rs;
    wire [4:0]rt;
    wire [4:0]rd;
    wire [4:0]shamt;
    wire [5:0]funct;
    wire [31:0]imm32;
    wire  ctrl_distReg;
    wire [31:0]reg_dataIn;
    wire RegWr;
    wire [31:0]reg_out1;
    wire [31:0]reg_out2;
    wire   [31:0]ALU_in1;
    wire  [31:0]ALU_in2;
    wire   ALUsel_in1;
    wire  ALUsel_in2;
    wire [3:0]ALUctrl;
    wire  ALU_zero;
    wire  [31:0]ALU_out;
    wire  dataWr;
    wire [31:0]dataOut;
    wire regIn_sel_wire;
    wire  [1:0]pcSel;
   
    wire effective_clk;
    wire [31:0]extended_shamt;
    
    clk_dura my_clkdura(clk_7seg,push,effective_clk);
    
    extend5 shamt_ex(shamt,extended_shamt);
    
    pc my_pc(pc_in,effective_clk,pc_reset,pc_out,plused_pc);
    
    InsMem my_Insmem(pc_out,op,rs,rt,rd,shamt,funct);   extender my_extender({rd,shamt,funct},imm32);
    Register my_Register(effective_clk, rs, rt, rd, ctrl_distReg, RegWr, reg_dataIn, reg_out1, reg_out2);
    
    dataSel_2 ALU1(reg_out1,extended_shamt,ALUsel_in1,ALU_in1);  dataSel_2 ALU2(reg_out2,imm32,ALUsel_in2,ALU_in2);
    ALU my_ALU(ALUctrl,ALU_in1,ALU_in2,ALU_zero,ALU_out);
    dataMem mt_dataMem(effective_clk,ALU_out,reg_out2,dataWr,dataOut);
    dataSel_2 regIn_sel(ALU_out,dataOut,regIn_sel_wire,reg_dataIn);
    PCselect my_PCselect(plused_pc,imm32,{rs,rt,rd,shamt,funct},reg_out1,pcSel,pc_in);
        
    control my_control(op,funct,ALU_zero,ALUctrl,pcSel,ctrl_distReg,RegWr,dataWr,ALUsel_in1,ALUsel_in2,regIn_sel_wire);

    _7seg my_7seg(clk_7seg,SW,pc_out,pc_in,rs,reg_out1,rt,reg_out2,ALU_out,reg_dataIn,display);
endmodule

                之前顶层模块把所有参数都写进了模块的参数列表(即input,output)中,但在约束文件中并未将他们接至引脚上,因此会造成输出悬空的现象,在进行硬件implementation会报错如下:

        顶层模块仿真(top_test_tb.v):

`timescale 1ns / 1ps

module top_test_tb();
    reg push;
    reg clk_7seg;
    reg [1:0]SW;
    reg pc_reset;
    
    wire [11:0]display;
//    wire effective_clk;

    
    top test_top(push,clk_7seg,SW,display,pc_reset);
                 
    initial begin
        push = 0;
        clk_7seg = 0;
        SW = 0;
        pc_reset = 0;
    end
    
    always @(clk_7seg) begin
        #5 clk_7seg <=  ~clk_7seg;
    end
    
    always @(push) begin
        #80 push <= ~push;
    end
    
    
endmodule

        指令文件(test.txt):

20010008    //add $1,$0,8
34020002    //ori $2,$0,2
00411820    //add $3,$2,$1
00622822    //sub $5,$3,$2
00a22024    //and $4,$5,$2
00824025    //or $8,$4,$2
00084040    //sll $8,$8,1
ac080004    //sw $8,4($0)
8c090004    //lw $9,4($0)

        0~2条指令波形(pc:00~08):

         3~5条指令波形(pc:0C~14):

         6~8条指令(pc:18~20):

 3.CPU烧板

        本实验还有在basys3板子上展示的要求,要求如下:

         七段数码管

                为了在数码管上显示,要写一个七段数码管显示的模块,通过当前的pc,rs,rt,ALU_out值来进行显示。

                该模块代码(_7seg.v):

`timescale 1ns / 1ps

module _7seg(
    input clk_7seg,
    input [1:0]SW,  //(sw15,sw14)
    input [31:0]pc_now, //当前pc值
    input [31:0]pc_next,    //下条pc值
    
    input [4:0]rs,  //rs寄存器的编号
    input [31:0]reg_out1,   //rs寄存器的内容
    
    input [4:0]rt,  //rt寄存器的编号
    input [31:0]reg_out2,   //rt寄存器的内容
    
    input [31:0]ALU_out,    //ALU结果
    input [31:0]dataOut,    //DB总线
    
    output reg[11:0] display
    
    );
    
    //count == T1MS 用来分频
    reg [19:0] count = 0;
    reg [2:0] sel = 0;
    parameter T1MS = 50000;
    always @(posedge clk_7seg) begin
        count <= count+1;
        if(count == T1MS) begin
            count <= 0;
            if(sel==3) sel<=0;
            else sel <= sel+1;
        end
    end
    
    always @(posedge clk_7seg) begin
        case(SW) 
            //pc
            0: begin
                case(sel)
                    0: begin
                        display[11:8] <= 4'b0111;
                        //第一位输出
                        case(pc_now[7:4])
                            4'b0000: display[7:0] <= 8'b1100_0000; 
                            4'b0001: display[7:0] <= 8'b1111_1001;
                            4'b0010: display[7:0] <= 8'b1010_0100;
                            4'b0011: display[7:0] <= 8'b1011_0000;
                            4'b0100: display[7:0] <= 8'b1001_1001;
                            4'b0101: display[7:0] <= 8'b1001_0010;
                            4'b0110: display[7:0] <= 8'b1000_0010;
                            4'b0111: display[7:0] <= 8'b1101_1000;
                            4'b1000: display[7:0] <= 8'b1000_0000;
                            4'b1001: display[7:0] <= 8'b1001_0000;
                            4'b1010: display[7:0] <= 8'b1000_1000;
                            4'b1011: display[7:0] <= 8'b1000_0011;
                            4'b1100: display[7:0] <= 8'b1100_0110;
                            4'b1101: display[7:0] <= 8'b1010_0001;
                            4'b1110: display[7:0] <= 8'b1000_0110;
                            4'b1111: display[7:0] <= 8'b1000_1110;
                            default : display[7:0] = 8'b1111_1111; //全灭
                         endcase
                    end
                    1: begin
                        display[11:8] <= 4'b1011;
                        //第二位输出
                        case(pc_now[3:0])
                            4'b0000: display[7:0] <= 8'b1100_0000; 
                            4'b0001: display[7:0] <= 8'b1111_1001;
                            4'b0010: display[7:0] <= 8'b1010_0100;
                            4'b0011: display[7:0] <= 8'b1011_0000;
                            4'b0100: display[7:0] <= 8'b1001_1001;
                            4'b0101: display[7:0] <= 8'b1001_0010;
                            4'b0110: display[7:0] <= 8'b1000_0010;
                            4'b0111: display[7:0] <= 8'b1101_1000;
                            4'b1000: display[7:0] <= 8'b1000_0000;
                            4'b1001: display[7:0] <= 8'b1001_0000;
                            4'b1010: display[7:0] <= 8'b1000_1000;
                            4'b1011: display[7:0] <= 8'b1000_0011;
                            4'b1100: display[7:0] <= 8'b1100_0110;
                            4'b1101: display[7:0] <= 8'b1010_0001;
                            4'b1110: display[7:0] <= 8'b1000_0110;
                            4'b1111: display[7:0] <= 8'b1000_1110;
                            default : display[7:0] = 8'b1111_1111; //全灭
                         endcase
                    end
                    2: begin
                        display[11:8] <= 4'b1101;
                        //第三位输出
                        case(pc_next[7:4])
                            4'b0000: display[7:0] <= 8'b1100_0000; 
                            4'b0001: display[7:0] <= 8'b1111_1001;
                            4'b0010: display[7:0] <= 8'b1010_0100;
                            4'b0011: display[7:0] <= 8'b1011_0000;
                            4'b0100: display[7:0] <= 8'b1001_1001;
                            4'b0101: display[7:0] <= 8'b1001_0010;
                            4'b0110: display[7:0] <= 8'b1000_0010;
                            4'b0111: display[7:0] <= 8'b1101_1000;
                            4'b1000: display[7:0] <= 8'b1000_0000;
                            4'b1001: display[7:0] <= 8'b1001_0000;
                            4'b1010: display[7:0] <= 8'b1000_1000;
                            4'b1011: display[7:0] <= 8'b1000_0011;
                            4'b1100: display[7:0] <= 8'b1100_0110;
                            4'b1101: display[7:0] <= 8'b1010_0001;
                            4'b1110: display[7:0] <= 8'b1000_0110;
                            4'b1111: display[7:0] <= 8'b1000_1110;
                            default : display[7:0] = 8'b1111_1111; //全灭
                         endcase
                    end
                    3: begin
                        display[11:8] <= 4'b1110;
                        //第四位输出
                        case(pc_next[3:0])
                            4'b0000: display[7:0] <= 8'b1100_0000; 
                            4'b0001: display[7:0] <= 8'b1111_1001;
                            4'b0010: display[7:0] <= 8'b1010_0100;
                            4'b0011: display[7:0] <= 8'b1011_0000;
                            4'b0100: display[7:0] <= 8'b1001_1001;
                            4'b0101: display[7:0] <= 8'b1001_0010;
                            4'b0110: display[7:0] <= 8'b1000_0010;
                            4'b0111: display[7:0] <= 8'b1101_1000;
                            4'b1000: display[7:0] <= 8'b1000_0000;
                            4'b1001: display[7:0] <= 8'b1001_0000;
                            4'b1010: display[7:0] <= 8'b1000_1000;
                            4'b1011: display[7:0] <= 8'b1000_0011;
                            4'b1100: display[7:0] <= 8'b1100_0110;
                            4'b1101: display[7:0] <= 8'b1010_0001;
                            4'b1110: display[7:0] <= 8'b1000_0110;
                            4'b1111: display[7:0] <= 8'b1000_1110;
                            default : display[7:0] = 8'b1111_1111; //全灭
                         endcase
                    end
                endcase
            end
            //rs
            1: begin
                  case(sel)
                      0: begin
                          display[11:8] <= 4'b0111;
                          //第一位输出
                          case(rs[4])
                              0: display[7:0] <= 8'b1100_0000; 
                              1: display[7:0] <= 8'b1111_1001;
                          
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                      1: begin
                          display[11:8] <= 4'b1011;
                          //第二位输出
                          case(rs[3:0])
                              4'b0000: display[7:0] <= 8'b1100_0000; 
                              4'b0001: display[7:0] <= 8'b1111_1001;
                              4'b0010: display[7:0] <= 8'b1010_0100;
                              4'b0011: display[7:0] <= 8'b1011_0000;
                              4'b0100: display[7:0] <= 8'b1001_1001;
                              4'b0101: display[7:0] <= 8'b1001_0010;
                              4'b0110: display[7:0] <= 8'b1000_0010;
                              4'b0111: display[7:0] <= 8'b1101_1000;
                              4'b1000: display[7:0] <= 8'b1000_0000;
                              4'b1001: display[7:0] <= 8'b1001_0000;
                              4'b1010: display[7:0] <= 8'b1000_1000;
                              4'b1011: display[7:0] <= 8'b1000_0011;
                              4'b1100: display[7:0] <= 8'b1100_0110;
                              4'b1101: display[7:0] <= 8'b1010_0001;
                              4'b1110: display[7:0] <= 8'b1000_0110;
                              4'b1111: display[7:0] <= 8'b1000_1110;
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                      2: begin
                          display[11:8] <= 4'b1101;
                          //第三位输出
                          case(reg_out1[7:4])
                              4'b0000: display[7:0] <= 8'b1100_0000; 
                              4'b0001: display[7:0] <= 8'b1111_1001;
                              4'b0010: display[7:0] <= 8'b1010_0100;
                              4'b0011: display[7:0] <= 8'b1011_0000;
                              4'b0100: display[7:0] <= 8'b1001_1001;
                              4'b0101: display[7:0] <= 8'b1001_0010;
                              4'b0110: display[7:0] <= 8'b1000_0010;
                              4'b0111: display[7:0] <= 8'b1101_1000;
                              4'b1000: display[7:0] <= 8'b1000_0000;
                              4'b1001: display[7:0] <= 8'b1001_0000;
                              4'b1010: display[7:0] <= 8'b1000_1000;
                              4'b1011: display[7:0] <= 8'b1000_0011;
                              4'b1100: display[7:0] <= 8'b1100_0110;
                              4'b1101: display[7:0] <= 8'b1010_0001;
                              4'b1110: display[7:0] <= 8'b1000_0110;
                              4'b1111: display[7:0] <= 8'b1000_1110;
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                      3: begin
                          display[11:8] <= 4'b1110;
                          //第四位输出
                          case(reg_out1[3:0])
                              4'b0000: display[7:0] <= 8'b1100_0000; 
                              4'b0001: display[7:0] <= 8'b1111_1001;
                              4'b0010: display[7:0] <= 8'b1010_0100;
                              4'b0011: display[7:0] <= 8'b1011_0000;
                              4'b0100: display[7:0] <= 8'b1001_1001;
                              4'b0101: display[7:0] <= 8'b1001_0010;
                              4'b0110: display[7:0] <= 8'b1000_0010;
                              4'b0111: display[7:0] <= 8'b1101_1000;
                              4'b1000: display[7:0] <= 8'b1000_0000;
                              4'b1001: display[7:0] <= 8'b1001_0000;
                              4'b1010: display[7:0] <= 8'b1000_1000;
                              4'b1011: display[7:0] <= 8'b1000_0011;
                              4'b1100: display[7:0] <= 8'b1100_0110;
                              4'b1101: display[7:0] <= 8'b1010_0001;
                              4'b1110: display[7:0] <= 8'b1000_0110;
                              4'b1111: display[7:0] <= 8'b1000_1110;
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                  endcase
            end
            //rt
            2: begin
                  case(sel)
                      0: begin
                          display[11:8] <= 4'b0111;
                          //第一位输出
                          case(rt[4])
                                0: display[7:0] <= 8'b1100_0000; 
                                1: display[7:0] <= 8'b1111_1001;
                            
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                      1: begin
                          display[11:8] <= 4'b1011;
                          //第二位输出
                          case(rt[3:0])
                              4'b0000: display[7:0] <= 8'b1100_0000; 
                              4'b0001: display[7:0] <= 8'b1111_1001;
                              4'b0010: display[7:0] <= 8'b1010_0100;
                              4'b0011: display[7:0] <= 8'b1011_0000;
                              4'b0100: display[7:0] <= 8'b1001_1001;
                              4'b0101: display[7:0] <= 8'b1001_0010;
                              4'b0110: display[7:0] <= 8'b1000_0010;
                              4'b0111: display[7:0] <= 8'b1101_1000;
                              4'b1000: display[7:0] <= 8'b1000_0000;
                              4'b1001: display[7:0] <= 8'b1001_0000;
                              4'b1010: display[7:0] <= 8'b1000_1000;
                              4'b1011: display[7:0] <= 8'b1000_0011;
                              4'b1100: display[7:0] <= 8'b1100_0110;
                              4'b1101: display[7:0] <= 8'b1010_0001;
                              4'b1110: display[7:0] <= 8'b1000_0110;
                              4'b1111: display[7:0] <= 8'b1000_1110;
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                      2: begin
                          display[11:8] <= 4'b1101;
                          //第三位输出
                          case(reg_out2[7:4])
                              4'b0000: display[7:0] <= 8'b1100_0000; 
                              4'b0001: display[7:0] <= 8'b1111_1001;
                              4'b0010: display[7:0] <= 8'b1010_0100;
                              4'b0011: display[7:0] <= 8'b1011_0000;
                              4'b0100: display[7:0] <= 8'b1001_1001;
                              4'b0101: display[7:0] <= 8'b1001_0010;
                              4'b0110: display[7:0] <= 8'b1000_0010;
                              4'b0111: display[7:0] <= 8'b1101_1000;
                              4'b1000: display[7:0] <= 8'b1000_0000;
                              4'b1001: display[7:0] <= 8'b1001_0000;
                              4'b1010: display[7:0] <= 8'b1000_1000;
                              4'b1011: display[7:0] <= 8'b1000_0011;
                              4'b1100: display[7:0] <= 8'b1100_0110;
                              4'b1101: display[7:0] <= 8'b1010_0001;
                              4'b1110: display[7:0] <= 8'b1000_0110;
                              4'b1111: display[7:0] <= 8'b1000_1110;
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                      3: begin
                          display[11:8] <= 4'b1110;
                          //第四位输出
                          case(reg_out2[3:0])
                              4'b0000: display[7:0] <= 8'b1100_0000; 
                              4'b0001: display[7:0] <= 8'b1111_1001;
                              4'b0010: display[7:0] <= 8'b1010_0100;
                              4'b0011: display[7:0] <= 8'b1011_0000;
                              4'b0100: display[7:0] <= 8'b1001_1001;
                              4'b0101: display[7:0] <= 8'b1001_0010;
                              4'b0110: display[7:0] <= 8'b1000_0010;
                              4'b0111: display[7:0] <= 8'b1101_1000;
                              4'b1000: display[7:0] <= 8'b1000_0000;
                              4'b1001: display[7:0] <= 8'b1001_0000;
                              4'b1010: display[7:0] <= 8'b1000_1000;
                              4'b1011: display[7:0] <= 8'b1000_0011;
                              4'b1100: display[7:0] <= 8'b1100_0110;
                              4'b1101: display[7:0] <= 8'b1010_0001;
                              4'b1110: display[7:0] <= 8'b1000_0110;
                              4'b1111: display[7:0] <= 8'b1000_1110;
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                  endcase
            end
            //ALUout:DB数据
            3: begin
                  case(sel)
                      0: begin
                          display[11:8] <= 4'b0111;
                          //第一位输出
                          case(ALU_out[7:4])
                              4'b0000: display[7:0] <= 8'b1100_0000; 
                              4'b0001: display[7:0] <= 8'b1111_1001;
                              4'b0010: display[7:0] <= 8'b1010_0100;
                              4'b0011: display[7:0] <= 8'b1011_0000;
                              4'b0100: display[7:0] <= 8'b1001_1001;
                              4'b0101: display[7:0] <= 8'b1001_0010;
                              4'b0110: display[7:0] <= 8'b1000_0010;
                              4'b0111: display[7:0] <= 8'b1101_1000;
                              4'b1000: display[7:0] <= 8'b1000_0000;
                              4'b1001: display[7:0] <= 8'b1001_0000;
                              4'b1010: display[7:0] <= 8'b1000_1000;
                              4'b1011: display[7:0] <= 8'b1000_0011;
                              4'b1100: display[7:0] <= 8'b1100_0110;
                              4'b1101: display[7:0] <= 8'b1010_0001;
                              4'b1110: display[7:0] <= 8'b1000_0110;
                              4'b1111: display[7:0] <= 8'b1000_1110;
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                      1: begin
                          display[11:8] <= 4'b1011;
                          //第二位输出
                          case(ALU_out[3:0])
                              4'b0000: display[7:0] <= 8'b1100_0000; 
                              4'b0001: display[7:0] <= 8'b1111_1001;
                              4'b0010: display[7:0] <= 8'b1010_0100;
                              4'b0011: display[7:0] <= 8'b1011_0000;
                              4'b0100: display[7:0] <= 8'b1001_1001;
                              4'b0101: display[7:0] <= 8'b1001_0010;
                              4'b0110: display[7:0] <= 8'b1000_0010;
                              4'b0111: display[7:0] <= 8'b1101_1000;
                              4'b1000: display[7:0] <= 8'b1000_0000;
                              4'b1001: display[7:0] <= 8'b1001_0000;
                              4'b1010: display[7:0] <= 8'b1000_1000;
                              4'b1011: display[7:0] <= 8'b1000_0011;
                              4'b1100: display[7:0] <= 8'b1100_0110;
                              4'b1101: display[7:0] <= 8'b1010_0001;
                              4'b1110: display[7:0] <= 8'b1000_0110;
                              4'b1111: display[7:0] <= 8'b1000_1110;
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                      2: begin
                          display[11:8] <= 4'b1101;
                          //第三位输出
                          case(dataOut[7:4])
                              4'b0000: display[7:0] <= 8'b1100_0000; 
                              4'b0001: display[7:0] <= 8'b1111_1001;
                              4'b0010: display[7:0] <= 8'b1010_0100;
                              4'b0011: display[7:0] <= 8'b1011_0000;
                              4'b0100: display[7:0] <= 8'b1001_1001;
                              4'b0101: display[7:0] <= 8'b1001_0010;
                              4'b0110: display[7:0] <= 8'b1000_0010;
                              4'b0111: display[7:0] <= 8'b1101_1000;
                              4'b1000: display[7:0] <= 8'b1000_0000;
                              4'b1001: display[7:0] <= 8'b1001_0000;
                              4'b1010: display[7:0] <= 8'b1000_1000;
                              4'b1011: display[7:0] <= 8'b1000_0011;
                              4'b1100: display[7:0] <= 8'b1100_0110;
                              4'b1101: display[7:0] <= 8'b1010_0001;
                              4'b1110: display[7:0] <= 8'b1000_0110;
                              4'b1111: display[7:0] <= 8'b1000_1110;
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                      3: begin
                          display[11:8] <= 4'b1110;
                          //第四位输出
                          case(dataOut[3:0])
                              4'b0000: display[7:0] <= 8'b1100_0000; 
                              4'b0001: display[7:0] <= 8'b1111_1001;
                              4'b0010: display[7:0] <= 8'b1010_0100;
                              4'b0011: display[7:0] <= 8'b1011_0000;
                              4'b0100: display[7:0] <= 8'b1001_1001;
                              4'b0101: display[7:0] <= 8'b1001_0010;
                              4'b0110: display[7:0] <= 8'b1000_0010;
                              4'b0111: display[7:0] <= 8'b1101_1000;
                              4'b1000: display[7:0] <= 8'b1000_0000;
                              4'b1001: display[7:0] <= 8'b1001_0000;
                              4'b1010: display[7:0] <= 8'b1000_1000;
                              4'b1011: display[7:0] <= 8'b1000_0011;
                              4'b1100: display[7:0] <= 8'b1100_0110;
                              4'b1101: display[7:0] <= 8'b1010_0001;
                              4'b1110: display[7:0] <= 8'b1000_0110;
                              4'b1111: display[7:0] <= 8'b1000_1110;
                              default : display[7:0] = 8'b1111_1111; //全灭
                           endcase
                      end
                  endcase
            end
        endcase
    end
    
endmodule

            这里的display[0]~diplay[6]分别是数码管的A~G。

        按键消抖

                消抖原理:FPGA板子上的按键在按下时,由于弹性形变,因此会导致信号不稳定,在极短的时间内会产生多次高电平,因此要在信号保持稳定后再进行读取,这样              能避免按下一次按键后执行多条指令。

                脉冲信号为push,写一个模块消抖(clk_dura.v):

`timescale 1ns / 1ps

module clk_dura(
    input clk,
    input push,
    output reg effective_clk
    );
    
    reg [31:0] count;
    integer i;
    initial begin
     for(i=0;i<32;i=i+1) count[i] = 0; 
    end
    
    always @(posedge clk) begin
        if(push && count<=32'd30000000) count = count + 1;
        else if(!push) count = 0; 
    end
    
    always @(count) begin
        if(count == 32'd10000000) effective_clk = 1'b1;
        else effective_clk = 0;
    end
endmodule

        虽然加了延迟消除抖动,但是在上板子的时候会发现仍会出现按一下跳两条指令的情况,(猜测是板子问题)因此加入了简易的松手检测,如下图:

   

        强行抹除了长按可能造成跳多条指令的影响(计数器到了30ms后不会再增加)。

        修改过后按键运行正常。

  需要全项目源代码的,链接已经贴在下面,需要自取。

  项目源代码:https://download.csdn.net/download/wuyzh39/87244971

  • 31
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 32位MIPS单周期CPU的具体程序包括以下几个模块:指令存储器(Instruction Memory, IMEM)、数据存储器(Data Memory, DMEM)、寄存器堆(Register File)、控制器(Controller)、算术逻辑单元(Arithmetic Logic Unit, ALU)和数据通路(Data Path)。 首先,指令存储器(IMEM)存储指令数据,并根据指令地址提供指令。数据存储器(DMEM)用于存储操作数和结果。寄存器堆实现了多个通用寄存器,并能读取和写入寄存器数据。ALU用于执行算术运算和逻辑运算。数据通路负责将各个模块相连,并完成数据的传输。 控制器根据当前指令的操作码和其他信号,生成控制信号来控制各个模块的操作。根据操作码和控制信号,数据通路执行对应的操作。包括指令读取、寄存器读取、算术逻辑运算、数据存储等操作。 具体程序的编写如下: ```verilog module MIPS_CPU ( input wire clk, // 时钟信号 input wire reset, // 复位信号 input wire[31:0] imem_data_in, // IMEM写入数据 input wire[31:0] dmem_data_in, // DMEM写入数据 input wire imem_data_write, // 控制IMEM写使能 input wire dmem_data_write, // 控制DMEM写使能 output wire[31:0] imem_data_out, // IMEM读取数据 output wire[31:0] dmem_data_out // DMEM读取数据 ); // 此处为具体实现代码,包括各个模块的实例化和信号连接 // 以及控制信号和操作的逻辑 endmodule ``` 需要注意的是,以上代码为顶层模块的框架,不包含具体的控制信号和操作逻辑。具体的控制信号和操作逻辑需要根据指令集架构(ISA)和需求来设计和实现。 在具体的控制信号和操作逻辑编写中,需要考虑到指令的解码、数据的传输、状态的转换等。对于每个指令,需要确定其对应的操作和控制信号,以便数据通路能够正确执行。此外,还需要考虑流水线冒险和异常处理等问题。 因此,为了完整实现32位MIPS单周期CPU的具体程序,还需要考虑更多细节和细化设计。上述代码只是一个框架,具体实现需要根据需求进行调整和补充。 ### 回答2: 32位MIPS单周期CPUVerilog是一种用于设计计算机中央处理单元的硬件描述语言。具体程序如下: ```verilog module MIPS_CPU ( input wire clk, input wire reset, input wire [31:0] in_data, output wire [31:0] out_data ); reg [31:0] pc; // 程序计数器 reg [31:0] instruction; // 存储指令寄存器 reg [31:0] reg[31:0]; // 32个通用寄存器 reg [4:0] op; // 指令操作码 reg [4:0] rs; // 寄存器rs reg [4:0] rt; // 寄存器rt reg [4:0] rd; // 寄存器rd reg [15:0] imm; // 立即数 reg [31:0] ALU_out; // ALU计算结果 reg Mem_read, Mem_write; // 读写内存信号 reg RegWrite; // 寄存器写信号 reg Zero; // 零标志位 // 常量定义 localparam ADD = 5'b00000; localparam SUB = 5'b00001; // ... // 指令执行模块 always @(posedge clk) begin if (reset) begin pc <= 0; end else begin // 取指令 instruction <= mem[pc]; // 解析指令 op <= instruction[31:26]; rs <= instruction[25:21]; rt <= instruction[20:16]; rd <= instruction[15:11]; imm <= instruction[15:0]; // 执行指令 case (op) ADD: ALU_out <= reg[rs] + reg[rt]; SUB: ALU_out <= reg[rs] - reg[rt]; // ... endcase // 存储结果 if (Mem_write) mem[ALU_out] <= reg[rt]; if (Mem_read) out_data <= mem[ALU_out]; if (RegWrite && (rd != 0)) reg[rd] <= ALU_out; // 更新PC pc <= pc + 1; end end endmodule ``` 此程序中定义了一个带有时钟clk、复位reset、输入数据in_data和输出数据out_data的模块MIPS_CPU。模块内部包含了寄存器、ALU和存储器等元素,并按时钟信号进行指令的解析、执行和存储结果等操作。通过组合逻辑控制和状态寄存器的更新,实现了32位MIPS单周期CPU的功能。 ### 回答3: 32位MIPS单周期CPUVerilog编程,需要包括以下关键模块:指令存储器、寄存器堆、ALU及控制单元。 指令存储器(Instruction Memory)模块:该模块负责存储指令,并根据指令地址输出相应指令。 寄存器堆(Register File)模块:该模块包括32个32位的寄存器,可以进行读写操作。根据输入的寄存器地址,输出相应的寄存器值。 算术逻辑单元(ALU)模块:该模块负责执行算术和逻辑运算,如加法、减法、AND、OR等。 控制单元(Control Unit)模块:该模块根据指令的操作码,生成相应的控制信号,控制指令的执行过程。 在主模块中,可以按照指令执行的顺序连接各个模块。主要包括如下几个环节: 1. 指令提取:从指令存储器中读取指令,并将指令的操作码和操作数传递给控制单元。 2. 寄存器读取:根据操作数中的寄存器地址,从寄存器堆中读取相应的寄存器值。 3. ALU运算:根据控制单元产生的运算控制信号,将操作数和寄存器值送入ALU进行运算,得到运算结果。 4. 写回寄存器:将ALU运算的结果写回寄存器堆。 5. 数据存储器读写:根据指令进行读取或写入数据存储器。 6. PC更新:根据控制单元产生的控制信号,更新程序计数器(PC)指向下一条指令的地址。 以上步骤可以根据具体指令的要求进行循环执行,实现指令的顺序执行。在整个过程中,通过控制单元产生的控制信号决定了每个模块的工作状态。 此为MIPS单周期CPU的基本结构,当然还可以根据具体需求进行功能扩展,如加入中断处理、异常处理等模块,以提升其功能和性能表现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

相当乏善

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值