【问题标题】:Incrementing a counter variable in verilog: combinational or sequential在verilog中增加一个计数器变量:组合或顺序
【发布时间】:2026-01-30 04:00:01
【问题描述】:

我正在为数据路径电路实现 FSM 控制器。控制器在内部增加一个计数器。当我模拟下面的程序时,计数器从未更新。

reg[3:0] counter;

//incrementing counter in combinational block
counter = counter + 4'b1;

但是,在创建一个额外的变量 counter_next(如 Verilog Best Practice - Incrementing a variable 中所述)并仅在顺序块中递增计数器时,计数器会递增。

reg[3:0] counter, counter_next;

//sequential block
always @(posedge clk) 
   counter <= counter_next;

//combinational block
counter_next = counter + 4'b1;

为什么在前一种情况下计数器没有增加?我有什么遗漏吗?

【问题讨论】:

    标签: hardware verilog fpga sequential


    【解决方案1】:

    好的。我假设您在第一个示例中留下了一些代码,因为它甚至不应该编译。不过,我想我无论如何都可以为您阐明这个问题。

    在一个看起来像这样的块中:

    always @(*) begin // or always @(counter)
        counter = counter + 4'b1;
    end
    

    有两个问题。

    1) 计数器从未初始化。所有 'reg' 类型的变量在仿真开始时都是 X,所以在 X 上加 1 就是 X。

    2) 这被认为是组合循环。该块对“计数器”的变化很敏感,因此即使假设“计数器”被初始化为 0,模拟器也会永远循环更新“计数器”并且模拟时间永远不会提前。即

    always block executes -> counter = 1
    counter has changed
    always block executes -> counter = 2
    counter has changed
    and so on...
    

    如果您要在其中放置 $display 语句,您可能会看到此循环发生。否则只会显示模拟器挂起,不会写入任何波形。

    第二个例子有效的原因是你有一个触发器打破了组合循环。在每个时钟边沿,“counter”都会更新为“counter_next”的当前值。然后组合块执行一次(并且只执行一次)以计算新版本的 'counter_next'。

    为了完整起见,您仍然缺少通过重置子句或初始语句对“计数器”进行初始化。

    reg [3:0] counter;
    reg [3:0] counter_next;
    
    always @(*) begin
       counter_next = counter + 1;
    end
    
    always @(posedge clk or negedge rst_l) begin
       if (!rst_l)
          counter <= 4'b0;
       else
          counter <= counter_next;
    end
    

    【讨论】:

    • 显然,没有任何代码可以编译。我遗漏了大量代码,因为我为使其正常工作所做的唯一更改是更改边缘触发器上的“计数器”。在这两种情况下,寄存器“counter”都被初始化为 0。
    • 我在模拟中没有遇到任何“循环”。 'counter' 变量甚至从未更新过。它只是固定为 0。当我将“计数器”的值与常量进行比较时,不太可能出现循环。
    • 很公平。我不确定其他地方的比较与它有什么关系。我目前无法访问模拟器来确认(模拟器)循环行为,但事实仍然是组合循环是您的第一个示例的问题。它基本上说“这组线同时代表 X 和 X+1”没有可实现的硬件可以做到这一点。请记住,Verilog 不是命令式编程。您在这里描述的是硬件。
    • 计数器 = 计数器 + 4'b1;在组合语句中是一个循环。可能永远不会更新,因为敏感性列表从未触发。这很好:总是@(posedge clk) counter