4. 寄存器文件设计

任务目标

设计并实现一个32×32位的寄存器文件(Register File),提供两个异步读端口和一个同步写端口,满足RISC-V整数指令集对双源操作数读取与结果回写的基本需求。通过编写测试平台验证写使能控制下的同步写入行为以及组合逻辑读出的正确性。

实现方案

寄存器文件采用行为级Verilog描述,核心存储体为一个二维数组。写操作在时钟上升沿触发,受写使能信号控制;读操作无需时钟,直接组合输出对应地址的数据。该设计遵循RISC-V规范中寄存器x0恒为0的约定,对x0的写入操作被硬件忽略。

关键完整代码

module register_file #(
    parameter DATA_WIDTH = 32,
    parameter ADDR_WIDTH = 5,
    parameter DEPTH      = 32
)(
    input  wire                     clk,
    input  wire                     rst_n,
    // 写端口
    input  wire                     we,
    input  wire [ADDR_WIDTH-1:0]    waddr,
    input  wire [DATA_WIDTH-1:0]    wdata,
    // 读端口1
    input  wire [ADDR_WIDTH-1:0]    raddr1,
    output wire [DATA_WIDTH-1:0]    rdata1,
    // 读端口2
    input  wire [ADDR_WIDTH-1:0]    raddr2,
    output wire [DATA_WIDTH-1:0]    rdata2
);

    // 寄存器阵列定义
    reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];

    // 异步读端口1(x0恒为0)
    assign rdata1 = (raddr1 == 5'b0) ? {DATA_WIDTH{1'b0}} : mem[raddr1];

    // 异步读端口2(x0恒为0)
    assign rdata2 = (raddr2 == 5'b0) ? {DATA_WIDTH{1'b0}} : mem[raddr2];

    // 同步写端口
    integer i;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (i = 0; i < DEPTH; i = i + 1)
                mem[i] <= {DATA_WIDTH{1'b0}};
        end else if (we && (waddr != 5'b0)) begin
            mem[waddr] <= wdata;
        end
        // 写x0时忽略(保持为0)
    end

endmodule

测试平台与验证

module register_file_tb;

    reg         clk, rst_n, we;
    reg  [4:0]  waddr, raddr1, raddr2;
    reg  [31:0] wdata;
    wire [31:0] rdata1, rdata2;

    // 时钟周期10ns
    always #5 clk = ~clk;

    register_file uut (
        .clk(clk), .rst_n(rst_n),
        .we(we), .waddr(waddr), .wdata(wdata),
        .raddr1(raddr1), .rdata1(rdata1),
        .raddr2(raddr2), .rdata2(rdata2)
    );

    initial begin
        // 初始化
        clk = 0; rst_n = 0; we = 0;
        waddr = 0; wdata = 0; raddr1 = 0; raddr2 = 0;
        #12 rst_n = 1;

        // 测试1:写入x1=0x12345678,x2=0x87654321
        @(negedge clk);
        we = 1; waddr = 5'd1; wdata = 32'h12345678;
        @(negedge clk);
        waddr = 5'd2; wdata = 32'h87654321;
        @(negedge clk);
        we = 0;

        // 测试2:读x1和x2,验证双读端口
        raddr1 = 5'd1; raddr2 = 5'd2;
        #1;
        if (rdata1 !== 32'h12345678) $display("ERROR: rdata1 = %h", rdata1);
        else $display("PASS: rdata1 = %h", rdata1);
        if (rdata2 !== 32'h87654321) $display("ERROR: rdata2 = %h", rdata2);
        else $display("PASS: rdata2 = %h", rdata2);

        // 测试3:写x0应被忽略
        @(negedge clk);
        we = 1; waddr = 5'd0; wdata = 32'hDEADBEEF;
        @(negedge clk);
        we = 0;
        raddr1 = 5'd0;
        #1;
        if (rdata1 !== 32'h0) $display("ERROR: x0 = %h", rdata1);
        else $display("PASS: x0 remains 0");

        // 测试4:写使能关闭时数据不变
        @(negedge clk);
        we = 0; waddr = 5'd1; wdata = 32'hFFFFFFFF;
        @(negedge clk);
        raddr1 = 5'd1;
        #1;
        if (rdata1 !== 32'h12345678) $display("ERROR: we=0 write through, rdata1 = %h", rdata1);
        else $display("PASS: write disabled correctly");

        $display("All tests completed.");
        $finish;
    end

    // 波形导出
    initial begin
        $dumpfile("register_file_tb.vcd");
        $dumpvars(0, register_file_tb);
    end

endmodule

运行方式与通过条件

环境依赖:Icarus Verilog(iverilog)或同等Verilog仿真器。

iverilog -o rf_tb register_file.v register_file_tb.v
vvp rf_tb

通过判定:仿真终端输出全部4条PASS信息且无ERROR提示,即验证通过。同时可打开生成的register_file_tb.vcd波形文件,观察在时钟上升沿写入数据后,读端口在组合逻辑延迟后输出对应数值,且x0始终为0。

时序说明

本设计为同步写、异步读结构。写入数据在时钟上升沿锁存,读数据在地址稳定后约一个与门延迟内有效,不依赖时钟。该结构是RISC-V流水线中寄存器文件的标准实现方式,满足双发射读操作数需求。