在一个 always @(posedge clk)
块中,所有的代码都是顺序执行的。但这不意味着它就像软件一样“一条一条执行”,因为最终是电路!电路是并行存在的!
Verilog 是硬件描述语言(HDL),你写的 if
看起来像判断语句,实际上是用来描述硬件行为的。比如赋值,就是描述电路连线和寄存器触发器之间的连接与更新方式。
always @(posedge clk) begin
if (a)
x <= 1;
if (b)
y <= 2;
end
x
和 y
是两个不同的变量(信号)
if (a)
和 if (b)
虽然写在一个 always 里,但它们控制的是不同的变量
所以它们最终在硬件上是并行存在的,谁满足条件谁赋值,互不干扰
你想象有两个电工:
电工A:看到 a=1
就把灯 x
打开
电工B:看到 b=1
就把灯 y
点亮
两个电工各干各的,不会打架。
always @(posedge clk) begin
if (a)
x <= 1;
if (b)
x <= 2;
end
现在两个 if
都是对 x
赋值
如果 a=1
,b=1
,那么两个赋值都触发,但最后一个有效
Verilog 顺序描述 => 最后一个赋值会“覆盖”前面那个 所以:
如果 a=1
,b=1
,x <= 2
如果 a=1
,b=0
,x <= 1
如果 a=0
,b=1
,x <= 2
还是两个电工:
电工A先来,把灯 x
调到1
电工B后到,把灯 x
调到2
最后你看到灯 x
是2,电工A的动作被“覆盖”了
这里如果有疑惑,可以看这篇博客:
【verilog】多个 if 控制同一个变量(后面会覆盖前面)非阻塞赋值真的并行吗?
always @(posedge clk) begin
if (a)
x <= 1;
x_d1 <= x; // 打拍:让x的值走一拍
if (b)
x <= 2;
end
x <= 1
先执行,但后面又 x <= 2
,会不会有问题?会被覆盖!除非你做数据隔离或选择性赋值
always @(posedge clk) begin
if (sel == 0)
x <= 1;
else if (sel == 1)
x <= 2;
case(sel)
2: x <= 3;
3: x <= 4;
endcase
end
如果 sel == 2
,那么:上面 if-else 不触发,case 触发 x <= 3
如果 sel == 1
,if-else 触发 x <= 2
,case 不触发
但是注意,如果你写多个能影响同一个变量的赋值语句,它们中间的优先级以代码顺序为准
情况 | 行为 | 说明 |
---|---|---|
多个 if 控制不同变量 | 并行 | 谁条件满足谁执行,互不干扰 |
多个 if 控制同一个变量 | 顺序 | 后面赋值覆盖前面 |
if/else + case 控制同一变量 | 顺序+可能冲突 | 后面的 case 会覆盖前面 if 的赋值 |
插入打拍语句 | 合法 | 不影响并行结构,但要注意覆盖风险 |
多个嵌套 if | 按照语句顺序 | 但并不表示电路是串行运行,是条件表达上的判断顺序 |
为了代码清晰和避免莫名其妙的赋值冲突:
推荐使用 if-else if-else
结构统一处理一个变量的所有情况
或者先保存中间变量,最后统一赋值
打拍信号最好和主赋值语句分开块写,减少混乱