【问题标题】:Assign first register to zero and do not write将第一个寄存器分配为零并且不写
【发布时间】:2017-01-30 18:56:55
【问题描述】:

我是 Verilog 的新手,试图创建一个包含 32 位寄存器的寄存器文件。我能够正确地写入和读取所有内容,但是,位于地址 5'b00000 的第一个寄存器(我们将其命名为 R0)必须始终等于 0,并且不得随时更改。在测试台上读取它时,当 R0 突然变为“xxxxxxxx”而不是 0 或 00000000 时,问题就出现了。其余的寄存器被正确读取。我在代码中可能做错了什么,这可能是什么解决方法?下面是代码:

module regfile (
    clk,
    nrst,
    rd_addrA,
    rd_addrB,
    wr_addr,
    wr_en,
    wr_data,
    rd_dataA,
    rd_dataB
);

//Input and output ports
input wire clk;
input wire nrst;
input wire [4:0] rd_addrA;
input wire [4:0] rd_addrB;
input wire [4:0] wr_addr;
input wire wr_en;
input wire [31:0] wr_data;
output reg [31:0] rd_dataA;
output reg [31:0] rd_dataB;

reg [31:0] regfile[0:31];
integer i;

always @ (nrst)
    begin: RESET
        if(nrst == 0) begin
            for(i = 0; i < 32; i++) begin
                regfile[i] = 0;
            end
        end
    end

always @(rd_addrA or rd_addrB)
    begin: READ
        if(rd_addrA) begin
            case (rd_addrA)
                5'b00000: rd_dataA = regfile[0];
                5'b00001: rd_dataA = regfile[1];
                5'b00010: rd_dataA = regfile[2];
                5'b00011: rd_dataA = regfile[3];
                5'b00100: rd_dataA = regfile[4];
                5'b00101: rd_dataA = regfile[5];
                5'b00110: rd_dataA = regfile[6];
                5'b00111: rd_dataA = regfile[7];
                5'b01000: rd_dataA = regfile[8];
                5'b01001: rd_dataA = regfile[9];
                5'b01010: rd_dataA = regfile[10];
                5'b01011: rd_dataA = regfile[11];
                5'b01100: rd_dataA = regfile[12];
                5'b01101: rd_dataA = regfile[13];
                5'b01110: rd_dataA = regfile[14];
                5'b01111: rd_dataA = regfile[15];
                5'b10000: rd_dataA = regfile[16];
                5'b10001: rd_dataA = regfile[17];
                5'b10010: rd_dataA = regfile[18];
                5'b10011: rd_dataA = regfile[19];
                5'b10100: rd_dataA = regfile[20];
                5'b10101: rd_dataA = regfile[21];
                5'b10110: rd_dataA = regfile[22];
                5'b10111: rd_dataA = regfile[23];
                5'b11000: rd_dataA = regfile[24];
                5'b11001: rd_dataA = regfile[25];
                5'b11010: rd_dataA = regfile[26];
                5'b11011: rd_dataA = regfile[27];
                5'b11100: rd_dataA = regfile[28];
                5'b11101: rd_dataA = regfile[29];
                5'b11110: rd_dataA = regfile[30];
                5'b11111: rd_dataA = regfile[31];
                default: rd_dataA = 16'hXXXX;
            endcase
        end

        if(rd_addrB) begin
            case (rd_addrB)
                5'b00000: rd_dataB = regfile[0];
                5'b00001: rd_dataB = regfile[1];
                5'b00010: rd_dataB = regfile[2];
                5'b00011: rd_dataB = regfile[3];
                5'b00100: rd_dataB = regfile[4];
                5'b00101: rd_dataB = regfile[5];
                5'b00110: rd_dataB = regfile[6];
                5'b00111: rd_dataB = regfile[7];
                5'b01000: rd_dataB = regfile[8];
                5'b01001: rd_dataB = regfile[9];
                5'b01010: rd_dataB = regfile[10];
                5'b01011: rd_dataB = regfile[11];
                5'b01100: rd_dataB = regfile[12];
                5'b01101: rd_dataB = regfile[13];
                5'b01110: rd_dataB = regfile[14];
                5'b01111: rd_dataB = regfile[15];
                5'b10000: rd_dataB = regfile[16];
                5'b10001: rd_dataB = regfile[17];
                5'b10010: rd_dataB = regfile[18];
                5'b10011: rd_dataB = regfile[19];
                5'b10100: rd_dataB = regfile[20];
                5'b10101: rd_dataB = regfile[21];
                5'b10110: rd_dataB = regfile[22];
                5'b10111: rd_dataB = regfile[23];
                5'b11000: rd_dataB = regfile[24];
                5'b11001: rd_dataB = regfile[25];
                5'b11010: rd_dataB = regfile[26];
                5'b11011: rd_dataB = regfile[27];
                5'b11100: rd_dataB = regfile[28];
                5'b11101: rd_dataB = regfile[29];
                5'b11110: rd_dataB = regfile[30];
                5'b11111: rd_dataB = regfile[31];
                default: rd_dataB = 16'hXXXX;
            endcase
        end
    end

