基于verilog的MIPS32单周期CPU设计与实现

1. 实验内容

  1. 本实例所设计CPU的指令格式的拟定;

  2. 基本功能部件的设计与实现;

  3. CPU各主要功能部件的设计与实现;

  4. CPU的封装;

  5. 仿真测试及板级测试。对各个单元组合而成的CPU进行指令测试,配合使用模拟仿真,了解指令和数据在各个单元中的传输过程及方向。

2. 实验要求

  1. 设计的CPU能够执行20条整数指令,每条指令的编码长度均为32位;

  2. 指令类型应有:计算类型、访问存储器类型、条件转移类型和无条件转移类型;

  3. 操作数应有:寄存器操作数、立即数;

  4. 测试进程应不少于10条,将设计成果仿真测试并起先板级验证。

3. 实验原理

3.1 概述

单周期(Single Cycle)CPU是指CPU从取出1条指令到执行完该指令只需1个时钟周期。
单周期在这里插入图片描述
一条指令的执行过程包括:取指令→分析指令→执行指令→保存结果(如果有的话)。对于单周期CPU来说,这些执行步骤均在一个时钟周期内完成。

3.2 单周期CPU的总体电路

本实验所设计的单周期CPU的总体电路结构如图所示:在这里插入图片描述本实验所设计的CPU主要组成部分有:运算器(ALU)、控制器(CONUNIT)、寄存器堆(REGFILE)、取指电路及相关基础部件(如选择器)等构成。

3.3 MIPS指令格式

MIPS指令系统结构有MIPS-32和MIPS-64两种。本实验的MIPS指令选用MIPS-32。以下所说的MIPS指令均指MIPS-32。

MIPS的指令格式为32位。下图给出了MIPS指令的3种格式。
在这里插入图片描述
本实验只选取了20条典型的MIPS指令来描述CPU逻辑电路的设计方法。下表列出了本实验的所涉及到的20条MIPS指令。

在这里插入图片描述
R型指令的Op均为0,具体操作由func指定。rs和rt是源寄存器号,rd是目的寄存器号。移位指令中使用sa指定移位位数。

I型指令的低16位是立即数,计算时需扩展到32位,依指令的不同需进行零扩展和符号扩展。

J型指令的低26位是地址,是用于产生跳转的目标地址。

3.4 模块分析

根据实验原理中的单周期CPU总体结构图,我们可以清楚的知道单周期CPU的设计应包括PC,INSTMEM,CONUNIT,REGFILE,ALU,DATAMEM, EXT16T32这几个核心模块,其中ALU模块需要用到32位加法器CLA_32。此外还需要一个固定左移两位的移位器SHIFT,左移处理模块SHIFTER_COMBINATION,一个PC+4的固定加法器,一个四选一多路选择器MUX4X32,两个32位二选一多路选择器MUX2X32,一个5位二选一多路选择器MUX2X5,一个数据扩展器EXT16T32。其中为了运行整个CPU还需要加入一个顶层模块CPU来调用这些模块,所以自然地,这些模块为顶层模块的子模块。

3.5 下一条指令地址的选择

CPU的电路包括数据路径(Data
path)和控制部件(CONUNIT)两大部分。下面介绍路径的设计。

下一条指令的地址有4种情况:

  1. 程序不转移时下一条指令的地址为PC+4;

  2. 执行beq和bne指令发生转移时,下一条指令的地址是PC加4,再加上符号扩展的偏移量左移2位的和;

  3. 执行jr指令时转移的目标地址就是rs寄存器中的内容;

  4. 执行j和jal指令时转移的目标地址是指令中的低26位地址左移2位,再与PC+4的高4位拼接在一起。

下一条指令地址的产生和选择电路如图所示:
在这里插入图片描述
在图中,控制器(CONUNIT)根据op、func和Z(对于beq和bne指令)信号产生相应的转移控制选择信号Pcsrc。

4. 基本功能部件的设计与实现

一个CPU主要由ALU(运算器)、控制器、寄存器堆、取指部件及其它基本功能部件等构成。

在本实验中基本功能部件主要有:32位2选1多路选择器、32位4选1多路选择器、5位2选1多路选择器、D触发器、移位器及32位加/减法器等。

4.1 32位2选1选择器的设计与实现

多路选择器是逻辑电路设计中最重要的基本逻辑电路之一,也是基于数据选择通路CPU的重要部件。32位2选1选择器的逻辑框图如图所示。

在这里插入图片描述
输入模块代码:

module MUX2X32(A0,A1,S,Y);
    input [31:0]A0,A1;
    input S;
    output [31:0]Y;
    function [31:0]select;
        input [31:0]A0,A1;
        input S;
        case(S)
            1'b0:select=A0;
            1'b1:select=A1;
        endcase
    endfunction
    assign Y = select(A0,A1,S);
endmodule

通过RTL Schematic查看综合后的RTL层电路,如图所示:在这里插入图片描述

4.2 32位4选1选择器的设计与实现

32位4选1选择器的逻辑框图如图所示:
在这里插入图片描述
32位4选1选择器的模块设计步骤可参考32位2选1选择器的设计步骤,下面给出32位4选1选择器的模块代码。

module MUX4X32(A0, A1, A2, A3, S, Y);
    input [31:0] A0, A1, A2, A3;
    input [1:0] S;
    output [31:0] Y;
    function [31:0] select;
        input [31:0] A0, A1, A2, A3;
        input [1:0] S;
            case(S)
                2'b00: select = A0;
                2'b01: select = A1;
                2'b10: select = A2;
                2'b11: select = A3;
            endcase
    endfunction
    assign Y = select (A0, A1, A2, A3, S);
