用Verilog设计1个单周期CPU


一、原理

1.单周期CPU

单周期CPU可以看成由数据通路和和控制部件两大部分组成。数据通路是指在指令执行过程中,数据所经过的路径和路径上所涉及的功能部件。而控制部件则根据每条指令的不同功能,生成对不同数据通路的不同控制信号,正确地控制指令的执行流程。
因此,要设计处理器,首先需要确定处理器的指令集和指令编码,然后确定每条指令的数据通路,最后确定数据通路的控制信号。

2.单周期CPU指令模块

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

3. MIPS指令格式

MIPS指令系统结构有MIPS-32和MIPS-64两种。本实验的MIPS指令选用MIPS-32。以下所说的MIPS指令均指MIPS-32。MIPS的指令格式为32位。下图给出了MIPS指令的3种格式。
在这里插入图片描述

4. 指令处理流程

在这里插入图片描述

5. 单周期CPU数据通路

CPU的电路包括数据路径(Data path)和控制部件(Control Unit)两大部分,下面给出了单周期CPU的总体设计图。
在这里插入图片描述
一个简单的基本上能够在单周期上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令储存在指令储存器,数据储存在数据存储器。访问存储器时,先给出地址,然后由读/写信号控制。对于寄存器组,读操作时,先给出地址,输出端直接输出相应数据;而在写操作时,在 We使能信号为1时,在时钟边沿触发写入。

二、单周期CPU的设计

1.pcadd4

在这里插入图片描述
In为输入端口,输入当前指令地址;Out为下一条指令地址,为输出端口。
代码:

`timescale 1ns / 1ps
 
module PCadd4(PC_0,PCadd4);
 
    input [31:0] PC_0;//偏移量
 
    output [31:0] PCadd4;//新指令地址
 
    CLA_32 cla32_for_pcadd(PC_0,4,0, PCadd4, Cout);//在ALU模块
 
endmodule

2.指令存储器INSTMEM

在这里插入图片描述
依据当前pc,读取指令寄存器中相对应地址Addr[6:2]的指令。
address: 指令地址,输入信号
inst:指令编码,输出信号
代码:

`timescale 1ns / 1ps
 
