欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

计算机组成原理-实验四-寄存器堆设计实验

程序员文章站 2022-03-17 14:10:56
...
实验内容与原理:

    CPU内部通常包含若干个通用寄存器,以暂存参加运算的数据和中间结果。寄存器速度快,个数少,但是RISC CPU的设计强调设置大量的寄存器。例如,Intel 80x86系列CPU中只有8个寄存器,而SPARC RISC CPU中却有120个寄存器。MISP CPU中有32个32位的寄存器堆。

  所谓寄存器堆,就是一个寄存器的集合,为方便访问其中的寄存器,对寄存器堆中的寄存器进行统一编码,称为寄存器号或者寄存器地址,每个寄存器均通过制定寄存器好进行访问。

  本实验要求设计一个32*32位的寄存器堆,即含有32个寄存器,每个寄存器32位。该寄存器堆有2个读端口、1个写端口,即能够同时读出2个寄存器的值,写入1个寄存器。读操作不需要时钟控制,写操作需要在上跳沿才能写入。

  其他具体实验内容参考计算机组成原理实验指导书。

程序模块结构图:

计算机组成原理-实验四-寄存器堆设计实验

ALU运算器模块代码:
`timescale 1ns / 1ps
//ALU模块
module ALU(OP,A,B,F,ZF,CF,OF,SF,PF);
    parameter SIZE = 32;//运算位数
    input [3:0] OP;//运算操作
    input [SIZE:1] A;//左运算数
    input [SIZE:1] B;//右运算数
    output [SIZE:1] F;//运算结果
    output  ZF, //0标志位, 运算结果为0(全零)则置1, 否则置0 
            CF, //进借位标志位, 取最高位进位C,加法时C=1则CF=1表示有进位,减法时C=0则CF=1表示有借位
            OF, //溢出标志位,对有符号数运算有意义,溢出则OF=1,否则为0
            SF, //符号标志位,与F的最高位相同
            PF; //奇偶标志位,F有奇数个1,则PF=1,否则为0
    reg [SIZE:1] F;
    reg C,ZF,CF,OF,SF,PF;//C为最高位进位
    aaa@qq.com(*)
    begin
        C=0;
        case(OP)
            4'b0000:begin F=A&B; end    //按位与
            4'b0001:begin F=A|B; end    //按位或
            4'b0010:begin F=A^B; end    //按位异或
            4'b0011:begin F=~(A|B); end //按位或非
            4'b0100:begin {C,F}=A+B; end //加法
            4'b0101:begin {C,F}=A-B; end //减法
            4'b0110:begin F=A<B; end    //A<B则F=1,否则F=0
            4'b0111:begin F=B<<A; end   //将B左移A位
        endcase
        ZF = F==0;//F全为0,则ZF=1
        CF = C; //进位借位标志
        OF = A[SIZE]^B[SIZE]^F[SIZE]^C;//溢出标志公式
        SF = F[SIZE];//符号标志,取F的最高位
        PF = ~^F;//奇偶标志,F有奇数个1,则F=1;偶数个1,则F=0
    end     
endmodule
寄存器堆模块代码:
`timescale 1ns / 1ps
//寄存器堆模块
module RegFile(Clk,Clr,Write_Reg,R_Addr_A,R_Addr_B,W_Addr,W_Data,R_Data_A,R_Data_B);
    parameter ADDR = 5;//寄存器编码/地址位宽
    parameter NUMB = 1<<ADDR;//寄存器个数
    parameter SIZE = 32;//寄存器数据位宽
    
    input Clk;//写入时钟信号
    input Clr;//清零信号
    input Write_Reg;//写控制信号
    input [ADDR:1]R_Addr_A;//A端口读寄存器地址
    input [ADDR:1]R_Addr_B;//B端口读寄存器地址
    input [ADDR:1]W_Addr;//写寄存器地址
    input [SIZE:1]W_Data;//写入数据
    
    output [SIZE:1]R_Data_A;//A端口读出数据
    output [SIZE:1]R_Data_B;//B端口读出数据
    
    reg [SIZE:1]REG_Files[0:NUMB-1];//寄存器堆本体
    integer i;//用于遍历NUMB个寄存器
    
    initial//初始化NUMB个寄存器,全为0
        for(i=0;i<NUMB;i=i+1) REG_Files[i]<=0;
    aaa@qq.com(posedge Clk or posedge Clr)//时钟信号或清零信号上升沿
    begin
        if(Clr)//清零
                for(i=0;i<NUMB;i=i+1) REG_Files[i]<=0;
        else//不清零,检测写控制, 高电平则写入寄存器
                if(Write_Reg) REG_Files[W_Addr]<=W_Data;
    end
    
    //读操作没有使能或时钟信号控制, 使用数据流建模(组合逻辑电路,读不需要时钟控制)
    assign R_Data_A=REG_Files[R_Addr_A];
    assign R_Data_B=REG_Files[R_Addr_B];