endmodule

4选1选择器的PTL仿真图为:
在这里插入图片描述

4.3 5位2选1选择器的设计与实现

5位2选1选择器的逻辑框图如图4-19所示。
在这里插入图片描述
5位2选1选择器的模块设计步骤可参考32位2选1选择器的设计步骤,下面给出5位2选1选择器的模块代码。

module MUX2X5(A0,A1,S,Y);
    input [4:0]A0,A1;
    input S;
    output [4:0] Y;
    function [4:0] select;
        input [4:0] A0,A1;
        input S;
            case(S)
                1'b0:select=A0;
                1'b1:select=A1;
        endcase
    endfunction
    assign Y=select(A0,A1,S);
endmodule

RTL仿真图为:
在这里插入图片描述

4.4 16位-32位扩展器的设计与实现

采用一个16位扩展成32位的扩展模块EXT16T32,实现零扩展和符号扩展。所以还需要一个32位2选1选择器。

module EXT16T32(X,Se,Y);
    input [15:0]X;
    input Se;
    output [31:0]Y;
    wire [31:0]E0,E1;
    wire [15:0]e={16{X[15]}};
    parameter z=16'b0;
    assign E0={z,X};
    assign E1={e,X};
    MUX2X32 i(E0,E1,Se,Y);
endmodule

RTL仿真图为:
在这里插入图片描述

4.5 32位移位器的设计与实现

移位器的功能是将一个数进行逻辑左移、逻辑右移或算术右移。其设计步骤可参考32位2选1选择器的设计步骤,下面给出其模块Verilog实现代码:

module SHIFTER(X,Sa,Arith,Right,Sh);
    input [31:0]X;
    input [4:0]Sa;
    input Arith,Right;
    output [31:0]Sh;
    wire [31:0]T4,T3,T2,T1,T0,S4,S3,S2,S1;
    wire a=X[31]&Arith;
    wire [15:0]e={16{a}};
    parameter z=16'b0000000000000000;
    wire [31:0]L1u,L1d,L2u,L2d,L3u,L3d,L4u,L4d,L5u,L5d;
    assign L1u={X[15:0],z[15:0]};
    assign L1d={e,X[31:16]};
    MUX2X32 M1l(L1u,L1d,Right,T4);
    MUX2X32 M1r(X,T4,Sa[4],S4);
    //1
    assign L2u={S4[23:0],z[7:0]};
    assign L2d={e[7:0],S4[31:8]};
    MUX2X32 M2l(L2u,L2d,Right,T3);
    MUX2X32 M2r(S4,T3,Sa[3],S3);
    //2
    assign L3u={S3[27:0],z[3:0]};
    assign L3d={e[3:0],S3[31:4]};
    MUX2X32 M3l(L3u,L3d,Right,T2);
    MUX2X32 M3r(S3,T2,Sa[2],S2);
    //3
    assign L4u={S2[29:0],z[1:0]};
    assign L4d={e[1:0],S2[31:2]};
    MUX2X32 M4l(L4u,L4d,Right,T1);
    MUX2X32 M4r(S2,T1,Sa[1],S1);
    //4
    assign L5u={S1[30:0],z[0]};
    assign L5d={e[0],S1[31:1]};
    MUX2X32 M5l(L5u,L5d,Right,T0);
    MUX2X32 M5r(S1,T0,Sa[0],Sh);
endmodule

RTL仿真图为:

在这里插入图片描述

4.6 26位-32位移位器的设计与实现

跳转的目标地址采用拼接的方式形成,最高4位为PC+4的最高4位,中间26位为J型指令的26位立即数字段,最低两位为0。Verilog代码实现为:

module SHIFTER_COMBINATION(X,PCADD4,Sh);
    input [25:0] X;
    input [31:0] PCADD4;
    output [31:0] Sh;
    parameter z=2'b00;
    assign Sh={PCADD4[31:28],X[25:0],z};
endmodule

RTL仿真图为:在这里插入图片描述

4.7 32位加/减法器的设计与实现

32位加/减法器的功能是完成32位的加法/减法运算。由于:
在这里插入图片描述
故可以用加法器实现减法操作。下面给出其模块Verilog实现代码:

module ADDSUB_32(X,Y,Sub,S);
    input [31:0]X,Y;
    wire Cout;
    input Sub;
    output [31:0]S;
    CLA_32 adder0(X,Y^{32{Sub}},Sub,S,Cout);
endmodule

RTL仿真图为:
在这里插入图片描述
下面给出其模块CLA_32 Verilog实现代码:

module CLA_32(X,Y,Cin,S,Cout);
    input[31:0]X,Y;
    input Cin;
    output[31:0]S;
    output Cout;
    wire Cout0,Cout1,Cout2,Cout3,Cout4,Cout5,Cout6;
    CLA_4 add0(X[3:0],Y[3:0],Cin,S[3:0],Cout0);
    CLA_4 add1(X[7:4],Y[7:4],Cout0,S[7:4],Cout1);
    CLA_4 add2(X[11:8],Y[11:8],Cout1,S[11:8],Cout2);
    CLA_4 add3(X[15:12],Y[15:12],Cout2,S[15:12],Cout3);
    CLA_4 add4(X[19:16],Y[19:16],Cout3,S[19:16],Cout4);
    CLA_4 add5(X[23:20],Y[23:20],Cout4,S[23:20],Cout5);
    CLA_4 add6(X[27:24],Y[27:24],Cout5,S[27:24],Cout6);
    CLA_4 add7(X[31:28],Y[31:28],Cout6,S[31:28],Cout);
