3. Verilog基础与组合逻辑:模块化设计、assign语句、always块、wire与reg区别、常用运算符
好,咱们正式开始动手写代码了。
这一章,我带你搞定Verilog里最基础、也最核心的几个概念。说白了,就是组合逻辑的几种写法。你想想看,高频交易里那些加法器、比较器、数据选择器,本质上都是组合逻辑。搞懂了这些,你就能看懂大部分FPGA代码了。
3.1 模块化设计——把大问题拆小
FPGA设计不是写一个超级大的代码块。我习惯把整个系统拆成一个个小模块。每个模块干一件事,然后像搭积木一样拼起来。
举个例子,一个简单的加法器模块:
module adder (
input [7:0] a, // 输入a,8位宽
input [7:0] b, // 输入b,8位宽
output [8:0] sum // 输出和,9位宽(防止溢出)
);
assign sum = a + b;
endmodule
你看,module和endmodule就是模块的边界。端口列表里声明了输入输出。内部实现就是一行assign。
3.2 assign语句——连续赋值
assign是组合逻辑最常用的写法。它的意思是:只要右边信号变了,左边立刻更新。没有延迟,没有时钟。
assign y = a & b; // y = a 与 b
assign z = a ^ b; // z = a 异或 b
这里要注意:assign左边必须是wire类型,不能是reg。为什么?因为assign是连续驱动,wire天生就是干这个的。
assign左边写成了reg,结果综合报错。后来养成了习惯:看到assign,左边一定是wire。
3.3 always块——更灵活的组合逻辑
always块也能描述组合逻辑。但写法跟assign不太一样。
always @(*) begin
if (sel)
y = a;
else
y = b;
end
这里的@(*)表示:只要块里用到的信号变了,就重新执行。对于组合逻辑,always块里必须用阻塞赋值(=),不能用非阻塞(<=)。
嗯,这里要注意:always块里的变量必须是reg类型。但别被名字骗了——组合逻辑里的reg并不生成寄存器,它只是语法要求。
| 写法 | 左边类型 | 适用场景 |
|---|---|---|
| assign | wire | 简单组合逻辑(加法、与或非) |
| always @(*) | reg | 复杂组合逻辑(if-else、case) |
3.4 wire与reg的区别——别再搞混了
很多初学者在这栽跟头。我直接说结论:
- wire:连线。只能被
assign或模块端口驱动。不能存储值。 - reg:变量。可以在
always块里赋值。但组合逻辑里它不存值,只是语法需要。
举个例子:
wire w1;
reg r1;
assign w1 = a & b; // 正确
always @(*) begin
r1 = a & b; // 正确
end
// assign r1 = a & b; // 错误!assign左边不能是reg
// always @(*) begin
// w1 = a & b; // 错误!always块里不能给wire赋值
// end
3.5 常用运算符——够用就行
Verilog的运算符很多,但高频交易里常用的就这些:
| 类别 | 运算符 | 说明 |
|---|---|---|
| 算术 | + - * / | 加减乘除。注意乘法会消耗大量LUT |
| 位运算 | & | ^ ~ | 与、或、异或、非。组合逻辑主力 |
| 逻辑 | && || ! | 逻辑与、或、非。用于条件判断 |
| 比较 | > < == != | 大于、小于、等于、不等 |
| 移位 | << >> | 左移、右移。乘2除2的快速实现 |
| 拼接 | {a, b} | 把多个信号拼成一个 |
举个例子,一个简单的数据选择器:
module mux2to1 (
input sel,
input [7:0] d0, d1,
output [7:0] y
);
assign y = sel ? d1 : d0;
endmodule
你看,一行assign加一个三目运算符,搞定。
3.6 本章知识体系
下面这张图,帮你理清本章的核心逻辑:
这张图把本章的知识点串起来了。你想想看,从模块化设计开始,到两种赋值方式(assign和always),再到对应的数据类型(wire和reg),最后用运算符实现具体功能。这就是组合逻辑的完整链路。
- 模块化:每个模块干一件事
- assign:连续赋值,左边wire
- always:过程赋值,左边reg
- wire是连线,reg是变量(组合逻辑里不存值)
- 运算符:够用就行,别炫技
好了,这一章就到这。下一章我们聊时序逻辑——就是带时钟的那种。到时候你会看到always @(posedge clk)这种写法,跟组合逻辑的always @(*)完全不一样。