精华内容
下载资源
问答
  • 单周期cpu设计verilog
    千次阅读
    2021-04-26 20:25:44

    单周期CPU数据通路

    在这里插入图片描述

    数据通路中各模块代码

    1. PC(程序计数器)
    module PC #(parameter WIDTH=32)(
    //author:XJTU mry
        input clk,reset,
        input [WIDTH-1:0] d,
        output reg[WIDTH-1:0] q
        );
        always @(posedge clk,posedge reset)
        if(reset)q<=0;
        else q<=d;
    endmodule
    
    1. Add(1)(使PC自增的加法器)
    module PC_adder(
        adr,n_adr
        );
        //author:XJTU mry
        input [31:0] adr;
        output [31:0] n_adr;
        assign n_adr = adr + 32'd4;
    endmodule
    
    1. 指令存储器
    module codeRAM(
        address,data
        );
        //author:XJTU mry
            parameter DataWidth=32;
        parameter AddrWidth=32;
        parameter DataDepth=32;
        
        input [AddrWidth-1:0] address;
    
        output [DataWidth-1:0] data;
        //reg[4:0] n;
        //reg [DataWidth-1:0] data_out;
        reg [DataWidth-1:0] men [0:DataDepth-1];
        assign data=men[address[6:2]];
      
        initial 
        begin
            men[0]=32'h20020005;
            men[1]=32'h2003000c;
            men[2]=32'h2067fff7;
            men[3]=32'h00e22025;
            men[4]=32'h00642824;
            men[5]=32'h00a42820;
            men[6]=32'h10a7000a;
            men[7]=32'h0064202a;
            men[8]=32'h10800001;
            men[9]=32'h20050000;
            men[10]=32'h00e2202a;
            men[11]=32'h10800001;
            men[12]=32'h20050000;
            men[13]=32'h00e2202a;
            men[14]=32'h00853820;
            men[15]=32'h00e23822;
            men[16]=32'hac670044;
            men[17]=32'h8c020050;
            men[18]=32'h08000011;
            men[19]=32'h20020001;
            men[20]=32'hac020054;
        end
    endmodule
    
    1. MCU(主控制器)
    module mainDec(
    //author:XJTU mry
        input [5:0] op,
        output mentoreg,menwrite,
        output branch,alusrc,
        output regdst,regwrite,
        output jump,
        output [1:0] aluop
        );
        reg [8:0] controls;
        assign{regwrite,regdst,alusrc,branch,menwrite,mentoreg,jump,aluop}=controls;
        always @(*)
            case(op)
                6'b000000:controls<=9'b110000010;
                6'b100011:controls<=9'b101001000;
                6'b101011:controls<=9'b001010000;
                6'b000100:controls<=9'b000100001;
                6'b001000:controls<=9'b101000000;
                6'b000010:controls<=9'b000000100;
                default:controls<=9'bxxxxxxxxx;
            endcase
    endmodule
    
    1. 多路选择器(6位)
    module MUX6(
    //author:XJTU mry
        A,B,Src,S
        );
        input [4:0] A,B;
        input Src;
        output [4:0] S;
        assign S = (Src==0) ? A:B;
    endmodule
    
    1. RF(寄存器组)
    module RegisterUnit(
    //author:XJTU mry
        clk,we,ra1,ra2,wa3,wd,rd1,rd2
        );
        parameter DataWidth=32;
        parameter AddrWidth=5;
        parameter DataDepth=1<<AddrWidth;
            input clk;
            input we;
            input [AddrWidth-1:0] ra1,ra2,wa3;
            input [DataWidth-1:0] wd;
            output [DataWidth-1:0] rd1,rd2;
            reg [DataWidth-1:0] rf [DataDepth-1:0];
            assign rd1=rf[ra1];
            assign rd2=rf[ra2];
            always @ (posedge clk)
            begin :MEM_WRITE
                if(we)begin
                    rf[wa3]=wd;
                end
            end
            initial 
                begin
                    rf[0]=32'h0;
                    rf[1]=32'h0;
                    rf[2]=32'h0;
                    rf[3]=32'h0;
                    rf[4]=32'h0;
                    rf[5]=32'h0;
                    rf[6]=32'h0;
                    rf[7]=32'h0;
                    rf[8]=32'h0;
                    rf[9]=32'h0;
                    rf[10]=32'h0;
                    rf[11]=32'h0;
                    rf[12]=32'h0;
                    rf[13]=32'h0;
                    rf[14]=32'h0;
                    rf[15]=32'h0;
                    rf[16]=32'h0;
                end
    
    endmodule
    
    1. SigExt16/32
    module Extend16to32(
    //author:XJTU mry
        order,addr
        );
        input [15:0] order;
        output reg [31:0] addr;
         
        always @(order)
        begin
           if(order[15]==1) addr[31:16]=16'b1111111111111111;
        else addr[31:16]=16'b0000000000000000;
            addr[15:0]=order[15:0];
        end
    endmodule
    
    1. 多路选择器(32位)
    module MUX32(
    //author:XJTU mry
    A,B,Src,S
        );
        input [31:0] A,B;
        input Src;
        output [31:0] S;
        assign S = (Src==0) ? A:B;
    endmodule
    
    1. ALUCU(ALU控制器)
    module aluDec(
    //author:XJTU mry
        input [5:0]funct,
        input [1:0]aluop,
        output reg [3:0] alucontrol
        );
        always @(*)
            case(aluop)
                2'b00:alucontrol<=3'b0010;
                2'b01:alucontrol<=3'b0011;
            default:case(funct)
                        6'b100000:alucontrol<=4'b0010;//ADD
                        6'b100001:alucontrol<=4'b0000;//ADDU
                        6'b100010:alucontrol<=4'b0011;//SUB
                        6'b100011:alucontrol<=4'b0001;//SUBU
                        6'b100100:alucontrol<=4'b0100;//AND
                        6'b100101:alucontrol<=4'b0101;//OR
                        6'b100110:alucontrol<=4'b0110;//XOR
                        6'b100111:alucontrol<=4'b0111;//NOR
                        6'b101010:alucontrol<=4'b1011;//SLT
                        6'b101011:alucontrol<=4'b1010;//SLTU
                        default:alucontrol<=4'bxxxx;
                    endcase
             endcase
    endmodule
    
    1. ALU
    module alu(
    //author:XJTU mry
            input [31:0] a,        //OP1
            input [31:0] b,        //OP2
            input [3:0] aluc,    //controller
            output [31:0] r,    //result
            output zero,
            output carry,
            output negative,
            output overflow);
            
        parameter Addu    =    4'b0000;    //r=a+b unsigned
        parameter Add    =    4'b0010;    //r=a+b signed
        parameter Subu    =    4'b0001;    //r=a-b unsigned
        parameter Sub    =    4'b0011;    //r=a-b signed
        parameter And    =    4'b0100;    //r=a&b
        parameter Or    =    4'b0101;    //r=a|b
        parameter Xor    =    4'b0110;    //r=a^b
        parameter Nor    =    4'b0111;    //r=~(a|b)
        //parameter Lui1    =    4'b1000;    //r={b[15:0],16'b0}
        //parameter Lui2    =    4'b1001;    //r={b[15:0],16'b0}
        parameter Slt    =    4'b1011;    //r=(a-b<0)?1:0 signed
        parameter Sltu    =    4'b1010;    //r=(a-b<0)?1:0 unsigned
        parameter Sra    =    4'b1100;    //r=b>>>a 
        parameter Sll    =    4'b1110;    //r=b<<a
        parameter Srl    =    4'b1101;    //r=b>>a
        
        parameter bits=31;
        parameter ENABLE=1,DISABLE=0;
        
        reg [32:0] result;
        wire signed [31:0] sa=a,sb=b;
        
        always@(*)begin
            case(aluc)
                Addu: begin
                    result=a+b;
                end
                Subu: begin
                    result=a-b;
                end
                Add: begin
                    result=sa+sb;
                end
                Sub: begin
                    result=sa-sb;
                end
                Sra: begin
                    if(a==0) {result[31:0],result[32]}={b,1'b0};
                    else {result[31:0],result[32]}=sb>>>(a-1);
                end
                Srl: begin
                    if(a==0) {result[31:0],result[32]}={b,1'b0};
                    else {result[31:0],result[32]}=b>>(a-1);
                end
                Sll: begin
                    result=b<<a;
                end
                And: begin
                    result=a&b;
                end
                Or: begin
                    result=a|b;
                end
                Xor: begin
                    result=a^b;
                end
                Nor: begin
                    result=~(a|b);
                end
                Sltu: begin
                    result=a<b?1:0;
                end
                Slt: begin
                    result=sa<sb?1:0;
                end
                //Lui1,Lui2: result = {b[15:0], 16'b0};
                default:
                    result=a+b;
            endcase
        end
        
        assign r=result[31:0];
        assign carry = result[32]; 
        assign zero=(r==32'b0)?1:0;
        assign negative=result[31];
        assign overflow=result[32];
    endmodule
    
    1. 数据存储器
    module RAM(
    //author:XJTU mry
    clk,address,data_in,data,we
        );
        parameter DataWidth=32;
        parameter AddrWidth=32;
        parameter DataDepth=32;
        input clk;
        input [AddrWidth-1:0] address;
    
        input we;
        input [DataWidth-1:0] data_in;
        output [DataWidth-1:0] data;
        
        reg [DataWidth-1:0] data_out;
        reg [DataWidth-1:0] men [0:DataDepth-1];
        assign data=(!we) ? data_out:8'bz;
        always @ (posedge clk)
        begin :MEM_WRITE
            if(we)begin
                men[address[6:2]]=data_in;
            end
        end
        
        always @ (posedge clk)
        begin :MEM_READ
            if(!we)begin
                data_out=men[address[6:2]];
            end
        end
    endmodule
    
    1. SHL2(图中右侧的那个)
    module Left2(data,odata);
    //author:XJTU mry
     input [31:0] data;
     output reg [31:0] odata;
    
     always @(data)
     begin
        odata[31:2]<=data[29:0];
      odata[1:0]<=2'b00;
     end
    endmodule
    
    1. Add(2)
    module ALU_Add_32(addr1,addr2,out_addr);
     input [31:0] addr1,addr2;
     output [31:0] out_addr;
     assign out_addr = addr1+addr2;
    endmodule
    
    1. 与操作模块
    module myand(
    //author:XJTU mry
    R,A,B
        );
        input A,B;
        output  R;
        assign R=A&B;
    endmodule
    
    1. SHL2(图中左侧的那个)
    module Left2_26(data,data1,odata);
    //author:XJTU mry
     input [25:0] data;
     input [3:0] data1;
     output reg [31:0] odata;
     
     always @(data,data1)
     begin
        odata[27:2]<=data[25:0];
      odata[1:0]<=2'b00;
      odata[31:28]<=data1[3:0];
     end
    endmodule
    

    连接各模块

    module CPU(
    clk,rst,out_ALU,Read_Data1,Read_Data2,Address_in_PC,Address_out_PC,Address_Add_PC,Order,Read_Data_Mem,Order_Left,zero
        );
         input clk;
         input rst;
         //output [6:0] data1,data2,data3,data4;
         output [31:0]out_ALU;
         output [31:0] Read_Data1,Read_Data2,Address_in_PC,Address_out_PC,Address_Add_PC,Order,Read_Data_Mem,Order_Left;
         output zero;
         wire [31:0] Read_Data1;
         wire [31:0] Read_Data2;
         wire [31:0] Address_in_PC,Address_out_PC;//用于PC的输入和输出
         wire [31:0] Address_Add_PC;//用于程序计数器正常指令进行加4的输出
         wire [31:0] Order;//用于指令存储器的指令输出
         wire RegDst,Jump,Branch,MemtoReg,ALUSrc,RegWrite,MemWrite;//用于对控制信号的连线
         wire [1:0] ALUop;//控制信号ALUop的连线
         wire [4:0] Write_register;//寄存器堆中写入寄存器所在的位号的连线
         wire [31:0] Write_Data;//Read_Data1,Read_Data2;//寄存器堆中的连线
         wire [31:0] sign,out_sign,ALU1,out_ALU;
         wire [31:0] ALU_Add_32_out,Branch_or_normal;
         wire [3:0] ALU_ctr;
         wire [31:0] Read_Data_Mem;
         wire [31:0] Order_Left;
         wire zero,Mux32_EN;
         wire carry,negative,overflow;
         
         //PC(CLK,Address_in_PC,Address_out_PC,rst);
         PC pc(.clk(clk),.reset(rst),.d(Address_in_PC),.q(Address_out_PC));
          //Add_PC(Address_out_PC,Address_Add_PC);
          PC_adder pc_adder(.adr(Address_out_PC),.n_adr(Address_Add_PC));
          //Order_Mem(Address_out_PC,Order);
          codeRAM coderam(.address(Address_out_PC),.data(Order));
          //Control_Unit(Order[31:26],RegDst,Jump,Branch,MemRead,MemtoReg,ALUop,MemWrite,ALUSrc,RegWrite);
          mainDec maindec(.op(Order[31:26]),.mentoreg(MemtoReg),.menwrite(MemWrite),.branch(Branch),.alusrc(ALUSrc),.regdst(RegDst),.regwrite(RegWrite),.jump(Jump),.aluop(ALUop));
          //Mux_6(Order[20:16],Order[15:11],RegDst,Write_register);
          MUX6 mux6(.A(Order[20:16]),.B(Order[15:11]),.Src(RegDst),.S(Write_register));
          //Registers(Order[25:21],Order[20:16],Write_register,Write_Data,Read_Data1,Read_Data2,RegWrite,CLK);
          RegisterUnit ru(.clk(clk),.we(RegWrite),.ra1(Order[25:21]),.ra2(Order[20:16]),.wa3(Write_register),.wd(Write_Data),.rd1(Read_Data1),.rd2(Read_Data2));
          //sign_ep(Order[15:0],sign);
          Extend16to32 extend16to32(.order(Order[15:0]),.addr(sign));
          //Mux_32(Read_Data2,sign,ALUSrc,ALU1);
          MUX32 mux32_1(.A(Read_Data2),.B(sign),.Src(ALUSrc),.S(ALU1));
          //ALU_Control(ALUop,Order[5:0],ALU_ctr);
          aluDec aludec(.funct(Order[5:0]),.aluop(ALUop),.alucontrol(ALU_ctr));
          //ALU(ALU_ctr,Read_Data1,ALU1,zero,out_ALU);
          alu ALU(.a(Read_Data1),.b(ALU1),.aluc(ALU_ctr),.r(out_ALU),.zero(zero),.carry(carry),.negative(negative),.overflow(overflow));
          //Data_Mem(out_ALU,Read_Data2,Read_Data_Mem,CLK,MemWrite,MemRead);
          RAM ram(.clk(clk),.address(out_ALU),.data_in(Read_Data2),.data(Read_Data_Mem),.we(MemWrite));
          //Mux_32(out_ALU,Read_Data_Mem,MemtoReg,Write_Data);
          MUX32 MUX32_2(.A(out_ALU),.B(Read_Data_Mem),.Src(MemtoReg),.S(Write_Data));
          Left2 left2(.data(sign),.odata(out_sign));
          ALU_Add_32 aluadd32(.addr1(Address_Add_PC),.addr2(out_sign),.out_addr(ALU_Add_32_out));
          myand myand_(.R(Mux32_EN),.A(Branch),.B(zero));
          //Mux_32(Address_Add_PC,ALU_Add_32_out,Mux32_EN,Branch_or_normal);
          MUX32 mux32_3(.A(Address_Add_PC),.B(ALU_Add_32_out),.Src(Mux32_EN),.S(Branch_or_normal));
          Left2_26 left2_26(.data(Order[25:0]),.data1(Address_Add_PC[31:28]),.odata(Order_Left));
          //Mux_32(Branch_or_normal,Order_Left,Jump,Address_in_PC);
          MUX32 mux32_4(.A(Branch_or_normal),.B(Order_Left),.Src(Jump),.S(Address_in_PC));
    endmodule
    

    测试

    1. 测试使用指令(已写入到指令寄存器)
      在这里插入图片描述

    2. 测试代码

    module sim_cpu(
    //author:XJTU mry
        );
        reg  clk;
        reg rst;
             //output [6:0] data1,data2,data3,data4;
        wire [31:0]out_ALU;
        wire [31:0] Read_Data1,Read_Data2,Address_in_PC,Address_out_PC,Address_Add_PC,Order,Read_Data_Mem,Order_Left;
        wire zero;
        CPU cpu(
        .clk(clk),
        .rst(rst),
        .out_ALU(out_ALU),
        .Read_Data1(Read_Data1),
        .Read_Data2(Read_Data2),
        .Address_in_PC(Address_in_PC),
        .Address_out_PC(Address_out_PC),
        .Address_Add_PC(Address_Add_PC),
        .Order(Order),
        .Read_Data_Mem(Read_Data_Mem),
        .Order_Left(Order_Left),
        .zero(zero)
        );
        always begin
            #10 clk=~clk;
        end
        initial begin
                clk=0;
                rst=0;
                #10;
                //clk=0;
                rst=1;
                #10;
                //clk=0;
                rst=0;
        end
    endmodule
    
    1. 测试结果
      仿真波形图如下,其中,Address_in_PC[31:0]为运行的指令地址(红色方框内容),Read_Data1[31:0]和Read_Data2[31:0]为读取的寄存器组Rs和Rt的值(蓝色方框内容)。因一次截取图片模糊不清,选择截取为2张图片。
      在这里插入图片描述
      在这里插入图片描述
    • 根据测试指令,PC的变化应该为:0->4->8->c->10->14->18->1c->20(跳转)->28->2c->30->34->38->3c(跳转)->44
      根据红色方框内容,PC的变化与预测相同。
    • 逐个考察蓝色方框内容,分析各个时钟周期内Rs和Rt所对应寄存器的值,与预期相同。
    • 运行完上述指令后,寄存器组的值如下图,与预期结果相同。
      在这里插入图片描述
    • 运行完上述指令后,数据寄存器的值如下图,与预期结果相同。
      在这里插入图片描述
    更多相关内容
  • 单周期CPU设计verilog

    2017-05-10 22:42:35
    单周期CPU设计verilog,课程设计
  • 单周期CPU设计,使用结构级语句与描述级语句构建寄存器堆、ALU、CONUNIT等模块,支持12条指令:add、sub、j、bne、bnq等
  • single_period_CPU_opt.zip 内部完整代码 仿真测试都通过
  • • 用硬件描述语言(Verilog设计MIPS CPU,支持如下指令集 • ISA1 = {ADD/ADDU/SUB/SUBU/SLL/SRL/SRA/SLLV/SRLV/SRAV/AND/OR/XOR/NOR/ SLT/SLTU/ADDI/ADDIU/ANDI/ORI/XORI/LUI/SLTI/SLTIU/LB/LBU/LH/LH U/LW/SB/...
  • 单周期cpu代码.zip

    2020-06-22 19:13:04
    单周期cpu代码.zip
  • 单周期CPU设计Verilog

    千次阅读 2021-02-23 21:20:13
    第一章 单周期CPU设计原理 为实现单周期CPU,本文首先研究了单周期CPU的相关理论和工作原理。本章首先简要介绍单周期CPU的基本概念,然后对CPU的工作原理进行简要分析,以便为单周期CPU设计和开发提供理论基础...

    第一章 单周期CPU的设计原理

    1.1 单周期CPU概述

    1.2 CPU工作原理

    第二章 单周期CPU的设计内容

    2.1 指令系统的设计

    2.1.1 概述

    2.1.2 运算类指令的设计

    2.1.3 传送类指令的设计

    2.1.4 存储类指令的设计

    2.1.5 控制类指令的设计

    2.2 整体框架的设计

    2.3 数据通路的设计

    2.4 控制信号的设计

    第三章 单周期CPU的具体实现

    3.1 底层模块的实现

    3.1.1 程序计数器PC

    3.1.2 指令存储器InstructionMemory

    3.1.3 寄存器组RegisterFile

    3.1.4 算术逻辑单元ALU

    3.1.5 数据存储器DataMemory

    3.1.6 控制单元ControlUnit

    3.2 顶层模块的实现

    第四章 单周期CPU的仿真验证

    4.1 测试程序

    4.2 波形仿真


    第一章 单周期CPU的设计原理

    为实现单周期CPU,本文首先研究了单周期CPU的相关理论和工作原理。本章首先简要介绍单周期CPU的基本概念,然后对CPU的工作原理进行简要分析,以便为单周期CPU的设计和开发提供理论基础。

    1.1 单周期CPU概述

    中央处理器,即CPU,作为计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元。在CPU内部,电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成。

    1.2 CPU工作原理

    CPU在处理指令时,一般需要经过以下几个步骤:

    (1) 取指令(IF):根据程序计数器PC中的指令地址,从存储器中取出一条指令,同时,PC根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入PC,当然得到的“地址”需要做些变换才送入PC。

    (2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。

    (3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。

    (4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。

    (5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。

    CPU的指令处理过程如下:

     

    单周期CPU,是在一个时钟周期内完成这五个阶段的处理。

    第二章 单周期CPU的设计内容

    2.1 指令系统的设计

    2.1.1 概述

    本文所设计的单周期CPU的指令系统采用类似MIPS的设计风格,包括以下四类指令:

    (1) 运算类指令;

    (2) 传送类指令;

    (3) 存储类指令;

    (4) 控制类指令;

    其中,所有指令的操作码部分用4位二进制表示,寄存器编号用3位二进制表示。在下述的具体设计表示中,以助记符表示的是汇编指令;以代码表示的则是二进制机器指令。

    2.1.2 运算类指令的设计

    运算类指令共有8条,具体的指令功能与指令格式如下:

    (1) add rd,rs,rt

    0000

    rs(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:将寄存器rs和rt中的值相加并将结果写回寄存器rd。reserved为预留部分,即未用,一般填“0”。

    (2) addi rt,rs,immediate

    0001

    rs(3位)

    rt(3位)

    immediate(6位)

    功能:将立即数immediate进行符号扩展后再和寄存器rs中的值相加并将结果写回寄存器rt。

    (3) sub rd,rs,rt

    0010

    rs(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:用寄存器rs中的值减去rt中的值并将结果写回寄存器rd。reserved为预留部分,即未用,一般填“0”。

    (4) or rd,rs,rt

    0011

    rs(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:将寄存器rs和rt中的值进行按位或运算并将结果写回寄存器rd。reserved为预留部分,即未用,一般填“0”。

    (5) ori rt,rs,immediate

    0100

    rs(3位)

    rt(3位)

    immediate(6位)

    功能:将立即数immediate进行“0”扩展后再和寄存器rs中的值进行按位或运算并将结果写回寄存器rt。

    (6) and rd,rs,rt

    0101

    rs(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:将寄存器rs和rt中的值进行按位与运算并将结果写回寄存器rd。reserved为预留部分,即未用,一般填“0”。

    (7) sll rd,rt,sa

    0110

    未用(3位)

    rt(3位)

    rd(3位)

    sa(3位)

    功能:先将sa表示的立即数进行“0”拓展,然后将寄存器rt中的值左移sa位并将结果写回寄存器rd。

    (8) slt rd,rs,rt

    0111

    rs(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:将寄存器rs和rt中的值作为带符号数进行比较,若rs中的值更小,则将寄存器rd中的值置1,否则置0。

    2.1.3 传送类指令的设计

    传送类指令共有2条,具体的指令功能与指令格式如下:

    (1) mov rd,rt

    1000

    未用(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:将寄存器rt中的值传送到寄存器rd。reserved为预留部分,即未用,一般填“0”。

    (2) movi rt,immediate

    1001

    未用(3位)

    rt(3位)

    immediate(6位)

    功能:将立即数immediate进行符号扩展后传送到寄存器rt。

    2.1.4 存储类指令的设计

    存储类指令共有2条,具体的指令功能与指令格式如下:

    (1) sw  rt,immediate(rs)

    1010

    rs(3位)

    rt(3位)

    immediate(6位)

    功能:将寄存器rt中的值存储到内存中,存储单元的地址为寄存器rs中的值加上经符号拓展的立即数immediate。

    (2) lw  rt,immediate(rs)

    1011

    rs(3位)

    rt(3位)

    immediate(6位)

    功能:将内存中的值加载到寄存器rt中,存储单元的地址为寄存器rs中的值加上经符号拓展的立即数immediate。

    2.1.5 控制类指令的设计

    控制类指令共有4条,具体的指令功能与指令格式如下:

    (1) beq  rs,rt,immediate

    1100

    rs(3位)

    rt(3位)

    immediate(6位)

    功能:比较寄存器rs和rt中的值,若相等则跳转到目标处执行,否则继续执行下一条指令。跳转的目标地址与当前指令的下一条指令的相对位移由立即数immediate给出。

    (2) bgtz  rs,immediate

    1101

    rs(3位)

    000

    immediate(6位)

    功能:判断寄存器rs中的值是否大于0,若大于0则跳转到目标处执行,否则继续执行下一条指令。跳转的目标地址与当前指令的下一条指令的相对位移由立即数immediate给出。

    (3) j  addr

    1110

    addr(12位)

    功能:执行无条件跳转,跳转的目的地址组成如下:高3位为PC中值的高3位,第1-12位由addr给出,最低位为0。

    (4) halt

    1111

    000000000000(12位)

    功能:停机指令,不改变PC的值,PC保持不变。

    2.2 整体框架的设计

    本文所设计的单周期CPU的整体框架主要包括七部分:程序计数器、指令寄存器、寄存器组、算术逻辑单元、数据存储器、控制单元和顶层模块。具体框架如下:

    2.3 数据通路的设计

    本文所设计的单周期CPU的数据通路如下:

    其中Ins.MEM为指令存储器,Data.MEM为数据存储器。访问存储器时,先给出内存地址,然后由读或写信号控制操作。对于寄存器组Register File,先给出寄存器编号,读操作时,输出端就直接输出相应数据;而在写操作时,当写使能信号为1时,在时钟边沿触发将数据写入寄存器。

    主要模块接口说明如下:

    (1) Instruction Memory:指令存储器。

    IAddr,指令存储器地址输入端口

    IDataOut,指令存储器数据输出端口(指令代码输出端口)

    InsMemRW,指令存储器读写控制信号,为0写,为1读

    (2) Data Memory:数据存储器。

    DAddr,数据存储器地址输入端口

    DataIn,数据存储器数据输入端口

    DataOut,数据存储器数据输出端口

    RD,数据存储器读控制信号,为0读

    WR,数据存储器写控制信号,为0写

    (3) Register File:寄存器组。

    Read Reg1,rs寄存器地址输入端口

    Read Reg2,rt寄存器地址输入端口

    Write Reg,将数据写入的寄存器端口,其地址来源为rt或rd字段

    Write Data,写入寄存器的数据输入端口

    Read Data1,rs寄存器数据输出端口

    Read Data2,rt寄存器数据输出端口

    WE,写使能信号,为1时,在时钟边沿触发写入

    (4) ALU:算术逻辑单元。

    A,操作数A输入端口

    B,操作数B输入端口

    result,ALU运算结果

    zero,运算结果标志,结果为0,则zero=1;否则zero=0

    sign,运算结果标志,结果最高位为0,则sign=0,正数;否则,sign=1,负数

    2.4 控制信号的设计

    本文所设计的单周期CPU的各控制信号如下:

     各控制信号的功能如下:

    控制信号名

    状态“0”

    状态“1”

    Reset

    初始化PC为0

    PC接收新地址

    PCWre

    PC不更改,相关指令:halt

    PC更改,相关指令:除指令halt外

    ALUSrcA

    来自寄存器堆data1输出,相关指令:add、addi、sub、or、ori、and、slt、mov、movi、beq、bgtz、sw、lw

    来自经过“0”拓展的移位数sa,即 {{13{0}},sa},相关指令:sll

    ALUSrcB

    来自寄存器堆data2输出,相关指令:add、sub、or、and、sll、slt、mov、beq、bgtz

    来自sign或zero扩展的立即数,相关指令:addi、ori、movi、sw、lw

    DBDataSrc

    来自ALU运算结果的输出,相关指令:add、addi、sub、or、ori、and、sll、slt、mov、movi

    来自数据存储器(Data MEM)的输出,相关指令:lw

    RegWre

    寄存器组写不使能,相关指令:sw、beq、bgtz、j、halt

    寄存器组写使能,相关指令:add、addi、sub、or、ori、and、sll、slt、mov、movi、lw

    InsMemRW

    写指令存储器

    读指令存储器

    RD

    读数据存储器,相关指令:lw

    无操作

    WR

    写数据存储器,相关指令:sw

    无操作

    RegDst

    写寄存器组寄存器的地址,来自rt字段,相关指令:addi、ori、movi、lw

    写寄存器组寄存器的地址,来自rd字段,相关指令:add、sub、or、and、sll、slt、mov

    ExtSel

    (zero-extend)immediate0扩展),相关指令:ori

    (sign-extend)immediate符号扩展,相关指令:addi、movi、sw、lw、beq、bgtz

    PCSrc[1..0]

    00:pc<-pc+2,相关指令:add、addi、sub、or、ori、and、sll、slt、sw、lw、beq(zero=0)、bgtz(sign=1,或zero=1);

    01:pc<-pc+2+(sign-extend)immediate,相关指令:beq(zero=1)、bgtz(sign=0,zero=0);

    10:pc<-{(pc+2)[15..13],addr[12..1],0},相关指令:j;

    11:未用

    ALUOp[2..0]

    ALU 8种运算功能选择(000-111),具体见ALU功能表

    ALU运算功能表如下:

    ALUOp[2..0]

    功能

    描述

    000

    Y = A+B

    001

    Y = A-B

    010

    Y = B<<A

    B左移A位

    011

    Y = A∨B

    100

    Y = A∧B

    101

    Y=(A<B)?1: 0

    比较A与B

    不带符号

    110

    if (A<B && (A[15] == B[15] ) )

        Y = 1;

    else if ( A[15] && !B[15)  Y = 1;

    else Y = 0; 

    比较A与B

    带符号

    111

    Y = B

    直送

    在具体进行控制时,PC的改变是在时钟上升沿进行的,指令执行的结果总是在时钟下降沿保存到寄存器和存储器中,这样稳定性较好。

    第三章 单周期CPU的具体实现

    3.1 底层模块的实现

    单周期CPU每个时钟周期可被划分为五个阶段,对应的有五个最关键的底层模块:IF阶段对应PC,ID阶段对应InstructionMemory和RegisterFile,EXE阶段对应ALU,MEM阶段对应DataMemory,WB阶段也对应RegisterFile。除此之外,还需要生成控制信号的控制单元(ControlUnit)等。下面具体介绍上述各主要模块的设计与实现。

    3.1.1 程序计数器PC

    PC为时序逻辑,在时钟上升沿到来时,若PCWre为1,则输出下一条待处理的指令地址,否则不输出新的指令地址。若Reset为0,则设置输出的指令地址为0。具体代码如下:

    module PC(    
        input clk,    
        input [15:0] PCin,    
        input PCWre,    
        input Reset,    
        output reg [15:0] PCout    
    );    
          
        initial begin    
            PCout <= 0;   
        end  
            
        always@(posedge clk) begin    
            if(Reset == 0) begin    
                PCout <= 0;    
            end    
            else if(PCWre == 0) begin    
                PCout <= PCout;    
            end    
            else begin    
                PCout <= PCin;    
            end    
        end  
            
    endmodule    
    

    3.1.2 指令存储器InstructionMemory

    InstructionMemory为组合逻辑,内含128个字节的mem。mem中存放要执行的测试程序段的机器码,使用initial语句中的$readmemb伪指令从Instructions.txt文件中读取出来。当输入一个16位长的指令地址,输出对应地址中的16位机器指令。具体代码如下:

    module InsMemory(    
        input InsMemRW,    
        input [15:0] address,    
        output reg [15:0] DataOut    
    );    
    
        reg [7:0] mem [0:127];   
    
        initial begin    
            DataOut = 16'b1111000000000000;  
            $readmemb("Instructions.txt", mem);      
        end   
          
        always@(*) begin   
            DataOut[15:8] <= mem[address];    
            DataOut[7:0] <= mem[address+1];  
        end   
           
    endmodule    
    

    3.1.3 寄存器组RegisterFile

    RegisterFile中定义了一个含有8个寄存器的寄存器组,设计逻辑包括两部分:一方面,读寄存器组为组合逻辑,当输入一个3位长的寄存器编号时,输出对应寄存器中的数值。另一方面,写寄存器组是时序逻辑,在时钟下降沿到来时触发写入。具体代码如下:

    module RegFile(    
        input CLK,    
        input RST,    
        input RegWre,    
        input [2:0] ReadReg1,    
        input [2:0] ReadReg2,    
        input [2:0] WriteReg,    
        input [15:0] WriteData,    
        output [15:0] ReadData1,    
        output [15:0] ReadData2    
    );    
      
        reg [15:0] regFile[0:7];  
           
        integer i;    
          
        assign ReadData1 = regFile[ReadReg1];   
        assign ReadData2 = regFile[ReadReg2];  
            
        always @ (negedge CLK) begin    
            if (RST == 0) begin    
                for(i=1;i<8;i=i+1)    
                    regFile[i] <= 0;    
            end    
            else if(RegWre == 1 && WriteReg != 0) begin    
                regFile[WriteReg] <= WriteData;    
            end    
        end    
                
    endmodule    
    

    3.1.4 算术逻辑单元ALU

    ALU为组合逻辑,根据控制信号对两个操作数进行相应的运算,并输出结果。具体代码如下:

    module ALU(    
        input [2:0] ALUopcode,    
        input [15:0] rega,    
        input [15:0] regb,    
        output reg [15:0] result,    
        output zero,    
        output sign    
    );    
      
        assign zero = (result==0)?1:0;    
        assign sign = result[15];    
          
        always @( ALUopcode or rega or regb ) begin    
            case (ALUopcode)    
                3'b000 : result = rega + regb;    
                3'b001 : result = rega - regb;    
                3'b010 : result = regb << rega;    
                3'b011 : result = rega | regb;    
                3'b100 : result = rega & regb;    
                3'b101 : result = (rega < regb)?1:0;     
                3'b110 : begin    
                    if (rega<regb &&(( rega[15] == 0 && regb[15]==0) ||    
                    (rega[15] == 1 && regb[15]==1))) result = 1;    
                    else if (rega[15] == 0 && regb[15]==1) result = 0;    
                    else if ( rega[15] == 1 && regb[15]==0) result = 1;    
                    else result = 0;    
                end    
                3'b111 : result = regb;    
            endcase    
        end   
           
    endmodule    
    

    3.1.5 数据存储器DataMemory

    DataMemory中包含128个字节的ram,设计逻辑包括两部分:一方面,读存储器为组合逻辑,当输入一个16位长的数据地址时,输出对应地址中的16位数据。另一方面,写存储器是时序逻辑,在时钟下降沿到来时触发写入。具体代码如下:

    module DataMemory(    
        input clk,    
        input [15:0] address,    
        input RD,    
        input WR,    
        input [15:0] DataIn,    
        output [15:0] DataOut    
    );    
        
        reg [7:0] ram[0:127];  
            
        integer i;    
          
        initial begin;    
            for(i=0;i<128;i=i+1)    
                ram[i]<=0;    
        end    
           
        assign DataOut[7:0] = (RD == 0)? ram[address+1]:8'bz;    
        assign DataOut[15:8] = (RD == 0)? ram[address]:8'bz;    
       
        always@(negedge clk) begin    
            if(WR == 0) begin    
                if(address>=0 && address<128) begin    
                    ram[address] <= DataIn[15:8];    
                    ram[address+1] <= DataIn[7:0];     
                end    
            end    
        end  
            
    endmodule    
    

    3.1.6 控制单元ControlUnit

    ControlUnit为组合逻辑,将机器码中的操作码(opcode)转换为各个控制信号,从而控制不同的指令在不同的数据通路中传输。具体代码如下:

    module ControlUnit(    
        input [3:0] opcode,    
        input zero,    
        input sign,    
        output reg PCWre,    
        output reg ALUSrcA,    
        output reg ALUSrcB,    
        output reg DBDataSrc,    
        output reg RegWre,    
        output reg InsMemRW,    
        output reg RD,    
        output reg WR,    
        output reg RegDst,    
        output reg ExtSel,    
        output reg [1:0] PCSrc,    
        output reg [2:0] ALUOp    
    );    
      
        initial begin    
            RD = 1;    
            WR = 1;    
            RegWre = 0;   
            PCWre = 0;   
            InsMemRW = 1;    
        end   
           
        always@ (opcode) begin    
            case(opcode)     
                4'b0000:begin // add    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b000;    
                end    
                4'b0001:begin //addi    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 1;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 0;    
                    ExtSel = 1;    
                    ALUOp = 3'b000;    
                end    
                4'b0010:begin //sub    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b001;    
                end  
                4'b0011:begin // or    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b011;    
                end     
                4'b0100:begin // ori    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 1;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 0;    
                    ExtSel = 0;    
                    ALUOp = 3'b011;    
                end    
                4'b0101:begin //and    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b100;    
                end    
                4'b0110:begin //sll    
                    PCWre = 1;    
                    ALUSrcA = 1;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b010;    
                end    
                4'b0111:begin //slt    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b110;    
                end  
                4'b1000:begin //mov    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ExtSel = 1;    
                    ALUOp = 3'b111;    
                end  
                4'b1001:begin //movi    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 1;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 0;    
                    ExtSel = 1;    
                    ALUOp = 3'b111;    
                end      
                4'b1010:begin //sw    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 1;    
                    RegWre = 0;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 0;    
                    ExtSel =1;    
                    ALUOp = 3'b000;    
                end    
                4'b1011:begin //lw    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 1;    
                    DBDataSrc = 1;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 0;    
                    WR = 1;    
                    RegDst = 0;    
                    ExtSel = 1;    
                    ALUOp = 3'b000;    
                end   
                4'b1100:begin //beq    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    RegWre = 0;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    ExtSel = 1;    
                    ALUOp = 3'b001;    
                end    
                4'b1101:begin  //bgtz  
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    RegWre = 0;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    ExtSel = 1;    
                    ALUOp = 3'b001;    
                end    
                4'b1110:begin //j    
                    PCWre = 1;    
                    RegWre = 0;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    ALUOp = 3'b010;    
                end    
                4'b1111:begin //halt    
                    PCWre = 0;    
                    RegWre = 0;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                end    
                default:begin    
                    RD = 1;    
                    WR = 1;    
                    RegWre = 0;    
                    InsMemRW = 0;    
                end    
            endcase    
        end   
           
        always@(opcode or zero or sign) begin    
            if(opcode == 4'b1110) // j    
                PCSrc = 2'b10;    
            else if(opcode == 4'b1100) begin  
                if(zero == 1)  
                    PCSrc = 2'b01;  
                else  
                    PCSrc = 2'b00;  
            end  
            else if(opcode == 4'b1101) begin  
                if(zero == 0 && sign == 0)    
                    PCSrc = 2'b01;    
                else    
                    PCSrc = 2'b00;  
            end    
            else begin    
                PCSrc = 2'b00;    
            end    
        end   
           
    endmodule    
    

    3.2 顶层模块的实现

    在顶层模块中,通过实例化各个底层模块,并使用导线将它们按照数据通路图连接起来,构成单周期CPU的完整结构,具体原理图如下:

    第四章 单周期CPU的仿真验证

    根据上述实现的单周期CPU,利用指令系统中所有的指令设计一个测试程序,对该CPU进行如下仿真验证。

    4.1 测试程序

    使用该指令系统中所有指令,设计出如下一段具有实际意义的测试程序。该程序计算数字1到5的和以及按位或的和,并将两者中较大者存储到内存中地址为14的存储单元中,最后读出该存储单元中的值。具体指令序列如下:

    地址

    汇编程序

    机器指令代码(二进制)

    0x0000

    movi     $0,0

    10010000 00000000

    0x0002

    mov      $1,$0

    10000000 00001000

    0x0004

    and      $3,$1,$0

    01010010 00011000

    0x0006

    addi      $2,$0,6

    00010000 10000110

    0x0008

    ori      $3,$0,1

    01000000 11000001

    0x000A

    add      $5,$0,$3

    00000000 11101000

    0x000C

    or       $6,$1,$3

    00110010 11110000

    0x000E

    mov      $0,$5

    10000001 01000000

    0x0010

    mov      $1,$6

    10000001 10001000

    0x0012

    addi      $4,$3,1

    00010111 00000001

    0x0014

    mov      $3,$4

    10000001 00011000

    0x0016

    beq      $3,$2,1

    11000110 10000001

    0x0018

    j         5

    11100000 00000101

    0x001A

    slt      $5,$0,$1

    01110000 01101000

    0x001C

    movi     $4,4

    10010001 00000100

    0x001E

    sub     $6,$2,$4

    00100101 00110000

    0x0020

    sll       $7,$6,1

    01100001 10111001

    0x0022

    bgtz      $5,2

    11011010 00000010

    0x0024

    sw      $0,10($7)

    10101110 00001010

    0x0026

    j         21

    11100000 00010101

    0x0028

    sw      $1,10($7)

    10101110 01001010

    0x002A

    lw       $6,10($7)

    10111111 10001010

    0x002C

    halt

    11110000 00000000

    4.2 波形仿真

    在仿真过程中,设置如下输入和输出,并显示如下寄存器中的内容:

    输入

    clk

    CPU时钟信号,周期为10ns

    reset

    CPU复位信号,0为复位

    输出

    nowIns

    当前指令

    nextPC

    下一条指令地址

    nowPC

    当前PC地址

    ALUSrcA

    ALU操作数A

    ALUSrcB

    ALU操作数

    ALUResult

    ALU运算结果

    finalData

    写回的结果

    寄存器

    RegFile[0]-RegFile[7]

    寄存器组

    DataMemory[10]-DataMemory[15]

    数据存储器

    仿真结果如下:

    (1) movi $0,0至ori $3,$0,1

    (2) add $5,$0,$3至j 5 (第一轮循环)

    (3) add $5,$0,$3至j 5 (第二轮循环)

    (4) add $5,$0,$3至j 5 (第三轮循环)

    (5) add $5,$0,$3至j 5 (第四轮循环)

    (6) add $5,$0,$3至beq $3,$2,1 (第五轮循环)

    (7) slt $5,$0,$1至sll $7,$6,1

    (8) bgtz $5,2至halt

     

    展开全文
  • 单周期的整个项目,在电脑上安装vivado即可添加项目,我个人使用的是15版的。另外需要看波形图的,点击仿真,调节相关参数即可
  • 《计算机组成原理》单周期CPU处理器的设计一、实验原理1. 单周期CPU2. 单周期CPU指令模块3. MIPS指令格式4. 指令处理流程5. 单周期CPU数据通路二、单周期CPU设计1.pcadd42. PC3.INSTMEM4. DATAMEM5. DATAMEM三、...


    一、实验原理

    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

    在这里插入图片描述
    pcadd4用来作为PC寄存器的更新信号。
    由于每条指令32位,所以增加一个32位加法器,固定与32位的立即数4进行相加,且得到的结果在当前时钟信号的上升沿更新进PC寄存器。
    其中,In为输入端口,输入当前指令地址;Out为下一条指令地址,为输出端口。

    ① PCADD4

    module PCadd4(PC_o,PCadd4);
        input [31:0] PC_o;//偏移量
        output [31:0] PCadd4;//新指令地址
        CLA_32 cla32(PC_o,4,0, PCadd4, Cout);
    endmodule
    

    ② CLA_32

    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

    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
    

    2. PC

    在这里插入图片描述

    PC寄存器用来给出指令在指令储存器中的地址。
    为实现稳定输出,在时钟信号的上升沿更新,而且需要一个控制信号,在控制信号为0的时候初始化PC寄存器,即全部置零。
    其中,Clk为输入信号,时钟周期;Reset为输入信号,输入控制信号;Result为输入信号,输入目标地址,可能是跳转地址或者是下一条指令的地址;Address为输出信号,输出指令地址。

    代码如下:

    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
    

    3.INSTMEM

    在这里插入图片描述
    依据当前pc,读取指令寄存器中相对应地址Addr[6:2]的指令。
    将pc的输入作为敏感变量,当pc发生改变的时候,则进行指令的读取,根据相关的地址,输出指令寄存器中相对应的指令,且在设计指令的时候,要用到12条给出的指令且尽量合理。
    Addr为输入信号,输入指令地址;Inst为输出信号,输出指令编码。

    代码如下:

    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
    

    4. DATAMEM

    在这里插入图片描述
    数据存储器,通过控制信号,对数据寄存器进行读或者写操作,并且此处模块额外合并了输出DB的数据选择器,此模块同时输出写回寄存器组的数据DB。
    由于需要支持取数/存数指令,所以要在指令储存器的基础上增加写入数据的数据写入端口,写使能信号。又因为写操作在时钟信号的上升沿,所以要增加时钟信号。
    在这里插入图片描述
    代码如下:

    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
    

    5. SHIFTER32_L2

    一个固定左移两位的移位器。
    使用32位移位器SHIFTER32,固定左移两位即可。
    X:指令中的偏移量,输入信号;
    Sh:偏移左移后的结果,输出信号

    代码如下:

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

    6. SHIFTER_COMBINATION

    在这里插入图片描述
    J指令中用以产生跳转的目标地址。
    跳转的目标地址采用拼接的方式形成,最高4位为PC+4的最高4位,中间26位为J型指令的26位立即数字段,最低两位为0。
    X:指令编码的低26位字段,输入信号。
    PCADD4:PC+4的32位字段,输入信号。
    Y:32位转移目标地址,输出信号。

    代码如下:

    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
    
    

    7. MUX4X32

    在这里插入图片描述
    实现目标地址的选择。
    目标地址可能是PC+4,也可能是beq和bne的跳转地址或是J型跳转地址,所以采用一个32位四选一多路选择器。
    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)
                    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
    

    8. MUX2X5

    在这里插入图片描述
    R型指令和I行指令的Wr信号不同,所以需要一个5位二选一选择器进行选择。
    R型指令Wr选择rd信号,I型指令Wr选择rt信号。
    A0:R型指令的rd信号,输入信号
    A1:I型指令的rt信号,输入信号
    S:选择指令的控制信号,输入信号
    Y: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)
                    1'b0:select=A0;
                    1'b1:select=A1;
            endcase
        endfunction
        assign Y=select(A0,A1,S);
    endmodule
    

    9. EXT16T32

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

    代码如下:

    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
    

    10. MUX2X32

    在这里插入图片描述
    模块1的功能
    ALU的Y端输入信号种类根据指令的不同而不同。
    在执行R型指令时,ALU的Y端输入信号可能来自Qb,在执行I型指令的addi,andi和ori指令时时,ALU的Y端输入信号来自EXT16T32,所以需要一个二选一选择器。
    A0:来自EXT16T32的信号,输入信号。
    A1:来自REGFLE中Qb端口的信号,输入信号。
    S:控制信号。
    Y:输入ALU进行后续计算的信号,输出信号。

    模块2的功能
    在lw指令中,需要将DATAMEM中选中储存器的值保存到REGFILE的寄存器中,而其他会更新REGFILE的指令的更新信号来自于ALU的R输出端。所以需要一个二选一选择器进行选择。
    A0: DATAMEM的输出值,输入信号
    A1: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)
                1'b0:select=A0;
                1'b1:select=A1;
            endcase
        endfunction
        assign Y = select(A0,A1,S);
    endmodule
    

    11.CONUNIT

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

    控制信号
    在这里插入图片描述
    代码如下:

    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
    

    12.REGFILE

    在这里插入图片描述
    给出要读取的两个寄存器编号和要写入的寄存器编号,然后由Qa和Qb端口更新Ra和Rb端口的输入编号分别输入其值。
    由32个寄存器组成,增加两个端口用于接收要读取的两个寄存器编号,另一个端口用于接收要写入的寄存器的编号。且在时钟上升沿将D写入。
    Clk:时钟周期,输入信号。
    21
    Clrn:清零信号,输入信号。
    D:寄存器更新值,输入信号。
    Ra:读取寄存器编号1,输入信号。
    Rb:读取寄存器编号2或立即数,输入信号。
    We:写入寄存器编号3,输入信号。
    Wr:写使能信号,为0的时候不能写入,D值不更新,为1的时候能写入,D值更新,输入信号。
    Qa:输出寄存器1的值,输入信号。
    Qb:输出寄存器2的值,输入信号。

    代码如下:

    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
    

    13. ALU

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

    代码如下:

    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
    

    14.CPU

    在这里插入图片描述
    实现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
    

    15.Test测试代码

    module test();
    reg Clk,Reset;
    wire [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
    wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,X,Y,Dout,mux4x32_2,R,D1;
    wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem,shift,j;
    
    wire [3:0]Aluc;
    wire [1:0]Pcsrc;
    wire [4:0]Wr,Wr1;
    initial begin
    Clk=0;
    Reset=0;
    #5
    Reset<=1;
    end
    always #5 Clk=~Clk;
    
    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
    

    16.其他模块的代码

    由于篇幅有限,因此其模块代码上传至百度网盘,链接如下:
    https://pan.baidu.com/s/1Ax3WyUybfBT8028JHug2RQ
    提取码:jxlx


    三、设计运行结果

    具体“调制”过程省略。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 基于Verilog单周期和多周期CPU设计

    千次阅读 2022-03-12 21:06:23
    基于Verilog单周期和多周期CPU设计,数据通路采用分散互联,控制器采用硬布线方式实现 原理方面如各条指令的流程和控制信号的内容可参考相关教材,在此不进行介绍,仅介绍核心组件的实现方法 模拟采用的是modelsim...

    基于Verilog的单周期和多周期CPU设计,数据通路采用分散互联,控制器采用硬布线方式实现
    原理方面如各条指令的流程和控制信号的内容可参考相关教材,在此不进行介绍,仅介绍核心组件的实现方法
    模拟采用的是modelsim,指令集参考的是MIPS32的标准指令集,实现的十条指令格式皆取自其中
    10条指令内容:
    在这里插入图片描述

    单周期

    总体结构

    • 数据通路

    在这里插入图片描述

    分散互联,CU采用组合逻辑+硬布线实现

    组件实现

    ALU

    根据ALUctl信号对输入的A和B进行计算,若结果为零则Zero输出高电平

    module ALU (ALUctl,A,B,ALUout,Zero);
    
    input [2:0] ALUctl;
    input [31:0] A,B;
    output reg [31:0] ALUout;
    output Zero;
    
    assign Zero=(ALUout==0);
    
    always @(ALUctl,A,B)
    begin
    	case(ALUctl)
    		3'b000:ALUout<=A+B;	//add
    		3'b001:ALUout<=A-B;	//sub
    		3'b010:ALUout<=A+B;	//addu
    		3'b011:ALUout<=A&B;	//and
    		3'b100:ALUout<=A|B;	//or
    		3'b101:ALUout<=~(A|B);	//nor
    		default:ALUout<=0;
    	endcase
    end
    
    endmodule
    

    ALU_CU

    根据ALUOp信号输出ALUctl

    • 00->addu
    • 01->sub
    • 10->由FuncCode决定,根据MIPS32指令输出对应信号
    module ALU_CU(ALUop,FuncCode,Out);
    
    input [1:0] ALUop;
    input [5:0] FuncCode;
    output reg [2:0] Out;
    /*
    add	000
    sub	001
    addu	010
    and	011
    or	100
    nor	101
    */
    always @*
    case(ALUop)
    	2'b00:Out=3'b010;	//addu
    	2'b01:Out=3'b001;	//sub
    	2'b10:
    	begin
    		case(FuncCode)
    			6'b100000:Out=3'b000;	//add
    			6'b100010:Out=3'b001;	//sub
    			6'b100001:Out=3'b010;	//addu
    			6'b100100:Out=3'b011;	//and
    			6'b100101:Out=3'b100;	//or
    			6'b100111:Out=3'b101;	//nor	
    			default:Out=3'b111;
    		endcase
    	end
    	default:Out=3'b111;
    endcase
    
    endmodule
    

    RF

    具体输入输出如下,其中RegWr为写使能,当其有效时,在clock上升沿将数据写入寄存器

    为保证测试,第一个寄存器为基址寄存器,并初始化了基址为0

    module reg_file(Read1,Read2,WriteReg,WriteData,RegWr,Data1,Data2,clock);
    
    input [4:0] Read1,Read2,WriteReg;	//reg number
    input [31:0] WriteData;	//data to write
    input RegWr;	//write control
    input clock;	//clock for write
    output [31:0] Data1,Data2;	//data to read
    
    reg [31:0] RF [31:0];	//32 reg(32bit)
    
    //测试用,保存基址
    initial
    begin
    	RF[0]=0;
    end
    
    assign Data1=RF[Read1];
    assign Data2=RF[Read2];
    
    always
    begin
    	@(posedge clock) if(RegWr) RF[WriteReg]<=WriteData;
    end
    
    endmodule
    

    指令存储器

    使用寄存器堆模拟,为了测试根据MIPS32指令集初始化了一些指令,并且为了保证PC+4的地址正确,对输入地址进行了转换以对应寄存器序号,具体测试代码见测试部分

    module mem_ins(addr,ins);
    
    input [31:0] addr;
    output [31:0] ins;
    
    reg [31:0] mem [1023:0];
    
    //测试用
    initial
    begin
    mem[0]=32'b100011_00000_00100_00000000_00000011; //lw
    mem[1]=32'b100011_00000_00001_00000000_00000001; //lw
    mem[2]=32'b100011_00000_00010_00000000_00000010; //lw
    mem[3]=32'b100011_00000_00011_00000000_00000100; //lw
    mem[4]=32'b000100_00010_00011_00000000_00000010; //beq
    mem[5]=32'b000000_00010_00001_00010_00000_100000; //add
    mem[6]=32'b000010_00_0001_0000_0000_0000_0000_0100; //j
    mem[7]=32'b101011_00000_00010_00000000_00000011; //sw
    mem[8]=32'b000010_00_0001_0000_0000_0000_0000_0000; //j
    end
    
        assign ins=mem[((addr-32'h00400000)>>2)];	//转换输入地址为寄存器序号
    
    endmodule
    

    数据存储器

    使用寄存器模拟,由于未实现立即数指令,为保证测试,初始化了一些数据,内容为对应的寄存器编号

    由于只实现了按字读写,因此输入的地址无需翻译,直接对应了寄存器编号

    clock信号用于保证写入正确,存储器只在clock信号的下降沿写,若不加clock信号会出现写入不稳定的输入的问题

    module mem_data(ena_W,ena_R,addr,data_W,data_R,clock);
    
    input ena_W,ena_R,clock;
    input [31:0] addr;
    input [31:0] data_W;
    output reg [31:0] data_R;
    
    reg [31:0] mem [1023:0];
    
    //测试用
    integer i;
    initial
    begin
    	for(i=0;i<1024;i=i+1)
    		mem[i]=i;
    end
    //只在下降沿写,保证写入内容的稳定
    always@(negedge clock)
    begin
    	if(ena_W) 
    	begin
    		if((addr)<1024)
    		begin 
    			mem[(addr)]<=data_W;
    		end
    	end
    end
    
    always@(addr,ena_R,data_R)
    begin
    	if(ena_R)
    	begin
    		if((addr)<1024) data_R<=mem[(addr)];
    	end
    end 
    
    endmodule
    

    MCU

    组合逻辑实现,输出输入指令编号对应操作所需要的信号(5种类型)

    module MCU(
    input [5:0] In,
    output reg RegDst,reg Jump,reg RegWr,reg Branch,reg MemtoReg,reg [1:0] ALUOp,reg MemWr,reg MemRd,reg ALUSrc);
    
    
    always@ (In)
    begin
    	case(In)
    		6'b000000:{MemRd,MemWr,ALUOp,ALUSrc,RegDst,RegWr,Branch,Jump,MemtoReg}={10'b0010011000};	//R
    		6'b100011:{MemRd,MemWr,ALUOp,ALUSrc,RegDst,RegWr,Branch,Jump,MemtoReg}={10'b1000101001};	//lw
    		6'b101011:{MemRd,MemWr,ALUOp,ALUSrc,RegDst,RegWr,Branch,Jump,MemtoReg}={10'b010010000x};	//sw
    		6'b000100:{MemRd,MemWr,ALUOp,ALUSrc,RegDst,RegWr,Branch,Jump,MemtoReg}={10'b000100010x};	//beq
    		6'b000010:{MemRd,MemWr,ALUOp,ALUSrc,RegDst,RegWr,Branch,Jump,MemtoReg}={10'b0011xx0x1x};	//j
    		default:  {MemRd,MemWr,ALUOp,ALUSrc,RegDst,RegWr,Branch,Jump,MemtoReg}={10'b0000000000};
    	endcase
    end
    
    endmodule
    

    PC

    rst信号用于重置PC为第一条指令对应的地址;clock上升沿写入,由于是单周期,无需写使能

    module reg_PC(clock,rst,In,Out);
    
    input clock;
    input rst;
    //input ena;	单周期无需写使能
    input [31:0] In;
    output [31:0] Out;
    
    reg [31:0] PC;
    
    always @(posedge rst) PC<=32'h00400000;
        
    always @(posedge clock) 
    begin
        if(1/*ena*/) PC<=In;        //enable input (单周期无需写使能)
    end
    
    assign Out=PC;
    
    endmodule
    

    其他组件

    其他模块实现较为简单

    加法器

    module ADD (A,B,Out);
    
    input [31:0] A,B;
    output reg [31:0] Out;
    
    always @(A,B)
    begin
    	if(A===B) Out<=A+4;	//PC+4
    	else Out<=A+B;
    end
    
    endmodule
    

    与门

    module ANDgate(in1,in2,Out);
    
    input in1,in2;
    output Out;
    
    assign Out=in1&in2;
    
    endmodule
    

    MUX

    module MUX_2_32(in0,in1,Sel,Out);
    
    input [31:0] in0,in1;
    input Sel;
    output reg[31:0] Out;
    
    always@(in0,in1,Sel)
    case (Sel)
    	0: Out<=in0;
    	default: Out<=in1;
    endcase
    
    endmodule
    
    module MUX_2_5(in0,in1,Sel,Out);
    
    input [4:0] in0,in1;
    input Sel;
    output reg[4:0] Out;
    
    always@(in0,in1,Sel)
    case (Sel)
    	0: Out<=in0;
    	default: Out<=in1;
    endcase
    
    endmodule
    

    SHL2

    module SHL2_32(In,Out);
    
    input [31:0] In;
    output [31:0] Out;
    
    assign Out={In[29:0],2'b00};
    
    endmodule
    
    module SHL2_26(In,Out);
    
    input [25:0] In;
    output [27:0] Out;
    
    assign Out={In[25:0],2'b00};
    
    endmodule
    

    SigExt16_32

    module SigExt16_32(In,Out);
    
    input [15:0] In;
    output [31:0] Out;
    
    assign Out={{16{In[15]}},In[15:0]};
    
    endmodule
    

    CPU

    根据数据通路进行连线,时钟信号和rst信号由外部输入

    `include "MUX_2_32.v"
    `include "ALU_CU.v"
    `include "SHL2.v"
    `include "ALU.v"
    `include "reg_file.v"
    `include "SigExt16_32.v"
    `include "mem_ins.v"
    `include "reg_PC.v"
    `include "ANDgate.v"
    `include "MCU.v"
    `include "ADD_32.v"
    
    module CPU(clock,rst);
    
    input clock,rst;
    
    wire [31:0] PC_out, PC_in, Inst, Add1_out, ALU_A, ALU_B, ALU_out, ADD2_B, ADD2_out, SigExt_out, data_W, data_R, WriteData_RF;
    wire [31:0] MUX_2_32_1_out;
    wire MemRd,MemWr,ALUSrc,RegDst,RegWr,Branch,Jump,MemtoReg,Zero,ANDgate_out;
    wire [1:0] ALUOp;
    wire [2:0] ALUctl;
    wire [4:0] W_Reg;
    wire [27:0] SHL2_out28;
    
    reg_PC reg_PC(.clock(clock),.rst(rst),.In(PC_in),.Out(PC_out));
    
    ADD ADD1(.A(PC_out),.B(PC_out),.Out(Add1_out));
    
    MCU MCU(.MemRd(MemRd),.MemWr(MemWr),.ALUOp(ALUOp),.ALUSrc(ALUSrc),.RegDst(RegDst),.RegWr(RegWr),.Branch(Branch),.Jump(Jump),.MemtoReg(MemtoReg),
    	.In(Inst[31:26]));
    
    SHL2_26 SHL2_26(.In(Inst[25:0]),.Out(SHL2_out28));
    
    ALU_CU ALU_CU(.ALUop(ALUOp),.FuncCode(Inst[5:0]),.Out(ALUctl));
    
    ALU ALU(.ALUctl(ALUctl),.A(ALU_A),.B(ALU_B),.ALUout(ALU_out),.Zero(Zero));
    
    ADD ADD2(.A(Add1_out),.B(ADD2_B),.Out(ADD2_out));
    
    SigExt16_32 SigExt16_32(.In(Inst[15:0]),.Out(SigExt_out));
    
    SHL2_32 SHL2_32(.In(SigExt_out),.Out(ADD2_B));
    
    mem_ins mem_ins(.addr(PC_out),.ins(Inst));
    
    mem_data mem_data(.ena_W(MemWr),.ena_R(MemRd),.addr(ALU_out),.data_W(data_W),.data_R(data_R),.clock(clock));
    
    reg_file reg_file(.Read1(Inst[25:21]),.Read2(Inst[20:16]),.WriteReg(W_Reg),
    		.WriteData(WriteData_RF),.RegWr(RegWr),.Data1(ALU_A),.Data2(data_W),.clock(clock));
    
    ANDgate ANDgate(.in1(Branch),.in2(Zero),.Out(ANDgate_out));
    
    MUX_2_5 MUX_2_5(.in0(Inst[20:16]),.in1(Inst[15:11]),.Sel(RegDst),.Out(W_Reg));
    
    MUX_2_32 MUX_2_32_1(.in0(Add1_out),.in1(ADD2_out),.Sel(ANDgate_out),.Out(MUX_2_32_1_out));
    
    MUX_2_32 MUX_2_32_2(.in0(MUX_2_32_1_out),.in1({Add1_out[31:28],SHL2_out28[27:0]}),.Sel(Jump),.Out(PC_in));
    
    MUX_2_32 MUX_2_32_3(.in0(data_W),.in1(SigExt_out),.Sel(ALUSrc),.Out(ALU_B));
    
    MUX_2_32 MUX_2_32_4(.in0(ALU_out),.in1(data_R),.Sel(MemtoReg),.Out(WriteData_RF));
    
    endmodule
    

    测试

    测试模块

    时钟信号和rst信号由测试模块给出

    `include "CPU.v"
    
    module CPU_TEST();
    
    reg rst;
    reg clock;
    initial
    begin
    	rst=0;
    	clock=0;
    	#1 rst=1;
    	#5 rst=0;
    end
    always #50 clock=~clock;
    
    CPU CPU(.clock(clock),.rst(rst));
    
    endmodule
    

    测试代码

    根据MIPS32指令集,设计了如下测试代码

    编号PC内容指令
    00x00400000R4<=mem[3]lw_R0_R4_3
    10x00400004R1<=mem[1]lw_R0_R1_1
    20x00400008R2<=mem[2]lw_R0_R2_2
    30x0040000cR3<=mem[4]lw_R0_R3_4
    40x00400010beq R3==R2 ->7beq_R2_R3_2
    50x00400014R2=R2+R10_R2_R1_R2_0_add
    60x00400018jmp->4j_0x100004
    70x0040001cmem[3]<=R2sw_R0_R2_3
    80x00400020jmp->0j_0x100000

    首先寄存器R4,R1,R2,R3分别写入数据3,1,2,4,然后累加(每次加1(R1))R2直到R2(初值2)=R3(4),然后将R2的值写入内存mem[3]中,再跳转到程序开头,此时R4的写入的应该是R2的值(4)

    测试结果

    在这里插入图片描述

    可以看到R2的值不断累加,直到等于R3,最后R4写入了R2的值,结果符合程序逻辑

    多周期

    总体结构

    • 数据通路

    在这里插入图片描述

    CU采用组合逻辑+有限状态机实现

    • CU

    在这里插入图片描述

    • 有限状态机

    在这里插入图片描述

    主要组件

    ALU和ALU_CU等组件与单周期相同

    寄存器

    不同于单周期,多周期数据通路多了很多寄存器用于存储中间数据,这些寄存器有不同特点:

    • 与单周期相同,只需clock信号控制写入(MDR、ALUOut、A、B)

      module reg_32(clock,In,Out);
      
      input clock;
      input [31:0] In;
      output [31:0] Out;
      
      reg [31:0] reg32;
          
      always @(posedge clock) 
      begin
      	reg32<=In;       
      end
      
      assign Out=reg32;
      
      endmodule
      
    • 需要写使能和clock信号同时对写入进行控制(PC、IR)

      module reg_IR(clock,ena_W,In,Out);
      
      input clock;
      input ena_W;	
      input [31:0] In;
      output [31:0] Out;
      
      reg [31:0] IR;
         
      always @(posedge clock) 
      begin
      	if(ena_W) IR<=In;        //enable input 
      end
      
      assign Out=IR;
      
      endmodule
      
    • 需要clock信号进行写入控制和rst信号进行重置(PC、SR)

      module reg_SR(clock,rst,In,Out);
      
      input clock;
      input rst;
      input [3:0] In;
      output [3:0] Out;
      
      reg [3:0] SR;
      
      always @(posedge rst) SR<=0;
          
      always @(posedge clock) 
      begin
      	SR<=In;        
      end
      
      assign Out=SR;
      
      endmodule
      
      module reg_PC(clock,rst,ena_W,In,Out);
      
      input clock;
      input rst;
      input ena_W;	
      input [31:0] In;
      output [31:0] Out;
      
      reg [31:0] PC;
      
      always @(posedge rst) PC<=32'h00400000;
          
      always @(posedge clock) 
      begin
      	if(ena_W) PC<=In;        //enable input 
      end
      
      assign Out=PC;
      
      endmodule
      

    CU

    用有限状态机实现,根据前一个状态和输入判断下一步状态并输出对应信号

    module CU(
    input [3:0] S, [5:0] OP,
    output reg PCWr,reg PCWrCond,reg IorD,reg MemRd,reg MemWr,reg IRWr,reg MemtoReg,
    output reg [1:0] PCSrc,reg [1:0] ALUOp,reg [1:0] ALUSrcB,reg ALUSrcA,reg RegWr,reg RegDst,
    output reg [3:0] NS
    );
    
    always@(S)
    begin
      case(S)
      0:NS<=1;
      1:
      begin
      	case(OP)
      	6'b100011:NS<=2;
      	6'b101011:NS<=2;
      	6'b000000:NS<=6;
      	6'b000100:NS<=8;
      	6'b000010:NS<=9;
      	default:NS<=4'b1xx1;
      	endcase
      end
      2:
      begin
      	case(OP)
      	6'b100011:NS<=3;
      	6'b101011:NS<=5;
      	default:NS<=0;
      	endcase
      end
      3:NS<=4;
      4:NS<=0;
      5:NS<=0;
      6:NS<=7;
      7:NS<=0;
      8:NS<=0;
      9:NS<=0;
      default:NS<=0;
      endcase
    end
    
    always@(S)
    case(S)
    0:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b1001010_00_00_01_000};
    1:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b0000000_00_00_11_000};
    2:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b0000000_00_00_10_100};
    3:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b0011000_00_00_00_000};
    4:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b0000001_00_00_00_010};
    5:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b0010100_00_00_00_000};
    6:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b0000000_00_10_00_100};
    7:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b0000000_00_00_00_011};
    8:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b0100000_01_01_00_100};
    9:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b1000000_10_00_00_000};
    default:{PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg, PCSrc , ALUOp , ALUSrcB ,ALUSrcA,RegWr,RegDst}={16'b0000000_00_00_00_000};
    endcase
    
    endmodule
    

    内存

    合并了数据存储器和指令存储器,同样用寄存器堆模拟,并初始化相应数据,指令同单周期

    需要注意的是读指令和读数据的地址转换方式不同

    module mem(ena_W,ena_R,addr,data_W,data_R,clock);
    
    input ena_W,ena_R,clock;
    input [31:0] addr;
    input [31:0] data_W;
    output reg [31:0] data_R;
    
    reg [31:0] mem [1023:0];
    
    //测试用
    integer i;
    initial
    begin
    	for(i=0;i<512;i=i+1)
    		mem[i]=i;
    end
    //测试用
    initial
    begin
    mem[512+0]=32'b100011_00000_00100_00000000_00000011;	//lw
    mem[512+1]=32'b100011_00000_00001_00000000_00000001;	//lw
    mem[512+2]=32'b100011_00000_00010_00000000_00000010;	//lw
    mem[512+3]=32'b100011_00000_00011_00000000_00000100;	//lw
    mem[512+4]=32'b000100_00010_00011_00000000_00000010;	//beq
    mem[512+5]=32'b000000_00010_00001_00010_00000_100000;	//add
    mem[512+6]=32'b000010_00_0001_0000_0000_0000_0000_0100;	//j
    mem[512+7]=32'b101011_00000_00010_00000000_00000011;	//sw
    mem[512+8]=32'b000010_00_0001_0000_0000_0000_0000_0000;	//j
    end
    
    always@(negedge clock)
    begin
    	if(ena_W) 
    	begin
    		if((addr)<512)
    		begin 
    			mem[(addr)]<=data_W;
    		end
    	end
    end
    
    always@(addr,ena_R,data_R)
    begin
    	if(ena_R)
    	begin
            if((addr)<512) data_R<=mem[(addr)];	//读数据 
            else data_R<=mem[((addr-32'h00400000)>>2)+512];	//读指令
    	end
    end 
    
    endmodule
    

    其他组件

    其他大部分组件与单周期相同

    MUX_4

    module MUX_3_32(in0,in1,in2,Sel,Out);
    
    input [31:0] in0,in1,in2;
    input [1:0] Sel;
    output reg[31:0] Out;
    
    always@(in0,in1,in2,Sel)
    case (Sel)
    	0: Out<=in0;
    	1: Out<=in1;
    	2: Out<=in2;
    	default: Out<=in0;
    endcase
    
    endmodule
    
    module MUX_4_32_1x(in0,in2,in3,Sel,Out);
    
    input [31:0] in0,in2,in3;
    input [1:0] Sel;
    output reg[31:0] Out;
    
    always@(in0,in2,in3,Sel)
    case (Sel)
    	0: Out<=in0;
    	1: Out<=4;
    	2: Out<=in2;
    	default: Out<=in3;
    endcase
    
    endmodule
    

    或门

    module ORgate(in1,in2,Out);
    
    input in1,in2;
    output Out;
    
    assign Out=in1|in2;
    
    endmodule
    

    CPU

    根据数据通路进行连线,时钟信号和rst信号由外部输入

    `include "MUX_2_32.v"
    `include "ALU_CU.v"
    `include "MUX_4_32.v"
    `include "SHL2.v"
    `include "ALU.v"
    `include "reg_file.v"
    `include "SigExt16_32.v"
    `include "reg_PC.v"
    `include "reg_32.v"
    `include "ANDgate.v"
    `include "ORgate.v"
    `include "mem.v"
    `include "CU.v"
    
    module CPU(clock,rst);
    
    input clock,rst;
    
    wire [31:0] Inst,PC_in,PC_out,mem_addr,mem_data_R,A_in,B_in,A_out,B_out,ALU_A,ALU_B,ALU_out,ALUOut_out,MDR_out;
    wire [31:0] SigExt_out,SHL2_32_out,WriteData_RF;
    wire [27:0] SHL2_26_out;
    wire PCWr,PCWrCond,IorD,MemRd,MemWr,IRWr,MemtoReg,ALUSrcA,RegWr,RegDst,ena_W_PC,Zero,ANDgate_out;
    wire [1:0] PCSrc,ALUOp,ALUSrcB;
    wire [3:0] SR_out,SR_in;
    wire [2:0] ALUctl;
    wire [4:0] WriteReg;
    
    CU CU(.S(SR_out),.OP(Inst[31:26]),
    .PCWr(PCWr),.PCWrCond(PCWrCond),.IorD(IorD),.MemRd(MemRd),.MemWr(MemWr),.IRWr(IRWr),.MemtoReg(MemtoReg),
    .PCSrc(PCSrc),.ALUOp(ALUOp),.ALUSrcB(ALUSrcB),.ALUSrcA(ALUSrcA),.RegWr(RegWr),.RegDst(RegDst),
    .NS(SR_in));
    
    reg_SR SR(.clock(clock),.rst(rst),.In(SR_in),.Out(SR_out));
    
    reg_PC PC(.clock(clock),.rst(rst),.ena_W(ena_W_PC),.In(PC_in),.Out(PC_out));
    
    mem mem(.ena_W(MemWr),.ena_R(MemRd),.addr(mem_addr),.data_W(B_out),.data_R(mem_data_R),.clock(clock));
    
    reg_IR IR(.clock(clock),.ena_W(IRWr),.In(mem_data_R),.Out(Inst));
    
    reg_32 A(.clock(clock),.In(A_in),.Out(A_out));
    
    reg_32 B(.clock(clock),.In(B_in),.Out(B_out));
    
    ALU ALU(.ALUctl(ALUctl),.A(ALU_A),.B(ALU_B),.ALUout(ALU_out),.Zero(Zero));
    
    ALU_CU ALU_CU(.ALUop(ALUOp),.FuncCode(Inst[5:0]),.Out(ALUctl));
    
    reg_32 ALUOut(.clock(clock),.In(ALU_out),.Out(ALUOut_out));
    
    reg_32 MDR(.clock(clock),.In(mem_data_R),.Out(MDR_out));
    
    SigExt16_32 SigExt16_32(.In(Inst[15:0]),.Out(SigExt_out));
    
    SHL2_32 SHL2_32(.In(SigExt_out),.Out(SHL2_32_out));
    
    SHL2_26 SHL2_26(.In(Inst[25:0]),.Out(SHL2_26_out));
    
    reg_file RF(.Read1(Inst[25:21]),.Read2(Inst[20:16]),.WriteReg(WriteReg),.WriteData(WriteData_RF),.RegWr(RegWr),.Data1(A_in),.Data2(B_in),.clock(clock));
    
    ANDgate ANDgate(.in1(PCWrCond),.in2(Zero),.Out(ANDgate_out));
    
    ORgate ORgate(.in1(PCWr),.in2(ANDgate_out),.Out(ena_W_PC));
    
    MUX_2_32 MUX_2_32_1(.in0(PC_out),.in1(ALUOut_out),.Sel(IorD),.Out(mem_addr));
    
    MUX_2_32 MUX_2_32_2(.in0(ALUOut_out),.in1(MDR_out),.Sel(MemtoReg),.Out(WriteData_RF));
    
    MUX_2_32 MUX_2_32_3(.in0(PC_out),.in1(A_out),.Sel(ALUSrcA),.Out(ALU_A));
    
    MUX_2_5 MUX_2_5(.in0(Inst[20:16]),.in1(Inst[15:11]),.Sel(RegDst),.Out(WriteReg));
    
    MUX_4_32_1x MUX_4_32_1x(.in0(B_out),.in2(SigExt_out),.in3(SHL2_32_out),.Sel(ALUSrcB),.Out(ALU_B));
    
    MUX_3_32 MUX_3_32(.in0(ALU_out),.in1(ALUOut_out),.in2({PC_out[31:28],SHL2_26_out[27:0]}),.Sel(PCSrc),.Out(PC_in));
    
    endmodule
    

    测试

    测试模块和测试代码同单周期

    测试结果

    在这里插入图片描述

    可以看到状态正常转换,并且R2累加
    在这里插入图片描述

    最后R4写入了R2的值,结果符合程序逻辑

    展开全文
  • MIPS单周期CPU实验代码.zip,MIPS单周期CPU实验代码,ALU.v,extender.v,RegFile.v,ctrl.v,dm.v,testbench.v,NPC.v,mips.v,mux.v,PC.v,datapath.v,im.v,code.txt
  • 利用Verilog HDL硬件描述语言实现单周期MIPS_CPU设计
  • 【计算机组成原理】实验4:单周期CPU
  • 基于vivado软件,使用verilog语言,较好地实现MIPS32的20条指令。包含测试代码和CPU实现代码。 实现的具体功能如下: 1.设计CPU能够执行20条整数指令,每条指令的编码长度均为32位; 2.指令类型有:计算类型、...
  • 课程设计所得单周期十条指令cpu,源代码在压缩包里,可略作参考,如果怎么跑都不会就。。。
  • 单周期CPU设计vivado

    2018-12-03 17:10:27
    单周期CPU设计的文件,语言verilog,打开之后,可以用vivado直接跑,思路是正确的,具体的指令需要根据要求做小幅度调整
  • Verilog单周期CPU设计(超详细)

    万次阅读 多人点赞 2019-01-19 12:27:24
    实验 单周期CPU一、设计目的与目标实验内容实验要求二、课程设计器材硬件平台软件平台三、 CPU逻辑设计总体方案指令模块MIPS指令格式指令处理流程数据通路总体结构图设计流程逻辑图四、模块详细设计PCAdd4...
  • 31条指令单周期CPU

    2018-12-08 00:00:34
    采用单周期方式实现了MIPS的31条指令,具体指令参见压缩包中的PDF文件。配有31条指令仿真测试的coe文件以及每一条指令单独测试文件和测试结果,在Vivado2016和Modelsim上验证通过。
  • 单周期CPUVerilog实现

    2012-03-19 22:34:37
    单周期CPU,实现了lw,sw,add,sub,slt,jmp指令
  • 单周期32位R型指令CPU,在ISE进行波形仿真
  • 计算机组成课程作业源码。MIPS单周期/多周期流水线设计,多周期流水线实现了数据冒险,控制冒险。代码结构清晰,欢迎交流讨论。
  • 内含CPU所有Verilog源码、论文详细解析,作业成绩为优秀 所有代码和论文皆为原创,严禁二次转载!
  • 这是计算机组成原理课设,基于verilog实现的单周期CPU代码,实现了add, addu, addi, addiu, sub, slt, and, or, xor, beq, j, sw, lw, lui,andi,addu,ori,xori,共18条指令,能够运行简单的冒泡排序。内含单周期...
  • Verilog单周期CPU配套源码,两个压缩包一个是完整的工程,一个是可以直接导入的函数库,任选一个即可。关于本代码的详细解释请移步于本人博客:https://blog.csdn.net/Accelerato/article/details/86546751
  • Verilog 单周期CPU

    热门讨论 2012-04-14 10:43:19
    自己设计单周期CPU,可以直接运行查看结果。
  • 基于vivado软件,使用verilog语言,较好地实现MIPS32的20条指令。包含测试代码和CPU实现代码。 实现的具体功能如下: 1.设计CPU能够执行20条整数指令,每条指令的编码长度均为32位; 2.指令类型有:计算类型、...
  • 单周期CPU自己课程大作业做的,亲测好用,verilog语言,适用vivado
  • 计算机组成原理课程设计,一个简单的单周期54条MIPSCPU,实现过程也是不断翻找网上的资料搞定的~
  • 16位单周期处理器的verilog实现。包括存储模块和仿真模块,结构很清晰,大学计算机组成原理课程必备。
  • 单周期cpu带仿真,课设资料,保质保量!直接可以用的,单周期cpu verilog设计及仿真!

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,965
精华内容 786
关键字:

单周期cpu设计verilog