endmodule

CLA_4 Verilog实现代码:

module CLA_4(X,Y,Cin,S,Cout);
    input [3:0]X,Y;
    output Cout;
    input Cin;
    output [3:0]S;
    and i0(Y_3,X[3],Y[3]);
    or i1(X_3,X[3],Y[3]);
    and i2(Y_2,X[2],Y[2]);
    or i3(X_2,X[2],Y[2]);
    and i4(Y_1,X[1],Y[1]);
    or i5(X_1,X[1],Y[1]);
    and i6(Y_0,X[0],Y[0]);
    or i7(X_0,X[0],Y[0]);
    not i01(Y_31,Y_3);
    nand i02(Y_32,X_3,Y_2);
    nand i03(Y_33,X_3,X_2,Y_1);
    nand i04(Y_34,X_3,X_2,X_1,Y_0);
    nand i05(Y_35,X_3,X_2,X_1,X_0,Cin);
    nand i00(Cout,Y_31,Y_32,Y_33,Y_34,Y_35);//Cout的输出门级电路实现
    not i_2(Y__3,Y_3);
    and i21(Y_21,Y__3,X_3);
    not i22(Y_22,Y_2);
    nand i23(Y_23,X_2,Y_1);
    nand i24(Y_24,X_2,X_1,Y_0);
    nand i25(Y_25,X_2,X_1,X_0,Cin);
    nand i26(Y_26,Y_22,Y_23,Y_24,Y_25);
    xor i20(S[3],Y_21,Y_26);//S3的输出门级电路实现
    not i_1(Y__2,Y_2);
    and i11(Y_11,Y__2,X_2);
    not i12(Y_12,Y_1);
    nand i13(Y_13,X_1,Y_0);
    nand i14(Y_14,X_1,X_0,Cin);
    nand i15(Y_15,Y_12,Y_13,Y_14);
    xor i10(S[2],Y_11,Y_15);//S2的输出门级电路实现
    not i_0(Y__1,Y_1);
    and i51(Y_51,Y__1,X_1);
    not i52(Y_52,Y_0);
    nand i53(Y_53,X_0,Cin);
    nand i54(Y_54,Y_52,Y_53);
    xor i50(S[1],Y_51,Y_54);//S1的输出门级电路
    not i41(Y__0,Y_0);
    and i42(Y_4,Y__0,X_0);
    xor i40(S[0],Y_4,Cin);//S0的输出门级电路
endmodule

5. 运算器(ALU)的设计与实现

运算器ALU是CPU组成的核心部件之一,其实现方法主要有2种:一种是以加法器为核心,通过改变进位函数实现;另一种是运算部件并行多路选择实现。本实验采用运算部件并行多路选择实现。综合表3-1的指令,ALU只需完成9种运算即可,具体完成的功能如表所示。
在这里插入图片描述

ALU的逻辑框图如图所示。在图中各信号的功能如下:
在这里插入图片描述
X:操作数,32位,输入;
Y:操作数,32位,输入;
Aluc:4位操作码,输入;
R:运算结果,32位,输出;
Z:零标志,1位;当运算结果为0时,该位为1,否则为0;

ALU模块的Verilog程序参考代码:

