寄存器在非阻塞状态下总是递增两次

问题描述 投票:0回答:1

我想做一个具有输出使能的向上计数器(当为高电平时,向上计数器应该在每个时钟周期向上计数,否则它将保持其先前的值)。为了在我的 Altera TerASIC 板上获得可见的结果,我使用了它的板载时钟发生器,并在 Verilog 中内置了一个时钟分频器模块,以便为我的向上计数器提供一个频率更合适的时钟。此外,我创建了一个十六进制模块,将其连接到向上计数器输出,以便能够以数字形式查看输出值。 问题是,我不知道为什么在 TeraASIC 板上运行设计时,输出每个时钟周期都会增加 2(而不是预期的 1)。我提供了下面的完整代码,因为我不知道问题源于何处。尽管如此,我相信十六进制模块完全没问题。问题发生在计数器块内或除法器块内。 此外,我做了一个小测试台,其结果很奇怪,因为它们证实了电路工作正常。我在这篇文章的底部详细介绍了测试平台。 然而,有趣的是,如果我用一个不考虑 en 和 d 信号的简单 else 替换计数器中的 else if,我会得到一个完整的功能设计,一个简单的向上计数器,它在每个时钟沿后递增其值。我不知道为什么会发生这种情况。也许真正的问题就隐藏在那里。我在该代码区域中添加了注释,以便更加明显。

module counter(
    input clk,
    input rst_b,
    input en,
    input [2:0] d,
    output [6:0] digit
);

wire clk_d;
reg [2:0] q;

divider i0(.clk(clk),.rst_b(rst_b),.clk_d(clk_d));
decoder i1(.binary(q),.digit(digit));

always @(posedge clk_d, negedge rst_b) begin
    if(rst_b == 1'b0)
        q <= 3'b000;
    else if(en)//when it was only else q<=q+en; it went fine...why???
        if(q == d)
            q <= 3'b000;
        else
            q <= q + en;//increment by 2 instead of 1
end

endmodule


module divider(
    input clk,
    input rst_b,
    output clk_d
);

reg [24:0] q;

always @(posedge clk) begin
    if(rst_b == 1'b0)
        q <= 25'b0;
    else
        q <= q-1;
end

assign clk_d = (q == 25'b0);

endmodule


module decoder(
    input [2:0] binary,
    output reg [6:0] digit
);

always @ * begin
    case (binary)
        4'h0: digit = ~7'b0111111;
        4'h1: digit = ~7'b0000110;
        4'h2: digit = ~7'b1011011;
        4'h3: digit = ~7'b1001111;
        4'h4: digit = ~7'b1100110;
        4'h5: digit = ~7'b1101101;
        4'h6: digit = ~7'b1111101;
        4'h7: digit = ~7'b0000111;
        default : digit = ~7'b1110001;
    endcase
end

endmodule

我得到了一个小测试平台,它令人惊讶地证明至少在理论上我的模块工作得很好:

module counter_tb;

reg clk, rst_b, en;
reg [2:0] d;
wire [6:0] digit;

counter i0( .clk(clk),
            .rst_b(rst_b),
            .en(en),
            .d(d),
            .digit(digit)
);

always begin
    #1 clk = ~clk;
end

initial begin
    clk = 1'b0;
    en = 1'b1;
    d = 3'b111;
    rst_b = 1'b0;
    #3 rst_b = 1'b1;
end
    
endmodule
verilog digital digital-design
1个回答
1
投票

我不知道您的问题是什么或解决方法是什么,但是您使用的设计实践通常不适用于创建时钟的综合流程(针对硬件)。如果它在一个构建中有效,那么在下一个构建中可能就不起作用了。一点额外的延迟(例如添加启用)将在硬件中产生完全不同的结果。

这种做法是欺骗性的,因为您可以编写一些在模拟中表现如您所愿的东西,但在综合/硬件工作流程中却不会按预期表现。 Verilog模拟器通常不会对延迟进行建模(基于延迟的仿真是可能的,但仍然不能解决问题),因此必须使用某些良好的设计实践来使硬件结果与仿真一致。

您通常不能创建时钟并将其用作其他触发器的时钟,在物理 FPGA 中使用简单的触发器分频器。有两个可靠的选项可以获取与生成较慢时钟相关的行为。

  1. 使用计数器作为分频器创建慢时钟使能,然后将它们用作使能。
  2. 使用 MMCM/PLL 时钟分频器 IP 模块中内置的部件来创建较慢的时钟。

这个链接解释了更多,写得很好,我无法解释得更好
在 FPGA 上的 RTL 中使用触发器的时钟分频器 – 绝对不行!
无需写论文。

这将是一个单独的新问题和答案
“如何在 FPGA 中创建一个可以针对综合工作流程中的硬件的时钟分频器”

如果触发器除法器

assign clk_d = (q == 25'b0);
及其相关逻辑并使用我提到的两个解决方案之一,并在提供的链接中提到,我会摆脱它。


此外,如果您将 RTL 代码部署到实验室而不使用测试平台进行模拟,那么您通常会看到一个黑匣子,上面写着“它不起作用”

© www.soinside.com 2019 - 2024. All rights reserved.