endmodule
顶层综合模块:
`timescale 1ns / 1ps
//寄存器堆+ALU组合模块
module RF_ALU(
        Clk,Clr,Write_Reg,Write_Select,//控制信号
        R_Addr_A,R_Addr_B,W_Addr,//读写地址
        Input_Data,R_Data_A,R_Data_B,//数据IO
        OP,ZF,CF,OF,SF,PF,F//ALU运算
        );
    parameter ADDR = 5;//地址位宽
    parameter SIZE = 32;//数据位宽
    
    //寄存器堆
    input Clk, Clr;//写入时钟信号, 清零信号
    input Write_Reg;//写控制信号
    input [ADDR:1]R_Addr_A;//A读端口寄存器地址
    input [ADDR:1]R_Addr_B;//B读端口寄存器地址
    input [ADDR:1]W_Addr;//写寄存器地址
    input [SIZE:1]Input_Data;//外部输入数据
    
    output [SIZE:1]R_Data_A;//A端口读出数据
    output [SIZE:1]R_Data_B;//B端口读出数据
    
    //ALU
    input [3:0] OP;//运算符编码
    output ZF,//零标志
           CF,//进借位标志(只对无符号数运算有意义)
           OF,//溢出标志(只对有符号数运算有意义)
           SF,//符号标志(只对有符号数运算有意义)
           PF;//奇偶标志
    output [SIZE:1]F;//运算结果F
    
    input Write_Select;//写入数据选择信号
    wire [SIZE:1]ALU_F;//ALU运算结果中间变量
    wire [SIZE:1]W_Data;//写入数据

    //Write_Select高电平则写外部输入,否则写运算结果
    assign W_Data=Write_Select ? Input_Data : ALU_F;
    assign F = ALU_F;//输出运算结果F

    //实例化寄存器堆模块
    RegFile RF_Test(
        //输入
        .Clk(Clk),//时钟信号
        .Clr(Clr),//清零信号
        .Write_Reg(Write_Reg),//写入控制
        .R_Addr_A(R_Addr_A),//A端口读地址
        .R_Addr_B(R_Addr_B),//B端口读地址
        .W_Addr(W_Addr),//写入地址
        .W_Data(W_Data),//写入数据, 由外部或ALU输入
        
        //输出
        .R_Data_A(R_Data_A),//A端口读出数据
        .R_Data_B(R_Data_B)//B端口读出数据
    );   
       
    //实例化ALU模块
    ALU ALU_Test(
        //输入
        .OP(OP),//运算符
        
        .A(R_Data_A),//从寄存器读A操作数
        .B(R_Data_B),//从寄存器读B操作数
        .F(ALU_F),//ALU_F作为中间变量暂存运算结果,与Input_Data选择输入寄存器
        
        //输出
        .ZF(ZF),//零标志
        .CF(CF),//进借位标志(只对无符号数运算有意义)
        .OF(OF),//溢出标志(只对有符号数运算有意义)
        .SF(SF),//符号标志(只对有符号数运算有意义)
        .PF(PF)//奇偶标志
    );
endmodule
测试模块代码:
`timescale 1ns / 1ps

module Test();
    reg  Clk, Clr, Write_Reg, Write_Select;
    reg [4:0] R_Addr_A ,R_Addr_B, W_Addr;
    reg [31:0] Input_Data;
    reg [3:0] OP;
    wire [31:0] R_Data_A, R_Data_B, F;
    wire ZF,CF,OF,SF,PF;
    initial
    begin
        Clr=0;//不清零
        Write_Reg=1;//写进去之后读读看
        R_Addr_A=5'b00000;//A端口先读寄存器0
        R_Addr_B=5'b00001;//B端口先读寄存器1
        OP=4'b0101;//先做一个减法运算

        Write_Select=1'b1;//先写外部输入

        //将32'h7fff_fffd写入寄存器0,作为左操作数
        Input_Data=32'h7fff_fffd;W_Addr=5'b00000;Clk=0;#50;Clk=1;#50;
        
        //将32'h7fff_ffff写入寄存器0,作为右操作数
        Input_Data=32'h7fff_ffff;W_Addr=5'b00001;Clk=0;#50;Clk=1;#50;

        Write_Select=1'b0;//改为写入运算结果

        R_Addr_B=5'b00010;//B端口改为读寄存器2
        //将ALU的运算结果写入寄存器,并读读看
        W_Addr=5'b00010;Clk=0;#50;Clk=1;#50;
        
        //到此为止我们完成了一次减法运算
        //(其实不是(ALU实时运算),实际上ALU已经做了5次减法)
        //左右操作数跟运算结果依次写入了寄存器0,1,2

    end
    
    //实例化寄存器堆模块
    RF_ALU RF_Test(
        .Clk(Clk),
        .Clr(Clr),
        .Write_Reg(Write_Reg),
        .Write_Select(Write_Select),
        
        .R_Addr_A(R_Addr_A),
        .R_Addr_B(R_Addr_B),
        .W_Addr(W_Addr),
        
        .Input_Data(Input_Data),
        .R_Data_A(R_Data_A),
        .R_Data_B(R_Data_B),
        
        .OP(OP),
        .ZF(ZF),
        .CF(CF),
        .OF(OF),
        .SF(SF),
        .PF(PF),
        .F(F)
    );
endmodule

仿真波形图:

计算机组成原理-实验四-寄存器堆设计实验

仿真电路图:

计算机组成原理-实验四-寄存器堆设计实验

心得体会:

    小组在实验过程中碰到运算结果写不进去的问题,卡了一小会。最后lym同学的构思比较巧妙,将运算结果F做为输出,定义一个ALU_F作为中间变量。ALU_F和Input_Data设计一个二选一控制电路,外加一个控制信号,这样完美的解决了写入冲突的问题。到后面实验的R_I_J型指令,这种思想非常重要,但是在不知情的情况下,能想到这样,lym同学真的非常有编程天赋。