module ALU(X,Y,Aluc,R,Z);
    input[31:0]X,Y;
    input[3:0]Aluc;
    output[31:0]R;
    output Z;
    wire[31:0]d_as,d_and,d_or,d_xor,d_lui,d_sh,d;
    ADDSUB_32 as32(X,Y,Aluc[0],d_as);
    assign d_and=X&Y;
    assign d_or=X|Y;
    assign d_xor=X^Y;
    assign d_lui={Y[15:0],16'h0};
    SHIFTER shift(Y,X[10:6],Aluc[3],Aluc[1],d_sh);
    MUX6X32 select(d_and,d_or,d_xor,d_lui,d_sh,d_as,Aluc[3:0],R);
    assign Z=~|R;
endmodule

32位6选1选择器的代码如下:

module MUX6X32(d_and,d_or,d_xor,d_lui,d_sh,d_as,Aluc,d);
    input [31:0]d_and,d_or,d_xor,d_lui,d_sh,d_as;
    input [3:0]Aluc;
    output [31:0]d;
    function [31:0]select;
        input [31:0]d_and,d_or,d_xor,d_lui,d_sh,d_as;
        input [3:0]Aluc;
            case(Aluc)
                4'b0000:select=d_as;
                4'b0001:select=d_as;
                4'b0010:select=d_and;
                4'b0011:select=d_or;
                4'b0100:select=d_xor;
                4'b0110:select=d_lui;
                4'b0101:select=d_sh;
                4'b0111:select=d_sh;
                4'b1111:select=d_sh;
                4'b1101:select=d_sh;
            endcase
    endfunction
    assign d=select(d_and,d_or,d_xor,d_lui,d_sh,d_as,Aluc);
endmodule

RTL仿真图为:如图所示,在ALU的实现中,运用了32位加/减法器,32位移位器,32位6选1选择器,可以实现所有的功能。
在这里插入图片描述

6. 寄存器堆(REGFILE)的设计与实现

寄存器堆(REGFILE)是CPU组成的重要存储部件,也是数据通路中的重要部件,其主要功能是对数据进行存储。在本实验中将为REGFILE构建32×32的寄存器组,即共有32个寄存器,每个寄存器的位宽都是32位。32×32的REGFILE逻辑结构如图所示:
在这里插入图片描述
图中的寄存器组有1个数据输入端口,2个数据输出端口,因此可以从该寄存器组中同时输出2个数据。由于没有设置读信号,故在本实验中当Ra(或Rb)为0时,并不是选择输出Register的内容,而是直接输出0。图6-1中各信号引脚的功能:

Ra:寄存器rs输出端口地址,5位,输入;
Rb:寄存器rt输出端口地址,5位,输入;
Wr:数据写入的寄存器号,5位,输入;
We:写信号,1位,输入;
D:数据,32位,输入;
Qa:A端口的输出数据,32位,输出;
Qb:B端口的输出数据,32位,输出;
Clk:时钟信号,输入。

实现REGFILE的Verilog程序参考代码:

module REGFILE(Ra,Rb,D,Wr,We,Clk,Clrn,Qa,Qb);
    input [4:0]Ra,Rb,Wr;
    input [31:0]D;
    input We,Clk,Clrn;
    output [31:0]Qa,Qb;
    wire [31:0]Y;
    wire [31:0]Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0;
    DEC5T32E dec(Wr,We,Y);
    REG32 reg32(D,Y,Clk,Clrn,Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0);
    MUX32X32 select1(Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0,Ra,Qa);
    MUX32X32 select2(Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0,Rb,Qb);
endmodule

5-32译码器的代码实现为:

module DEC5T32E(I,En,Y);
    input [4:0] I;
    input En;
    output [31:0] Y;
    reg [31:0] Y;
    always@(En or I)
            begin
                if(En)
                    begin
                        case(I)
                            5'b00000:Y=32'b00000000000000000000000000000001;
                            5'b00001:Y=32'b00000000000000000000000000000010;
                            5'b00010:Y=32'b00000000000000000000000000000100;
                            5'b00011:Y=32'b00000000000000000000000000001000;
                            5'b00100:Y=32'b00000000000000000000000000010000;
                            5'b00101:Y=32'b00000000000000000000000000100000;
                            5'b00110:Y=32'b00000000000000000000000001000000;
                            5'b00111:Y=32'b00000000000000000000000010000000;
                            5'b01000:Y=32'b00000000000000000000000100000000;
                            5'b01001:Y=32'b00000000000000000000001000000000;
                            5'b01010:Y=32'b00000000000000000000010000000000;
                            5'b01011:Y=32'b00000000000000000000100000000000;
                            5'b01100:Y=32'b00000000000000000001000000000000;
                            5'b01101:Y=32'b00000000000000000010000000000000;
                            5'b01110:Y=32'b00000000000000000100000000000000;
                            5'b01111:Y=32'b00000000000000001000000000000000;
                            5'b10000:Y=32'b00000000000000010000000000000000;
                            5'b10001:Y=32'b00000000000000100000000000000000;
                            5'b10010:Y=32'b00000000000001000000000000000000;
                            5'b10011:Y=32'b00000000000010000000000000000000;
                            5'b10100:Y=32'b00000000000100000000000000000000;
                            5'b10101:Y=32'b00000000001000000000000000000000;
                            5'b10110:Y=32'b00000000010000000000000000000000;
                            5'b10111:Y=32'b00000000100000000000000000000000;
                            5'b11000:Y=32'b00000001000000000000000000000000;
                            5'b11001:Y=32'b00000010000000000000000000000000;
                            5'b11010:Y=32'b00000100000000000000000000000000;
                            5'b11011:Y=32'b00001000000000000000000000000000;
                            5'b11100:Y=32'b00010000000000000000000000000000;
                            5'b11101:Y=32'b00100000000000000000000000000000;
                            5'b11110:Y=32'b01000000000000000000000000000000;
                            5'b11111:Y=32'b10000000000000000000000000000000;
                        endcase
                    end
                else
                    Y=32'b00000000000000000000000000000000;
            end
endmodule

32位32选1选择器代码实现为:

module MUX32X32(Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0,S,Y);
    input [31:0]Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0;
    input [4:0]S;
    output [31:0]Y;
    function [31:0]select;
        input [31:0]Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0;
        input [4:0]S;
            case(S)
                5'b00000:select=Q0;
                5'b00001:select=Q1;
                5'b00010:select=Q2;
                5'b00011:select=Q3;
                5'b00100:select=Q4;
                5'b00101:select=Q5;
                5'b00110:select=Q6;
                5'b00111:select=Q7;
                5'b01000:select=Q8;
                5'b01001:select=Q9;
                5'b01010:select=Q10;
                5'b01011:select=Q11;
                5'b01100:select=Q12;
                5'b01101:select=Q13;
                5'b01110:select=Q14;
                5'b01111:select=Q15;
                5'b10000:select=Q16;
                5'b10001:select=Q17;
                5'b10010:select=Q18;
                5'b10011:select=Q19;
                5'b10100:select=Q20;
                5'b10101:select=Q21;
                5'b10110:select=Q22;
                5'b10111:select=Q23;
                5'b11000:select=Q24;
                5'b11001:select=Q25;
                5'b11010:select=Q26;
                5'b11011:select=Q27;
                5'b11100:select=Q28;
                5'b11101:select=Q29;
                5'b11110:select=Q30;
                5'b11111:select=Q31;
            endcase
    endfunction
    assign Y = select(Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0,S);    
endmodule

32个寄存器的代码实现如下:

module REG32(D,En,Clk,Clrn,Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0);
    input[31:0]D,En;
    input Clk,Clrn;
    output[31:0]Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0;
    wire [31:0]Qn31,Qn30,Qn29,Qn28,Qn27,Qn26,Qn25,Qn24,Qn23,Qn22,Qn21,Qn20,Qn19,Qn18,Qn17,Qn16,Qn15,Qn14,Qn13,Qn12,Qn11,Qn10,Qn9,Qn8,Qn7,Qn6,Qn5,Qn4,Qn3,Qn2,Qn1,Qn0;
    D_FFEC32 q31(D,Clk,En[31],Clrn,Q31,Qn31);
    D_FFEC32 q30(D,Clk,En[30],Clrn,Q30,Qn30);
    D_FFEC32 q29(D,Clk,En[29],Clrn,Q29,Qn29);
    D_FFEC32 q28(D,Clk,En[28],Clrn,Q28,Qn28);
    D_FFEC32 q27(D,Clk,En[27],Clrn,Q27,Qn27);
    D_FFEC32 q26(D,Clk,En[26],Clrn,Q26,Qn26);
    D_FFEC32 q25(D,Clk,En[25],Clrn,Q25,Qn25);
    D_FFEC32 q24(D,Clk,En[24],Clrn,Q24,Qn24);
    D_FFEC32 q23(D,Clk,En[23],Clrn,Q23,Qn23);
    D_FFEC32 q22(D,Clk,En[22],Clrn,Q22,Qn22);
    D_FFEC32 q21(D,Clk,En[21],Clrn,Q21,Qn21);
    D_FFEC32 q20(D,Clk,En[20],Clrn,Q20,Qn20);
    D_FFEC32 q19(D,Clk,En[19],Clrn,Q19,Qn19);
    D_FFEC32 q18(D,Clk,En[18],Clrn,Q18,Qn18);
    D_FFEC32 q17(D,Clk,En[17],Clrn,Q17,Qn17);
    D_FFEC32 q16(D,Clk,En[16],Clrn,Q16,Qn16);
    D_FFEC32 q15(D,Clk,En[15],Clrn,Q15,Qn15);
    D_FFEC32 q14(D,Clk,En[14],Clrn,Q14,Qn14);
    D_FFEC32 q13(D,Clk,En[13],Clrn,Q13,Qn13);
    D_FFEC32 q12(D,Clk,En[12],Clrn,Q12,Qn12);
    D_FFEC32 q11(D,Clk,En[11],Clrn,Q11,Qn11);
    D_FFEC32 q10(D,Clk,En[10],Clrn,Q10,Qn10);
    D_FFEC32 q9(D,Clk,En[9],Clrn,Q9,Qn9);
    D_FFEC32 q8(D,Clk,En[8],Clrn,Q8,Qn8);
    D_FFEC32 q7(D,Clk,En[7],Clrn,Q7,Qn7);
    D_FFEC32 q6(D,Clk,En[6],Clrn,Q6,Qn6);
    D_FFEC32 q5(D,Clk,En[5],Clrn,Q5,Qn5);
    D_FFEC32 q4(D,Clk,En[4],Clrn,Q4,Qn4);
    D_FFEC32 q3(D,Clk,En[3],Clrn,Q3,Qn3);
    D_FFEC32 q2(D,Clk,En[2],Clrn,Q2,Qn2);
    D_FFEC32 q1(D,Clk,En[1],Clrn,Q1,Qn1);
    assign Q0=0;
    assign Qn0=0;
endmodule

带有异步清零的32位D触发器的代码实现:

module D_FFEC32(D,Clk,En,Clrn,Q,Qn);
    input[31:0]D;
    input Clk,En,Clrn;
    output[31:0]Q,Qn;
    D_FFEC d0(D[0],Clk,En,Clrn,Q[0],Qn[0]);
    D_FFEC d1(D[1],Clk,En,Clrn,Q[1],Qn[1]);
    D_FFEC d2(D[2],Clk,En,Clrn,Q[2],Qn[2]);
    D_FFEC d3(D[3],Clk,En,Clrn,Q[3],Qn[3]);
    D_FFEC d4(D[4],Clk,En,Clrn,Q[4],Qn[4]);
    D_FFEC d5(D[5],Clk,En,Clrn,Q[5],Qn[5]);
    D_FFEC d6(D[6],Clk,En,Clrn,Q[6],Qn[6]);
    D_FFEC d7(D[7],Clk,En,Clrn,Q[7],Qn[7]);
    D_FFEC d8(D[8],Clk,En,Clrn,Q[8],Qn[8]);
    D_FFEC d9(D[9],Clk,En,Clrn,Q[9],Qn[9]);
    D_FFEC d10(D[10],Clk,En,Clrn,Q[10],Qn[10]);
    D_FFEC d11(D[11],Clk,En,Clrn,Q[11],Qn[11]);
    D_FFEC d12(D[12],Clk,En,Clrn,Q[12],Qn[12]);
    D_FFEC d13(D[13],Clk,En,Clrn,Q[13],Qn[13]);
    D_FFEC d14(D[14],Clk,En,Clrn,Q[14],Qn[14]);
    D_FFEC d15(D[15],Clk,En,Clrn,Q[15],Qn[15]);
    D_FFEC d16(D[16],Clk,En,Clrn,Q[16],Qn[16]);
    D_FFEC d17(D[17],Clk,En,Clrn,Q[17],Qn[17]);
    D_FFEC d18(D[18],Clk,En,Clrn,Q[18],Qn[18]);
    D_FFEC d19(D[19],Clk,En,Clrn,Q[19],Qn[19]);
    D_FFEC d20(D[20],Clk,En,Clrn,Q[20],Qn[20]);
    D_FFEC d21(D[21],Clk,En,Clrn,Q[21],Qn[21]);
    D_FFEC d22(D[22],Clk,En,Clrn,Q[22],Qn[22]);
    D_FFEC d23(D[23],Clk,En,Clrn,Q[23],Qn[23]);
    D_FFEC d24(D[24],Clk,En,Clrn,Q[24],Qn[24]);
    D_FFEC d25(D[25],Clk,En,Clrn,Q[25],Qn[25]);
    D_FFEC d26(D[26],Clk,En,Clrn,Q[26],Qn[26]);
    D_FFEC d27(D[27],Clk,En,Clrn,Q[27],Qn[27]);
    D_FFEC d28(D[28],Clk,En,Clrn,Q[28],Qn[28]);
    D_FFEC d29(D[29],Clk,En,Clrn,Q[29],Qn[29]);
    D_FFEC d30(D[30],Clk,En,Clrn,Q[30],Qn[30]);
    D_FFEC d31(D[31],Clk,En,Clrn,Q[31],Qn[31]);
endmodule

带有使能端D触发器的代码实现:

module D_FFEC(D,Clk,En,Clrn,Q,Qn);
    input D,Clk,En,Clrn;
    output Q,Qn;
    wire Y0,Y_C;
    MUX2X1 m0(Q,D,En,Y0);
    and i0(Y_C,Y0,Clrn);
    D_FF d0(Y_C,Clk,Q,Qn);
endmodule

2选1选择器的代码实现;

module MUX2X1(A0,A1,S,Y);
    input A0,A1,S;
    output Y;
    not i0(S_n,S);
    nand i1(A0_S,A0,S_n);
    nand i2(A1_S,A1,S);
    nand i3(Y,A0_S,A1_S);
endmodule

D锁存器的代码实现:

module D_FF(D,Clk,Q,Qn);
    input D,Clk;
    output Q,Qn;
    wire Clkn,Q0,Qn0;
    not i0(Clkn,Clk);
    D_Latch d0(D,Clkn,Q0,Qn0);
    D_Latch d1(Q0,Clk,Q,Qn);
endmodule

D触发器的代码实现如下:

module D_Latch(D,En,Q,Qn);
    input D,En;
    output Q,Qn;
    wire Sn,Rn,Dn;
    not i0(Dn,D);
    nand i1(Sn,D,En);
    nand i2(Rn,En,Dn);
    nand i3(Q,Sn,Qn);
    nand i4(Qn,Q,Rn);
endmodule

7. 控制器(CONUNIT)的设计与实现

实现CONUNIT的Verilog程序参考代码:

module CONUNIT(
 input [5:0]Op,
 input [5:0]Func,
 input Z,
 output Regrt,
 output Se,
 output Wreg,
 output Aluqb,
 output [3:0]Aluc,
 output Wmem,
 output [1:0]Pcsrc,
 output Reg2reg,
 output shift,
 output j 
    );
    wire i_add = (Op == 6'b000000 & Func == 6'b100000)?1:0;
    wire i_sub = (Op == 6'b000000 & Func == 6'b100010)?1:0;
    wire i_and = (Op == 6'b000000 & Func == 6'b100100)?1:0;
    wire i_or  = (Op == 6'b000000 & Func == 6'b100101)?1:0;
    wire i_xor = (Op == 6'b000000 & Func == 6'b100110)?1:0;
    wire i_sll = (Op == 6'b000000 & Func == 6'b000000)?1:0;
    wire i_srl = (Op == 6'b000000 & Func == 6'b000010)?1:0;
    wire i_sra = (Op == 6'b000000 & Func == 6'b000011)?1:0;
    wire i_jr  = (Op == 6'b000000 & Func == 6'b001000)?1:0;
    //R
    wire i_addi = (Op == 6'b001000)?1:0;
    wire i_andi = (Op == 6'b001100)?1:0; 
    wire i_ori  = (Op == 6'b001101)?1:0;
    wire i_xori = (Op == 6'b001110)?1:0;
    wire i_lw   = (Op == 6'b100011)?1:0;
    wire i_sw   = (Op == 6'b101011)?1:0;
    wire i_beq  = (Op == 6'b000100)?1:0;
    wire i_bne  = (Op == 6'b000101)?1:0;
    wire i_lui  = (Op == 6'b001111)?1:0;
    //I
    wire i_j    = (Op == 6'b000010)?1:0;
    wire i_jal  = (Op == 6'b000011)?1:0;
    assign Wreg = i_add|i_sub|i_and|i_or|i_xor|i_sll|i_srl|i_sra|i_addi|i_andi|i_ori|i_or|i_xori|i_lw|i_lui|i_jal;
    assign Regrt = i_addi|i_andi|i_ori|i_xori|i_lw|i_sw|i_lui|i_beq|i_bne|i_j|i_jal;
    assign Reg2reg  = i_add|i_sub|i_and|i_or|i_xor|i_sll|i_srl|i_sra|i_addi|i_andi|i_ori|i_xori|i_sw|i_beq|i_bne|i_j|i_jal;
    assign Aluqb = i_add | i_sub | i_and | i_or | i_xor | i_sll | i_srl | i_sra | i_beq | i_bne |i_j;
    assign Se   = i_addi | i_lw | i_sw | i_beq | i_bne;
    assign Aluc[3] = i_sra;
    assign Aluc[2] = i_xor |i_lui | i_sll | i_srl | i_sra |i_xori;
    assign Aluc[1] = i_and | i_or | i_lui | i_srl | i_sra | i_andi | i_ori;
    assign Aluc[0] = i_sub | i_ori | i_or | i_sll | i_srl |i_sra| i_beq | i_bne;
    assign Wmem = i_sw;
    assign Pcsrc[0] = (i_beq&Z) | (i_bne&~Z) | i_jal | i_j;
    assign Pcsrc[1] = i_j | i_jr | i_jal;
    assign shift=i_sll | i_srl | i_sra;
    assign j=i_jal | i_jr;
endmodule

在控制器的Verilog实现代码中,首先对指令的功能进行译码,以“i_”开头的变量表示批令的功能,例如i_add为1表示该指令的功能是加法(add),其余的以此类指。然后根据指令的功能产生相应的控制信号。

8.指令存储器的设计与实现

由于我们的主要目的是对所设计的CPU进行测试,故可以不使用IP核设计指令存储器,直接使用通用代码实现。指令存储器的设计实现了20条指令的设计,可以较好地验证单周期CPU具有的功能。实现INSTMEM的Verilog程序参考代码:

module INSTMEM(Addr,Inst);//指令存储器
    input[31:0]Addr;
    //状态为'0',写指令寄存器,否则为读指令寄存器
    output[31:0]Inst;
    wire[31:0]Rom[31:0];
    assign Rom[5'h00]=32'h20010008;//addi $1,$0,8 $1=8
    assign Rom[5'h01]=32'h3402000C;//ori $2,$0,12 $2=12
    assign Rom[5'h02]=32'h00221820;//add $3,$1,$2 $3=20
    assign Rom[5'h03]=32'h00412022;//sub $4,$2,$1 $4=4
    assign Rom[5'h04]=32'h00222824;//and $5,$1,$2 $5=8
    assign Rom[5'h05]=32'h00223025;//or $6,$1,$2 $6=12
    assign Rom[5'h06]=32'h14220002;//bne $1,$2,2
    assign Rom[5'h07]=32'hXXXXXXXX;
    assign Rom[5'h08]=32'hXXXXXXXX;
    assign Rom[5'h09]=32'h10220002;// beq $1,$2,2
    assign Rom[5'h0A]=32'h0800000D;// J 0D 
    assign Rom[5'h0B]=32'hXXXXXXXX;
    assign Rom[5'h0C]=32'hXXXXXXXX;
    assign Rom[5'h0D]=32'hAD02000A;// sw $2 10($8) memory[$8+10]=10
    assign Rom[5'h0E]=32'h8D04000A;//lw $4 10($8) $4=12
    assign Rom[5'h0F]=32'h00221826;//xor $3,$1,$2
    assign Rom[5'h10]=32'h00021900;//sll $3,$2,4
    assign Rom[5'h11]=32'h00021902;//srl $3,$2,4
    assign Rom[5'h12]=32'h00021903;//sra $3,$2,4
    assign Rom[5'h13]=32'h30470009;//andi $7,$2,9
    assign Rom[5'h14]=32'h382300EF;//xori $3,$1,0xef
    assign Rom[5'h15]=32'h3C011234;//lui $1,0x1234
    assign Rom[5'h16]=32'h0C00001A;//Jal 1A
    assign Rom[5'h17]=32'h0800001A;// J 1A
    assign Rom[5'h18]=32'hXXXXXXXX;
    assign Rom[5'h19]=32'hXXXXXXXX;
    assign Rom[5'h1A]=32'h03E00008;//Jr 16
    assign Rom[5'h1B]=32'hXXXXXXXX;
    assign Rom[5'h1C]=32'hXXXXXXXX;
    assign Rom[5'h1D]=32'hXXXXXXXX;
    assign Rom[5'h1E]=32'hXXXXXXXX;
    assign Rom[5'h1F]=32'hXXXXXXXX;
    assign Inst=Rom[Addr[6:2]];
endmodule

9.数据存储器的设计与实现

由于我们的主要目的是对所设计的CPU进行测试,故可以不使用IP核设计数据存储器,直接使用通用代码实现。由于需要支持取数/存数指令,所以要在指令储存器的基础上增加写入数据的数据写入端口,写使能信号。又因为写操作在时钟信号的上升沿,所以要增加时钟信号。
在这里插入图片描述
当We为1时,进行sw指令操作,此时Din端口输入信号实际为rt,Addr端口输入信号为rs和偏移量相加的地址,在时钟周期上升沿将rt的值写入改地址的储存单元。

当We为0时,进行lw指令操作,此时Addr端口输入信号为rs和偏移量相加的地址,Dout为读取该地址储存器的内容。

实现DATAMEM的verilog程序代码如下:

module DATAMEM(Addr,Din,Clk,We,Dout);
    input [31:0]Addr,Din;
    input Clk,We;
    output [31:0]Dout;
 reg [31:0]ram[0:31];
 integer i;
 initial begin
        for ( i = 0 ; i <= 31 ; i = i + 1) 
            ram [i] = i;
    end
 always @ (posedge Clk) begin
        if (We) ram[Addr[6:2]] <= Din;
 end
 assign Dout = ram[Addr[6:2]];
endmodule

10. PC寄存器的设计与实现

PC寄存器用于给出指令在指令储存器中的地址。为实现稳定输出,在时钟信号的上升沿更新,而且需要一个控制信号,在控制信号为0的时候初始化PC寄存器。主要的代码实现为:

module PC(Clk,Reset,Result,Address);  
    input Clk;//时钟
    input Reset;//是否重置地址。0-初始化PC,否则接受新地址       
    input[31:0] Result;
    output reg[31:0] Address;
    initial begin
        Address  <= 0;
    end
    always @(posedge Clk or negedge Reset)  
    begin  
        if (Reset==0) //如果为0则初始化PC,否则接受新地址
            begin  
                Address <= 0;  
            end  
        else   
            begin
                Address =  Result;  
        end  
    end  
endmodule

11. CPU的封装

当CPU的所有功能部件分别设计完成后,就要将其进行封装,以构成一个完整的CPU。本实验封装后CPU如图所示。
在这里插入图片描述
Clk:时钟周期,外部输入信号。
Reset:清零信号,外部输入信号。

CPU封装的基本原理就是根据单周期CPU的总体电路图,将各功能部件连接起来。CPU封装的Verilog代码如下:

module CPU(Clk,Reset,Addr,Inst,Qa,Qb,ALU_R,NEXTADDR,D);
    input Clk,Reset;
    output [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
    
    wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D1,X,Y,Dout,mux4x32_2,R;
    wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem,shift,j;
    wire [3:0]Aluc;
    wire [1:0]Pcsrc;
    wire [4:0]Wr,Wr1;
    
    PC pc(Clk,Reset,Result,Addr);
    PCadd4 pcadd4(Addr,PCadd4);
    INSTMEM instmem(Addr,Inst);
    
    CONUNIT conunit(Inst[31:26],Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg,shift,j);
    MUX2X5 mux2x5_1(Inst[15:11],Inst[20:16],Regrt,Wr1);
    MUX2X5 mux2x5_2(Wr1,31,j,Wr);
    EXT16T32 ext16t32(Inst[15:0],Se,EXTIMM);
    SHIFTER_COMBINATION shifter1(Inst[25:0],PCadd4,InstL2);
    SHIFTER shifter2(EXTIMM,2,0,0,EXTIMML2);
    REGFILE regfile(Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);
    MUX2X32 mux2x32_1(EXTIMM,Qb,Aluqb,Y);
    MUX2X32 mux2x32_2(Qa,Inst,shift,X);
    ALU alu(X,Y,Aluc,R,Z);
    DATAMEM datamem(R,Qb,Clk,Wmem,Dout); 
    MUX2X32 mux2x32_3(Dout,R,Reg2reg,D1);
    MUX2X32 mux2x32_4(D1,PCadd4,j,D);
    CLA_32 cla_32(PCadd4,EXTIMML2,0,mux4x32_2,Cout);
    MUX4X32 mux4x32(PCadd4,mux4x32_2,Qa,InstL2,Pcsrc,Result);
    assign NEXTADDR=Result;
    assign ALU_R=R;
endmodule

封装后的RTL图为:
在这里插入图片描述
测试代码为:

module test();
    reg Clk,Reset;
    wire [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
   
    wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,Y,Dout,mux4x32_2,R;
    wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem;
    wire [3:0]Aluc;
    wire [1:0]Pcsrc;
    wire [4:0]Wr;

CPU u(Clk,Reset,Addr,Inst,Qa,Qb,ALU_R,NEXTADDR,D);

    initial begin        
	Clk=0;
	Reset=0;
         #5
		Reset<=1;
    end
    always #5 Clk=~Clk;
 endmodule

仿真波形为:
在这里插入图片描述

12. 指令代码分析

当CPU设计完成后,还需要对其进行测试,为此需要再设计一个指令存储器和一个数据存储器,同时再设计相应的测试程序。

为方便对指令进行分析,在测试代码中进行CPU的封装。

1.assign
Rom[5’h00]=32’h20010008;//addi $1,$0,8 $1=8

指令含义:addi $1,$0,8

结果:$1=8
在这里插入图片描述

2.assign Rom[5’h01]=32’h3402000C;//ori $2,$0,12

指令含义:ori $2,$0,12

结果:$2=12

在这里插入图片描述

由于代码过多,不再进行一一分析,选取重要指令进行分析:

2.assign Rom[5’h06]=32’h14220002;//bne $1,$2,2

指令含义:bne $1,$2,2 ($1 和$2 不相等,则跳到 8+当前 PC 地址的值)

结果:PC 地址跳到 0x24
在这里插入图片描述

4.assign Rom[5’h0A]=32’h0800000D;// J0D

指令含义:J 0D 直接跳到0D指令

结果:PC寄存器的值更新为0x34
在这里插入图片描述

5.assign Rom[5’h0D]=32’hAD02000A;// sw $2 10($8)

指令含义:sw $2 10($8) memory[$8+10]=12

结果:DATAMEM打开,$2的数值写入到$8+10的存储单元中
在这里插入图片描述

6.assign Rom[5’h0E]=32’h8D04000A;//lw $4 10($8) $4=12

指令含义:lw $4 10($8) $4=memory[$8+10]=12

结果:DATAMEM关闭,读取$8+10存储单元中的值到$4中

在这里插入图片描述

7.assign Rom[5’h16]=32’h0C00001A;//Jal 1A

指令含义:Jal 1A ,跳到目标地址且PC+4存储在ra寄存器($31)中

结果:PC寄存器值更新为0x68

在这里插入图片描述

8.assign Rom[5’h1A]=32’h03E00008;//Jr 16

指令含义:Jr 16,跳到目标地址,目标地址为且第16条指令的返回地址

结果:PC寄存器值更新为0x5c

在这里插入图片描述

9.assign Rom[5’h17]=32’h0800001A;// J 1A

指令含义:J 1A,跳到目标地址

结果:PC寄存器值更新为0x68
在这里插入图片描述

13. 实验结论与体会

通过此处单周期CPU的实验,较好地实现了MIPS32的20条指令,了解到CPU处理程序的复杂过程。与verilog门级电路相比,算法实现的难度相对较低,且代码易读性更高。
参考:https://blog.csdn.net/Accelerato/article/details/86546751
源代码下载链接:https://download.csdn.net/download/qq_45288566/12254660

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值