文章目录
Verilog 的三种主要描述方式
1. Continuous Assignment
assign y = a & b;
适合简单组合逻辑。
2. Procedural Assignment always @(*)
always @(*) begin
...
end
- 适合复杂组合逻辑。
-
很多组合逻辑无法用一个简单表达式描述。很难写成一个优雅的
assign。 -
于是 Verilog 提供了
always @(*) begin...end用类似软件语言的写法描述组合逻辑。 -
always @(*) begin y = a & b; end综合为AND Gate -
根据敏感列表,
always @(*)综合器无法推导出 DFF,可导出组合逻辑 + Latch。
-
3. Sequential Logic
always @(posedge clk)
适合寄存器。
always_comb
-
always_comb是 SystemVerilog 专门用于描述**组合逻辑(Combinational Logic)**的过程块,可以看作是对 Verilog 中always @(*)的增强版和更安全版。 -
always_comb 中如果代码推导出了 latch,那就是编码错误,工具通常会报错或强警告。
-
基本对应关系
| Verilog | SystemVerilog |
|---|---|
always @(*) | always_comb |
always @(posedge clk) | always_ff @(posedge clk) |
| 锁存器逻辑 | always_latch |
工程上通常遵循:
| 场景 | 推荐 |
|---|---|
| 单行逻辑表达式 | assign |
| MUX | assign 或 always_comb |
if/case/for 等复杂逻辑 | always_comb |
| 寄存器 | always_ff |
典型写法
MUX
always_comb begin
if (sel)
y = a;
else
y = b;
end
Case状态转移
always_comb begin
case(state)
IDLE: next_state = RUN;
RUN : next_state = STOP;
default: next_state = IDLE;
endcase
end
默认赋值防锁存器
推荐:
always_comb begin
y = 0;
if (en)
y = a;
end
保证所有路径都赋值。
ALU 控制逻辑示例
assign写法
- 逻辑包含运算、条件判断、溢出检测、默认赋值,如果用
assign,会变成大量嵌套三元运算符:
assign result =
(op == ALU_ADD) ? (a+b) :
(op == ALU_SUB) ? (a-b) :
(op == ALU_AND) ? (a&b) :
(op == ALU_OR ) ? (a|b) :
(op == ALU_XOR) ? (a^b) :
(op == ALU_SLT) ? ($signed(a)<$signed(b)) :
32'd0;
verilog写法
module alu (
input [31:0] a,
input [31:0] b,
input [2:0] op,
output reg [31:0] result,
output reg overflow,
output zero
);
localparam ALU_ADD = 3'd0;
localparam ALU_SUB = 3'd1;
localparam ALU_AND = 3'd2;
localparam ALU_OR = 3'd3;
localparam ALU_XOR = 3'd4;
localparam ALU_SLT = 3'd5;
always @(*) begin
// 默认值
result = 32'd0;
overflow = 1'b0;
case(op)
ALU_ADD: begin
result = a + b;
overflow =
(~a[31] & ~b[31] & result[31]) |
( a[31] & b[31] & ~result[31]);
end
ALU_SUB: begin
result = a - b;
overflow =
(~a[31] & b[31] & result[31]) |
( a[31] & ~b[31] & ~result[31]);
end
ALU_AND:
result = a & b;
ALU_OR:
result = a | b;
ALU_XOR:
result = a ^ b;
ALU_SLT:
result = ($signed(a) < $signed(b));
default: begin
result = 32'd0;
overflow = 1'b0;
end
endcase
end
assign zero = (result == 32'd0);
endmodule
- Verilog 为什么必须写
output reg因为中的result是在过程块(procedural block)里赋值的。Verilog 规定:
assign → wire
always → reg
- 所以必须写:
output reg [31:0] result;
- 否则编译报错:
output [31:0] result;
always @(*) begin
result = a + b;
end
- 但是这里的 reg 不是寄存器。综合结果为组合逻辑

- 改为clk的情况:

SystemVerilog 写法(推荐)
typedef enum logic [2:0] {
ALU_ADD,
ALU_SUB,
ALU_AND,
ALU_OR,
ALU_XOR,
ALU_SLT
} alu_op_t;
module alu (
input logic [31:0] a,
input logic [31:0] b,
input alu_op_t op,
output logic [31:0] result,
output logic zero,
output logic overflow
);
always_comb begin
// 默认值,避免Latch
result = '0;
overflow = 1'b0;
case(op)
ALU_ADD: begin
result = a + b;
overflow =
(~a[31] & ~b[31] & result[31]) |
( a[31] & b[31] & ~result[31]);
end
ALU_SUB: begin
result = a - b;
overflow =
(~a[31] & b[31] & result[31]) |
( a[31] & ~b[31] & ~result[31]);
end
ALU_AND:
result = a & b;
ALU_OR:
result = a | b;
ALU_XOR:
result = a ^ b;
ALU_SLT:
result = ($signed(a) < $signed(b));
default:
result = '0;
endcase
end
assign zero = (result == 32'd0);
endmodule
现代编码风格对比
| Verilog | SystemVerilog |
|---|---|
reg [31:0] result; | logic [31:0] result; |
always @(*) | always_comb |
always @(posedge clk) | always_ff @(posedge clk) |
parameter IDLE=0; | typedef enum ... |

3136

被折叠的 条评论
为什么被折叠?