always @ (posedge clk)
    begin: WRITE
        if(wr_en == 1'b1) begin
            if(wr_addr != 5'd0) begin
                regfile[wr_addr] = #1 wr_data;
                //$display("%X", regfile[wr_addr]);
            end
            else begin
                $display("R0: %X", regfile[wr_addr]);
            end
        end
    end

endmodule

非常感谢您的帮助。

【问题讨论】:

  • 如果您希望寄存器无论如何都包含一个值,您还应该有一个初始条件。这可确保如果 GSR 或您的重置未命中,无论出于何种原因,您的 R0 将包含零值。

标签: verilog cpu-registers test-bench


【解决方案1】:

if(rd_addrA) 被解释为if(rd_addrA&gt;0)。因此读取regfile[0] 无法访问。因为所有条目都是有效的,所以您不需要 if 语句。

与您的主要问题无关的其他问题:

您的代码无法合成,因为regfile 分配在两个不同的 always 块中。您需要将regfile 的reset 和write 合并到一个always 块中。 always @(posedge clk) 用于同步复位(推荐用于 FPGA)。 always @(posedge clk or negedge nrst) 用于异步重置(推荐用于 ASIC)。

在组合块中指定灵敏度是旧样式,仅适用于 1995 版。自 2001 年以来,自动选择(always @* 或同义词 always @(*))是启动组合块的完美方式。自 Verilog-1995 以来,模块标题样式建议也发生了变化,因此您也应该阅读它。 Verilog-1995 样式仍然有效并受支持;只是不再完善。

同步逻辑(又名由时钟或锁存器使能分配的寄存器)应分配非阻塞分配 (&lt;=)。如果您没有正确使用阻塞和非阻塞分配,则 RTL 和门之间行为不匹配的风险会更高。

您的案例陈述可以简化为一行:rd_dataA = regfile[rd_addrA];。但是请检查您的合成结果,因为与案例语句相比,某些合成器在这种风格下的优化效果不佳。

【讨论】:

  • 为什么推荐 FPGA 中的同步复位。我认为异步复位是 FPGA 和 ASIC 的推荐技术
  • 大多数 FPGA 的触发器数量有限,带有异步复位/预设(如果有);至少设计师可以访问的。 FPGA 通常使用initial 块来定义上电复位值。 ASIC 合成器通常会忽略 initial 块,这就是使用异步复位的原因。
  • 我应该澄清异步复位应该主要用于上电复位。在大多数情况下,建议在正常操作期间同步重置。示例:计数器不应该异步重置自身(元稳定性问题)。
  • 我不知道初始块可以在 FPGA 中合成。感谢您指出。我想每天总会有新的东西要学。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-04
  • 2018-06-06
  • 2020-12-24
  • 2020-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多