3. Verilog基础回顾(一):模块结构、数据类型、连续赋值与过程赋值
各位同学,咱们今天聊聊Verilog最基础的东西。说实话,很多老工程师干了十年,回头一看,最容易出bug的地方反而是这些基础概念。我见过太多人把wire和reg搞混,最后仿真跑出来一堆红叉叉。
好,咱们直接开干。
3.1 模块结构——Verilog的骨架
Verilog里,模块(module)是最基本的设计单元。你可以把它想象成一个黑盒子:有输入、有输出,里面装着逻辑。
一个典型的模块长这样:
module counter (
input wire clk, // 时钟
input wire rst_n, // 复位,低有效
output reg [3:0] count // 4位计数器输出
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
count <= 4'd0;
else
count <= count + 1'b1;
end
endmodule
结构上就三部分:
- 端口声明:告诉外界这个模块长什么样
- 内部信号与逻辑:干活的代码
- endmodule:收工
我个人习惯,端口列表里只写名字,类型和位宽放到下面单独声明。这样看起来清爽,也方便后期改位宽。
uart_rx、spi_master。别用驼峰,也别全大写。这是行业不成文的规矩。
3.2 数据类型——wire 和 reg 到底怎么选?
这是新手最容易懵的地方。我当年刚学的时候,也纠结过:什么时候用wire?什么时候用reg?
其实很简单:
- wire:连线。它不存储值,只是把两个点连起来。用在assign语句、模块端口连接。
- reg:寄存器。它存储值,用在always块里被赋值。
但注意!reg不一定会综合成寄存器。如果你在组合逻辑的always块里用reg,它综合出来只是一堆门电路,没有存储功能。嗯,这里要小心。
举个例子:
// 组合逻辑:reg综合成纯组合电路
reg [3:0] sum;
always @(*) begin
sum = a + b;
end
// 时序逻辑:reg综合成触发器
reg [3:0] count;
always @(posedge clk) begin
count <= count + 1'b1;
end
我在项目中遇到过有人把组合逻辑的reg误当成寄存器用,结果仿真对了,上板子就乱跳。查了两天才发现是综合出来没有锁存功能。
always @(a) 没写 b,结果综合出来一堆latch。后来我养成了习惯:组合逻辑一律用 always @(*),省心。
3.3 连续赋值——assign 的用法
连续赋值,说白了就是用 assign 把右边的表达式实时赋给左边的wire。它没有时钟概念,输入一变,输出立刻变。
wire [3:0] sum;
assign sum = a + b;
这里 sum 必须声明为wire。为什么?因为assign是持续驱动的,它不存储值,只是把a+b的结果“连”到sum上。
连续赋值适合做:
- 简单的组合逻辑(加法、比较、选择)
- 数据通路中的连线
- 三态门控制
你想想看,如果我要做一个多路选择器,用assign写起来多清爽:
wire [7:0] data_out;
assign data_out = (sel == 2'b00) ? data0 :
(sel == 2'b01) ? data1 :
(sel == 2'b10) ? data2 : data3;
一行搞定,不用always,不用case。简洁就是美。
3.4 过程赋值——always 块里的门道
过程赋值,就是在 always 块里给reg赋值。这里有两个大坑:阻塞赋值(=)和非阻塞赋值(<=)。
我直接说结论:
- 组合逻辑:用阻塞赋值
= - 时序逻辑:用非阻塞赋值
<=
为什么?
阻塞赋值是“先算完再赋值”,后面的语句要等前面的执行完。非阻塞赋值是“先算好右边,再统一赋值”,所有赋值同时发生。
看个例子你就明白了:
// 错误示范:时序逻辑用了阻塞赋值
always @(posedge clk) begin
a = b;
c = a; // 这里c拿到的是a的新值,不是旧值
end
// 正确做法:时序逻辑用非阻塞赋值
always @(posedge clk) begin
a <= b;
c <= a; // 这里c拿到的是a的旧值,符合寄存器行为
end
我曾经在项目里看到有人把阻塞和非阻塞混用,结果仿真和综合结果不一致。那哥们查了三天,最后发现是赋值方式用错了。所以,记住这个铁律:时序逻辑永远用 <=,组合逻辑永远用 =。
- wire 配 assign,reg 配 always
- 组合逻辑用 =,时序逻辑用 <=
- always @(*) 写组合逻辑,always @(posedge clk) 写时序逻辑
- 敏感列表写全,别漏信号
3.5 知识体系总览
为了让你对本章内容有个整体印象,我画了张图。你看看,其实Verilog基础就这些东西,环环相扣。
这张图把本章的核心逻辑串起来了。你记住:模块是外壳,wire和reg是血肉,assign和always是灵魂。搞懂这四样,Verilog就算入门了。
好,今天就到这儿。下一节咱们聊运算符和表达式,那又是另一片天地。