4. Verilog基础回顾(上):模块结构、端口定义、数据类型、连续赋值
各位同学,咱们今天聊聊Verilog最基础的东西。说实话,这部分内容看起来简单,但我在带新人时发现,很多bug都出在这些“常识”上。你想想看,如果连模块怎么搭、端口怎么接都没搞明白,后面写testbench肯定要翻车。
4.1 模块结构——Verilog的“骨架”
一个Verilog模块,说白了就是一个“黑盒子”。它定义了输入输出,以及内部怎么干活。我习惯把模块想象成一个芯片的封装——外面露出来的是引脚(端口),里面藏着的是逻辑(功能)。
最基本的模块长这样:
module counter (
input wire clk, // 时钟
input wire rst_n, // 复位,低有效
output reg [3:0] count // 4位计数器输出
);
// 内部逻辑写在这里
endmodule
嗯,这里要注意几个点:
- module/endmodule:成对出现,就像括号一样,少一个编译器就报错。我曾经见过有人把endmodule拼成endmodle,找了半天bug……
- 模块名:建议用有意义的英文,别用a、b、c。我在项目中吃过亏,三个月后回看代码,完全不知道“module top1”是干嘛的。
- 端口列表:写在括号里,每个端口都要说明方向(input/output/inout)。
4.2 端口定义——给“引脚”起名字
端口定义有两种写法,我个人更推荐ANSI风格(也就是上面那种)。为什么?因为代码更紧凑,一眼就能看到端口的方向和类型。
另一种是传统风格,现在用得少了:
module counter (clk, rst_n, count);
input clk;
input rst_n;
output [3:0] count;
// 内部再声明 reg 或 wire
reg [3:0] count;
endmodule
你看,这种写法把端口声明和内部类型声明分开了,代码一长就容易乱。我建议新手直接学ANSI风格,少走弯路。
端口方向就三种:
| 方向 | 含义 | 举个栗子 |
|---|---|---|
| input | 输入,从外部进来 | 时钟、复位、数据输入 |
| output | 输出,送到外部去 | 计数结果、状态标志 |
| inout | 双向,既能进也能出 | I2C的SDA线、三态总线 |
4.3 数据类型——wire和reg,到底怎么选?
这个问题,我几乎每次面试新人都会问。很多人背了答案:“wire是线网,reg是寄存器”。但实际用起来,还是搞混。
说白了,记住一条黄金法则:
- wire:用
assign连续赋值,或者连接模块端口。它不存储值,只是“导线”。 - reg:用在
always块里,被赋值。它存储值,可以想象成一个小容器。
但注意!reg不一定是寄存器。在组合逻辑的always块里,reg综合出来只是导线。这个坑我当年也踩过——以为reg一定会生成触发器,结果综合出来一堆LUT。
看个例子就明白了:
wire a, b; // 导线
wire sum; // 导线
reg c; // 可以是寄存器,也可以是组合逻辑
// 连续赋值:用wire
assign sum = a + b;
// 时序逻辑:用reg,生成触发器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
c <= 1'b0;
else
c <= a & b;
end
4.4 连续赋值(assign)——组合逻辑的“直通车”
assign语句,说白了就是“一有风吹草动,立刻更新”。它描述的是组合逻辑,没有时钟,没有边沿触发。
基本语法:
assign 目标信号 = 表达式;
举个例子:
module adder (
input wire [3:0] a, b,
output wire [4:0] sum // 多1位防止溢出
);
assign sum = a + b;
endmodule
这里sum是wire类型,用assign赋值。只要a或b变了,sum立刻跟着变。这就是“连续”的含义——赋值是持续的,不是一次性的。
assign可以做的事情很多:
- 算术运算:
assign y = a + b - c; - 逻辑运算:
assign flag = (a > b) & enable; - 位运算:
assign data_out = {data_in[3:0], 4'b0}; - 三态门:
assign bus = enable ? data : 1'bz;
4.5 知识体系总览
为了让你更直观地理解这四块内容的关系,我画了张图:
这张图把四个知识点串起来了。你看,模块是外壳,端口是接口,wire/reg是材料,assign是连接方式。四者缺一不可。
最后说一句:Verilog基础就像盖房子的地基。地基没打好,后面盖再高的楼也悬。我建议你把这章的内容亲手敲一遍代码,别光看。仿真跑通了,才算真懂了。