You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Verilog技术问询:两个always @(posedge clk)块能否合并为一个?

两个always @(posedge clk)块和单个always @(posedge clk)块功能完全一致吗?

这个问题问得很关键——答案是不一定完全等价,得看你在task1task2里写的具体逻辑。大部分常规场景下两者行为一致,但存在几种特殊情况会导致差异,下面分情况举例说明:

情况1:功能完全等价的场景

task1task2是独立操作(不共享变量),且都使用**非阻塞赋值<=**时,两个always块和单个always块的行为完全一致。

比如下面的代码:

// 两个独立的always块
always @(posedge clk) begin
    reg_a <= data_in; // task1:给reg_a赋值
end
always @(posedge clk) begin
    reg_b <= reg_a; // task2:用reg_a的值给reg_b赋值
end

// 单个always块的写法
always @(posedge clk) begin
    reg_a <= data_in; // task1
    reg_b <= reg_a; // task2
end

Verilog中,非阻塞赋值的更新是在时钟边沿统一完成的,不管是分两个块还是放在同一个块里,reg_b都会拿到reg_a在当前时钟沿更新前的值,最终的硬件行为和仿真结果完全一致。

情况2:功能不一致的场景

场景A:多个块修改同一个变量(尤其是用阻塞赋值=

如果两个always块都修改同一个变量,且使用阻塞赋值,会出现竞争条件,仿真结果不确定;而单个always块里的顺序执行是确定的。

示例:

// 两个always块的写法
always @(posedge clk) begin
    cnt = cnt + 1; // task1:阻塞赋值修改cnt
end
always @(posedge clk) begin
    cnt = cnt + 1; // task2:同样修改cnt
end

// 单个always块的写法
always @(posedge clk) begin
    cnt = cnt + 1;
    cnt = cnt + 1;
end
  • 单个always块中,两次阻塞赋值是顺序执行的,cnt最终会加2,结果确定。
  • 两个always块的情况:仿真器在同一个时钟边沿会执行这两个块,但执行顺序是不确定的——可能先执行第一个块再执行第二个(cnt加2),也可能两个块的赋值操作互相覆盖(最终cnt只加1),不同仿真器甚至同一仿真器的不同运行都可能得到不同结果,行为完全不可控。

场景B:操作依赖执行顺序(比如打印、任务调用)

如果task1task2包含依赖执行顺序的操作(比如$display打印、自定义任务调用),两个always块的执行顺序不确定,而单个always块是严格顺序执行的。

示例:

// 两个always块的写法
always @(posedge clk) begin
    $display("Task1: reg_a = %d", reg_a); // task1打印
end
always @(posedge clk) begin
    $display("Task2: reg_b = %d", reg_b); // task2打印
end

// 单个always块的写法
always @(posedge clk) begin
    $display("Task1: reg_a = %d", reg_a);
    $display("Task2: reg_b = %d", reg_b);
end
  • 单个always块中,打印顺序固定是先输出Task1的内容,再输出Task2的内容。
  • 两个always块的情况:仿真器可能先执行第一个块,也可能先执行第二个块,所以打印的顺序是随机的,这就导致行为不一致。

总结

  • task1task2无共享变量的独立操作,且都使用非阻塞赋值,那么两个always块和单个always块功能完全一致。
  • 若存在共享变量的阻塞赋值同一变量被多个块修改,或者依赖执行顺序的操作,两者的行为会存在差异,甚至出现不可控的结果。

内容的提问来源于stack exchange,提问作者BlueHorn07

火山引擎 最新活动