module INSTMEM(
 
      input  [31:0] address,
 
      output [31:0] inst
 
    );
 
    wire [31:0] ram [0:18];
 
 
 
 assign ram[5'h00] = 32'b001000_00001_00001_0000000000000000;
 
//不写的码,仅占位用(andi R1 , R1 , 0x0000)
 
assign ram[5'h01] = 32'b001000_00001_00001_0001001000110100;
 
//addi R1 , R1 , 0x1234   
 
assign ram[5'h02] = 32'b001000_00010_00010_0001000100010001;
 
//addi R2 , R2 , 0x1111
 
 assign ram[5'h03] = 32'b000000_00001_00010_00011_00000_100000;
 
//add R3 , R1 , R2 0x2345
 
assign ram[5'h04] = 32'b000000_00001_00010_00100_00000_100010;
 
//sub R4 , R1 , R2 0x0123
 
assign ram[5'h05] = 32'b000000_00001_00010_00011_00000_100100;
 
//and R3 , R1 , R2  0x1010
 
assign ram[5'h06] = 32'b000000_00001_00010_00100_00000_100101;
 
//or R4 , R1 , R2 0x1335
 
assign ram[5'h07] = 32'b001100_00001_00101_0010001000100010;
 
//andi R5 , R1 , 0x2222
 
assign ram[5'h08] = 32'b001101_00001_00101_0011001100110011;
 
//ori R5 , R1 , 0x3333
 
assign ram[5'h09] = 32'b000010_00000000000000000000001100;
 
//j   跳转到beq行  0800000C
 
assign ram[5'h0a] = 32'b000000_00000_00010_00011_00100_000010;
 
//不用
 
assign ram[5'h0b] = 32'b000000_00000_00010_00011_00100_000011;
 
//不用
 
assign ram[5'h0c] = 32'b000100_00001_00011_0000000000000011;
 
//beq R1 , R2 , 3 不跳 10230003
 
assign ram[5'h0d] = 32'b000101_00001_00011_0000000000000010;
 
//bne R1 , R2 , 2 跳至sw行  14230002
 
assign ram[5'h0e] = 32'b001101_00001_00011_0000000011101111;
 
//不用
 
assign ram[5'h0f] = 32'b001110_00001_00011_0000000011101111;
 
//不用
 
assign ram[5'h10] = 32'b101011_00100_00101_0000000000000001;
 
//sw R5 , 1(R4)  AC850001 r5=r4=0x1335
 
assign ram[5'h11] = 32'b100011_00100_00110_0000000000000001;
 
// lw R6 , 1(R4)  8C860001
 
assign ram[5'h12] = 32'b000010_00000000000000000000000001;
 
//j 跳回开头  08000001
 
assign inst = ram[address[6:2]];
 
endmodule

3.控制单元CONUNIT

在这里插入图片描述
控制器是作为CPU控制信号产生的器件,通过解析op得到该指令的各种控制信号,使其他器件有效或无效。
Func:输入信号
Op :输入信号
Z:零标志信号,对Pcsrc有影响,输入信号
Aluc :控制ALU的计算种类,输出信号
Aluqb:控制ALU的Y端口的输入值,输出信号
Pcsrc :控制目标指令地址,输出信号
Regrt:控制输入寄存器的Wr端口,输出信号
Reg2reg:控制REHFILE更新值的来源
Se:控制扩展模块,输出信号
Wmem:控制数据存储器的写使能信号,输出信号
Wreg:控制寄存器端的写使能信号,输出信号
代码:

`timescale 1ns / 1ps
 
module CONUNIT(Op,Func,Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);
 
    input [5:0] Op,Func;
 
    input Z;
 
    //输入指令的op和func字段(对应[31:26][5:0])
 
    //输入的z是alu的运算结果 全为零则z为零
 
    output Regrt,Se,Wreg,Aluqb,Wmem,Reg2reg;
 
    output [1:0] Aluc,Pcsrc;
 
    //regrt决定是读取立即数还是寄存器1/0
 
    //se决定是符号扩展还是0扩展 1/0
 
    //wreg寄存器的写信号
 
    //aluqb选择alu读取的扩展后的 还是qb寄存器输出的0/1
 
    //wmem 数据储存器的写信号1有效
 
    //reg2regd 输入寄存器D的是运算R还是dmem的dout
 
    //aluc 00+ 01- 10& 11|
 
    //pcsrs 0 0pc+4 2分支语句 3j指令
 
    wire R_type=~|Op;
 
   
 
    wire I_add=R_type&Func[5]&~Func[4]&~Func[3]&~Func[2]&~Func[1]&~Func[0];
 
    wire I_sub=R_type&Func[5]&~Func[4]&~Func[3]&~Func[2]&Func[1]&~Func[0];
 
    wire I_and=R_type&Func[5]&~Func[4]&~Func[3]&Func[2]&~Func[1]&~Func[0];
 
    wire I_or=R_type&Func[5]&~Func[4]&~Func[3]&Func[2]&~Func[1]&Func[0];
 
    wire I_addi=~Op[5]&~Op[4]&Op[3]&~Op[2]&~Op[1]&~Op[0];
 
    wire I_andi=~Op[5]&~Op[4]&Op[3]&Op[2]&~Op[1]&~Op[0];
 
    wire I_ori=~Op[5]&~Op[4]&Op[3]&Op[2]&~Op[1]&Op[0];
 
    wire I_lw=Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&Op[0];
 
    wire I_sw=Op[5]&~Op[4]&Op[3]&~Op[2]&Op[1]&Op[0];
 
    wire I_beq=~Op[5]&~Op[4]&~Op[3]&Op[2]&~Op[1]&~Op[0];
 
    wire I_bne=~Op[5]&~Op[4]&~Op[3]&Op[2]&~Op[1]&Op[0];
 
    wire I_J=~Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&~Op[0];
 
   
 
    assign Regrt=I_addi|I_andi|I_ori|I_lw|I_sw|I_beq|I_bne|I_J;
 
    assign Se=I_addi|I_lw|I_sw|I_beq|I_bne;
 
    assign Wreg=I_add|I_sub|I_and|I_or|I_addi|I_andi|I_ori|I_lw;
 
    assign Aluqb=I_add|I_sub|I_and|I_or|I_beq|I_bne|I_J;
 
    assign Aluc[1]=I_and|I_or|I_andi|I_ori;
 
    assign Aluc[0]=I_sub|I_or|I_ori|I_beq|I_bne;
 
    assign Wmem=I_sw;
 
    assign Pcsrc[1]=I_beq&Z|I_bne&~Z|I_J;
 
    assign Pcsrc[0]=I_J;
 
    assign Reg2reg=I_add|I_sub|I_and|I_or|I_addi|I_andi|I_ori|I_sw|I_beq|I_bne|I_J;
 
   
 
endmodule   

控制信号
在这里插入图片描述

4.寄存器堆REGFILE

在这里插入图片描述
给出要读取的两个寄存器编号和要写入的寄存器编号,然后由Qa和Qb端口更新Ra和Rb端口的输入编号分别输入其值。
Clk:时钟周期,输入信号
Clrn:清零信号,输入信号
D:寄存器更新值,输入信号
Ra:读取寄存器编号1,输入信号
Rb:读取寄存器编号2或立即数,输入信号
We:写入寄存器编号3,输入信号
Wr:写使能信号,为0的时候不能写入,D值不更新,为1的时候能写入,D值更新(当时钟边沿到来时,将𝐷𝑎𝑡𝑎端口送来的数据写入𝑅𝑤指定的寄存器)入信号
Qa:输出寄存器1的值,输出信号
Qb:输出寄存器2的值,输出信号
代码:

`timescale 1ns / 1ps
 
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;
 
    //调用了一个3t32的译码器
 
//负责将输入的要写入的寄存器号转换为对应的的寄存器号
 
    wire [31:0]Y_mux;
 
    DEC5T32E dec(Wr,We,Y_mux);
 
    //调用一个reg32
 
    wire [31:0]Q31_reg32,Q30_reg32,Q29_reg32,Q28_reg32,Q27_reg32,Q26_reg32,Q25_reg32,Q24_reg32,Q23_reg32,Q22_reg32,Q21_reg32,Q20_reg32,Q19_reg32,Q18_reg32,Q17_reg32,Q16_reg32,Q15_reg32,Q14_reg32,Q13_reg32,Q12_reg32,Q11_reg32,Q10_reg32,Q9_reg32,Q8_reg32,Q7_reg32,Q6_reg32,Q5_reg32,Q4_reg32,Q3_reg32,Q2_reg32,Q1_reg32,Q0_reg32;  
 
    REG32  A(D,Y_mux,Clk,Clrn,Q31_reg32,Q30_reg32,Q29_reg32,Q28_reg32,Q27_reg32,Q26_reg32,Q25_reg32,Q24_reg32,Q23_reg32,Q22_reg32,Q21_reg32,Q20_reg32,Q19_reg32,Q18_reg32,Q17_reg32,Q16_reg32,Q15_reg32,Q14_reg32,Q13_reg32,Q12_reg32,Q11_reg32,Q10_reg32,Q9_reg32,Q8_reg32,Q7_reg32,Q6_reg32,Q5_reg32,Q4_reg32,Q3_reg32,Q2_reg32,Q1_reg32,Q0_reg32);
 
    //因为reg输出的是32个信号 所以要用2个32选1的复用器选择那个是最后的输出
 
    MUX32X32 select1(Q0_reg32,Q1_reg32,Q2_reg32,Q3_reg32,Q4_reg32,Q5_reg32,Q6_reg32,Q7_reg32,Q8_reg32,Q9_reg32,Q10_reg32,Q11_reg32,Q12_reg32,Q13_reg32,Q14_reg32,Q15_reg32,Q16_reg32,Q17_reg32,Q18_reg32,Q19_reg32,Q20_reg32,Q21_reg32,Q22_reg32,Q23_reg32,Q24_reg32,Q25_reg32,Q26_reg32,Q27_reg32,Q28_reg32,Q29_reg32,Q30_reg32,Q31_reg32,Ra,Qa);
 
    MUX32X32 select2(Q0_reg32,Q1_reg32,Q2_reg32,Q3_reg32,Q4_reg32,Q5_reg32,Q6_reg32,Q7_reg32,Q8_reg32,Q9_reg32,Q10_reg32,Q11_reg32,Q12_reg32,Q13_reg32,Q14_reg32,Q15_reg32,Q16_reg32,Q17_reg32,Q18_reg32,Q19_reg32,Q20_reg32,Q21_reg32,Q22_reg32,Q23_reg32,Q24_reg32,Q25_reg32,Q26_reg32,Q27_reg32,Q28_reg32,Q29_reg32,Q30_reg32,Q31_reg32,Rb,Qb);
 
endmodule
 
 
 
module DEC5T32E(X,En,Y);
 
   input [4:0]X;
 
   input En;
 
   output [31:0]Y;
 
    wire [31:0]Y1,En_32;
 
    assign Y1=1<<X;
 
    assign En_32={32{En}};
 
    assign Y=Y1&En_32;
 
endmodule
 
 
 
 
 
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);
 
    //参数的输入顺序和课本保持一致
 
    //32位寄存器堆 共有32个32位的输出接口 一个32位的使能信号
 
    input [31:0]D,En;
 
    input Clk,Clrn;
 
    output [31:0]Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16;
 
    output [31:0]Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31;
 
    D_FFEC32 q1(D,Clk,En[1],Clrn,Q1);
 
    D_FFEC32 q2(D,Clk,En[2],Clrn,Q2);
 
    D_FFEC32 q3(D,Clk,En[3],Clrn,Q3);
 
    D_FFEC32 q4(D,Clk,En[4],Clrn,Q4);
 
    D_FFEC32 q5(D,Clk,En[5],Clrn,Q5);
 
    D_FFEC32 q6(D,Clk,En[6],Clrn,Q6);
 
    D_FFEC32 q7(D,Clk,En[7],Clrn,Q7);
 
    D_FFEC32 q8(D,Clk,En[8],Clrn,Q8);
 
    D_FFEC32 q9(D,Clk,En[9],Clrn,Q9);
 
    D_FFEC32 q10(D,Clk,En[10],Clrn,Q10);
 
    D_FFEC32 q11(D,Clk,En[11],Clrn,Q11);
 
    D_FFEC32 q12(D,Clk,En[12],Clrn,Q12);
 
    D_FFEC32 q13(D,Clk,En[13],Clrn,Q13);
 
    D_FFEC32 q14(D,Clk,En[14],Clrn,Q14);
 
    D_FFEC32 q15(D,Clk,En[15],Clrn,Q15);
 
    D_FFEC32 q16(D,Clk,En[16],Clrn,Q16);
 
    D_FFEC32 q17(D,Clk,En[17],Clrn,Q17);
 
    D_FFEC32 q18(D,Clk,En[18],Clrn,Q18);
 
    D_FFEC32 q19(D,Clk,En[19],Clrn,Q19);
 
    D_FFEC32 q20(D,Clk,En[20],Clrn,Q20);
 
    D_FFEC32 q21(D,Clk,En[21],Clrn,Q21);
 
    D_FFEC32 q22(D,Clk,En[22],Clrn,Q22);
 
    D_FFEC32 q23(D,Clk,En[23],Clrn,Q23);
 
    D_FFEC32 q24(D,Clk,En[24],Clrn,Q24);
 
    D_FFEC32 q25(D,Clk,En[25],Clrn,Q25);
 
    D_FFEC32 q26(D,Clk,En[26],Clrn,Q26);
 
    D_FFEC32 q27(D,Clk,En[27],Clrn,Q27);
 
    D_FFEC32 q28(D,Clk,En[28],Clrn,Q28);
 
    D_FFEC32 q29(D,Clk,En[29],Clrn,Q29);
 
    D_FFEC32 q30(D,Clk,En[30],Clrn,Q30);
 
    D_FFEC32 q31(D,Clk,En[31],Clrn,Q31);
 
    assign Q0=0;
 
endmodule
 
 
 
module D_FFEC32(D,Clk,En,Clrn,Q,Qn);//32位的带有使能端的D触发器 用了32个1位的D_FFEC
 
    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
 
 
 
module D_FFEC(D,Clk,En,Clrn,Q,Qn);//带有使能端的1位D触发器
 
    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
 
 
 
module MUX2X1(A0,A1,S,Cout);//在D_FFEC中用到的选择器
 
    input A0,A1,S;
 
    output Cout;
 
    function Select;
 
        input A0,A1,S;
 
        case(S)
 
            0:Select=A0;
 
            1:Select=A1;
 
        endcase
 
    endfunction
 
    assign Cout=Select(A0,A1,S);
 
endmodule
 
 
 
module D_FF(D,Clk,Q,Qn);//1位D触发器
 
    input D,Clk;
 
    output Q,Qn;
 
    wire Clkn,QO,Qn0;
 
    not i0(Clkn,Clk);
 
    D_Latch d0 (D,Clkn,Q0,Qn0);
 
    D_Latch d1 (Q0,Clk,Q,Qn);
 
endmodule
 
 
 
module D_Latch(D,En,Q,Qn);//一个d锁存器
 
    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

5.ALU

在这里插入图片描述
算数逻辑部件,需要实现加,减,按位与,按位或。
Aluc:控制信号
X:寄存器1的值,输入信号
Y:寄存器2的值或立即数,输入信号
Cout:输入寄存器端口D的计算结果,输出信号
Z:当值为1时代表两个输入信号值相等,当值为0时代表两个输入信号不等,输出信号。
代码:

`timescale 1ns / 1ps
 
module ALU(X,Y,Aluc,Cout,Z);
 
    //00+ 01- 10& 11|
 
    input [31:0]X,Y;
 
    input [1:0]Aluc;
 
    output [31:0]Cout;
 
    output Z;
 
   
 
    wire[31:0]out_as,out_and,out_or,out_and_or;
 
   
 
    ADDSUB_32 as(X,Y,Aluc[0],out_as);
 
   
 
    assign out_and=X&Y;
 
    assign out_or=X|Y;
 
   
 
    MUX2X32 and_or_select(out_and,out_or,Aluc[0],out_and_or);//0& 1|
 
    MUX2X32 as_ao_selete(out_as,out_and_or,Aluc[1],Cout);//0+- 1&|
 
   
 
    assign Z=~|Cout;
 
endmodule

32位加法器ADDSUB_32

`timescale 1ns / 1ps
 
//顶层文件 输入两个数 选择(进位)
 
//输出两个 结果 进位
 
module ADDSUB_32(X, Y, Sub, S, Cout);// 32位加法器ADDSUB_32
 
    //sub 0是加法 1是减法
 
    input [31:0] X;
 
    input [31:0] Y;
 
    input Sub;
 
    output [31:0] S;
 
    output Cout;//输出进位信息
 
    //对输入的sub进行扩展 如果是加法的话0的话异或还是原数
 
    //如果是减的话做异或并且让进位信号为1
 
    CLA_32 add0 (X, Y^{32{Sub}}, Sub, S, Cout); 
 
endmodule
 
 
 
module CLA_32(X, Y, Cin, S, Cout);
 
    input [31:0] X, Y;
 
    input Cin;//输入进位信息
 
    output [31:0] S;
 
    output Cout;
 
    //32位分成8个CLA
 
    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
 
 
 
module CLA_4(X,Y,Cin,S,Cout);
 
    input [3:0]X,Y;
 
    input Cin;
 
    output [3:0]S;
 
    output Cout;
 
    wire cout0,cout1,cout2,cout3;
 
    assign cout0=X[0]&Y[0]|((X[0]|Y[0])&Cin);
 
    assign cout1=X[1]&Y[1]|((X[1]|Y[1])&cout0);
 
    assign cout2=X[2]&Y[2]|((X[2]|Y[2])&cout1);
 
    assign Cout=X[3]&Y[3]|((X[3]|Y[3])&cout2);
 
    assign S[0]=X[0]^Y[0]^Cin;
 
    assign S[1]=X[1]^Y[1]^cout0;
 
    assign S[2]=X[2]^Y[2]^cout1;
 
    assign S[3]=X[3]^Y[3]^cout2;
 
endmodule

6.数据存储器DATAMEM

在这里插入图片描述
数据存储器,通过控制信号,对数据寄存器进行读或者写操作,并且此处模块额外合并了输出DB的数据选择器,此模块同时输出写回寄存器组的数据DB。
Clock:时钟周期,输入信号
addr:访存地址,输入信号
datain:输入的值,输入信号
we:写使能信号,We为1时,进行sw指令操作,此时Din端口输入信号实际为rt,Addr端口输入信号为rs和偏移量相加的地址,在时钟周期上升沿将rt的值写入改地址的储存单元;We为0时,进行lw指令操作,此时Addr端口输入信号为rs和偏移量相加的地址,Dout为读取该地址储存器的内容,输入信号
dataout:读取的值,输出信号

`timescale 1ns / 1ps
 
module DATEMEM(
 
      input [31:0] addr,
 
      input [31:0] datain,
 
      input Clock,
 
      input        we,
 
        output [31:0] dataout
 
    );
 
   reg [31:0] ram [31:0];
 
   assign dataout = ram[addr[6:2]];
 
   always @ (posedge Clock) begin
 
          if (we) ram[addr[6:2]] <= datain;
 
   end
 
   integer i;
 
   initial begin
 
          for ( i = 0 ; i <= 31 ; i = i + 1) ram [i] = i * i;
 
   end   
 
endmodule

7.选择器MUX

MUX4X32
在这里插入图片描述
实现目标地址的选择,四选一多路选择器,主要选择从输入的4个口中的具体选择哪一个口子数据输出
A0:PC+4的地址,输入信号
A1:空位,输入信号
A2:beq和bne指令的跳转地址,输入信号
A3:J指令的跳转地址,输入信号
S:对地址进行选择的控制信号,输入信号
Y:目标地址,输出信号
代码:

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)
 
                00:select=A0;
 
                01:select=A1;
 
                10:select=A2;
 
                11:select=A3;
 
            endcase
 
        endfunction
 
    assign Y=select(A0,A1,A2,A3,S);
 
endmodule

MUX2X5
在这里插入图片描述
R型指令和I行指令的Wr信号不同,所以需要一个5位二选一选择器进行选择。
Inst[15:11],:R型指令的rd信号,输入信号
nst[20:16]:I型指令的rt信号,输入信号
Regrt:选择指令的控制信号,输入信号
Wr:Wr信号,输出信号
代码:

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)
 
            0:select=A0;
 
            1:select=A1;
 
        endcase
 
    endfunction
 
    assign Y=select(A0,A1,S);
 
endmodule

8.MUX2X32

在这里插入图片描述
对写入寄存器的数据进行选择
Dout: DATAMEM的输出值,输入信号
R:ALU的输出值,输入信号
S:控制信号
Y:写入R寄存器堆D端的信号,输出信号
代码:

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)
 
                0:select=A0;
 
                1:select=A1;
 
            endcase
 
        endfunction
 
    assign Y=select(A0,A1,S);
 
endmodule

9.移位器SHIFTER

在这里插入图片描述一个固定左移两位的移位器,用于beq指令的移位
X:指令中的偏移量,输入信号
Sh:偏移量左移后的结果,输出信号
代码:

module SHIFTER32_L2(X,Sh);
 
    //用于beq指令的移位
 
    //beq指令的扩展之后左移两位即乘四
 
    input [31:0] X;
 
    output [31:0] Sh;
 
    parameter pa=2'b00;//一个局部的常量
    assign Sh={X[29:0],pa};
endmodule

10.SHIFTER_FOR_J

在这里插入图片描述
用于j指令的移位器,指令中用以产生跳转的目标地址
Inst[26:0]:指令编码的低26位字段,输入信号。
PCadd4:PC+4的32位字段,输入信号。
sh:32位转移目标地址,输出信号。
代码:

module SHIFTER_FOR_J(X,PCADD4,Sh);
 
    //用于j指令的移位器 需要产生的前四位是pc+4的最高四位
 
    //加上指令的第26位 最低位是00
 
    //就是课本上最下边的移位器
 
    input [26:0] X;
 
    input [31:0] PCADD4;
 
    output [31:0] Sh;
 
    parameter pa=2'b00;
    assign Sh={PCADD4[3:0],X[26:0],pa};
endmodule

11.扩展模块EXT16T32

在这里插入图片描述
I指令的addi需要对立即数进行符号拓展,andi和ori需要对立即数进行零扩展,所以需要一个扩展模块。
select:选择零扩展或是符号扩展的控制模块,输入信号
X:I型指令的立即数字段,输入信号
Y:扩展后的立即数,输出信号。
代码:

`timescale 1ns / 1ps
 
module EXT16T32 (X, Select, Y);
 
    //要移位的信号x 选择是0扩展还是符号扩展的select
 
    input [15:0] X;
 
    input Select;
 
    output [31:0] Y;
 
   
 
    wire [31:0] E0, E1;//e0是零扩展 e1是符号扩展
 
    wire [15:0] e = {16{X[15]}};//e是x[15]的符号位*16
 
   
 
    parameter z = 16'b0;//用于0扩展
   
    assign E0 = {z, X};
    assign E1 = {e, X};
   
    MUX2X32 select_for_ext16t32(E0, E1, Select, Y);//e0是零扩展 e1是符号扩展
   
endmodule

12.SingleCycleCPU

在这里插入图片描述
实现CPU的封装,设计输出信号使得在方正时便于观察其波形图
Clk:时钟周期,外部输入信号。
Reset:清零信号,外部输入信号。
代码:

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

13.测试代码

`timescale 1ns / 1ps
 
module FINAL_TEST;
 
    reg Clk;
 
    reg Reset;
 
    wire [31:0] Addr,Inst,Qa,Qb,R,Result,D;
 
    FINAL test(
 
    .Clk(Clk),
 
    .Clrn(Reset)
 
);
 
    initial begin
 
        Clk=0;Reset=0;
 
        #10;
 
        Clk=1;Reset=0;
 
        #10;
 
        Reset=1;
 
        Clk=0;
 
        forever #20 Clk=~Clk;
 
    end
 
endmodule

元件图
在这里插入图片描述
波形图
在这里插入图片描述

参考链接:https://blog.csdn.net/qq_44040327/article/details/116395694
https://blog.csdn.net/qq_42840665/article/details/114003321

  • 20
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值