在 HDL(以 Verilog / SystemVerilog 为主)里,assign和 always都用于描述硬件行为 ,但建模层次、触发方式、可综合性和适用场景都有明显区别。
下面从几个关键维度系统对比说明。
一、本质区别一句话总结
**
assign描述的是"组合逻辑的连续赋值",
always描述的是"在某个触发条件下执行的一段过程性语句"。**
二、基本语法与模型差异
1️⃣ assign(连续赋值)
assign y = a & b;
-
属于 连续赋值(Continuous Assignment)
-
右侧表达式 任一信号变化,左侧立即更新
-
类似一条硬连线
✅ 特点:
-
无时钟
-
无顺序
-
并行执行
-
只适合组合逻辑
2️⃣ always(过程块)
always @(*) begin
y = a & b;
end
或:
always @(posedge clk) begin
q <= d;
end
-
属于 过程性语句(Procedural Block)
-
只有在 敏感列表触发时 才执行
-
内部可以使用:
-
if / else -
case -
阻塞(
=) / 非阻塞(<=)赋值
-
✅ 特点:
-
可描述组合逻辑
-
也可描述时序逻辑
-
更接近"软件思维方式"
三、核心区别对比表
| 对比项 | assign |
always |
|---|---|---|
| 类型 | 连续赋值 | 过程块 |
| 触发方式 | 信号变化立即生效 | 敏感列表触发 |
| 是否有时钟 | ❌ 没有 | ✅ 可以有 |
| 是否可描述时序逻辑 | ❌ 不行 | ✅ 可以 |
| 是否支持条件/分支 | ❌ 不支持 | ✅ 支持 |
| 赋值符号 | = |
=或 <= |
| 可综合性 | ✅ 仅限组合逻辑 | ✅ 组合 + 时序 |
| 典型用途 | 简单组合逻辑 | 复杂逻辑 / 寄存器 |
四、组合逻辑:两种写法对比
✅ 用 assign
assign y = sel ? a : b;
✅ 用 always @(*)
always @(*) begin
if (sel)
y = a;
else
y = b;
end
📌 等价,但:
-
assign:简洁直观 -
always:更适合复杂逻辑
五、时序逻辑只能用 always
always @(posedge clk) begin
if (!rst_n)
q <= 0;
else
q <= d;
end
❌ 以下写法是非法的 / 不可综合的:
assign q = posedge clk ? d : q; // 错误
六、阻塞赋值 vs 非阻塞赋值(always 内)
在 always中:
always @(*) begin
a = b; // 阻塞赋值
end
always @(posedge clk) begin
q <= d; // 非阻塞赋值
end
-
组合逻辑 :推荐
= -
时序逻辑 :必须
<=
⚠️ assign中只能使用 =
七、常见错误与注意事项
❌ 同一信号被多次驱动
assign a = b;
assign a = c; // 错误:多驱动
always @(*) a = b;
always @(*) a = c; // 同样错误
✝ latch 风险(always 中)
always @(*) begin
if (en)
y = a;
end
❌ 会推断锁存器(latch)
✅ 正确写法:
always @(*) begin
if (en)
y = a;
else
y = b;
end
八、什么时候用哪个?
✅ 用 assign的情况
-
简单组合逻辑
-
信号连线
-
不需要条件判断
✅ 用 always的情况
-
复杂组合逻辑
-
时序逻辑(寄存器)
-
需要
if / case -
状态机
九、一句话总结
**
assign是"线级建模",
always是"行为级建模";简单用
assign,复杂用always。**