2. 命名规范(一):模块名、端口名、信号名的命名规则与最佳实践
说实话,我在刚入行那会儿,觉得命名这事儿挺虚的。
代码能跑通不就行了?名字长一点短一点,有什么关系?
直到有一次,我接手了一个前辈留下的项目。那个模块叫 top_module_v2_final_final,里面信号名全是 a1、b2、temp 这种。我花了整整三天才理清楚每个信号是干嘛的。嗯,从那以后,我再也不敢小看命名规范了。
核心观点:命名规范不是形式主义,它是代码的可读性、可维护性、可复用性的基石。一个规范的命名,能让你的同事(包括三个月后的你自己)少掉一大半头发。
2.1 模块命名规则
模块名,说白了就是你这个硬件模块的身份证。我个人习惯用 小写字母 + 下划线 的组合。为什么?因为 Verilog 本身不区分大小写,但全小写加下划线,在跨工具、跨平台时最不容易出问题。
我建议的命名格式:模块功能_版本_类型
- 功能描述:用英文单词概括模块的核心功能,比如
uart、fifo、spi_master - 版本标识:如果模块有多个版本,可以加
_v1、_v2后缀 - 类型后缀:比如
_top表示顶层模块,_wrapper表示封装模块
// 好的命名示例
module uart_rx_v2_top;
module spi_master_wrapper;
module ahb_to_apb_bridge;
// 差的命名示例
module top; // 太模糊,不知道是啥的顶层
module test; // 测试什么?
module my_module; // 毫无信息量
我的小技巧:模块名尽量控制在 20 个字符以内。太长的话,在波形图里根本看不全。我曾经见过一个模块叫 ddr3_controller_with_ecc_and_self_refresh_v3,每次看波形都要拖滚动条,太痛苦了。
2.2 端口命名规则
端口是模块与外界通信的接口。端口命名要遵循一个原则:一看就知道方向和数据含义。
我个人习惯用 方向前缀 + 功能名 的格式:
| 方向 | 前缀 | 示例 |
|---|---|---|
| 输入 | i_ 或 in_ |
i_clk, i_rst_n, in_data_valid |
| 输出 | o_ 或 out_ |
o_data, out_ready |
| 双向 | io_ 或 b_ |
io_sda, b_data_bus |
你想想看,如果端口名不加方向前缀,你在代码里看到 data 这个信号,还得去端口声明里翻它是 input 还是 output。加了前缀,一眼就能看出来。
// 规范的端口声明
module uart_rx (
input wire i_clk, // 时钟输入
input wire i_rst_n, // 异步复位,低有效
input wire i_rx_line, // 串行数据输入
output reg [7:0] o_data, // 接收到的数据
output wire o_data_valid // 数据有效标志
);
注意:我曾经见过有人用 in 和 out 作为端口名。这在 Verilog 里是关键字,会报编译错误!所以方向前缀一定要加下划线,比如 in_ 而不是 in。
2.3 信号命名规则
信号命名,是日常写代码时最频繁的操作。我总结了几个黄金法则:
2.3.1 使用有意义的英文单词
不要用拼音,更不要用单个字母。我见过有人写 xinhao_a、xinhao_b,这跟没命名一样。
// 好的信号名
wire [7:0] fifo_wr_data;
reg fifo_full_flag;
wire spi_cs_n; // n 表示低有效
// 差的信号名
wire [7:0] d; // 啥意思?
reg f; // flag?full?还是fifo?
wire cs; // 是高有效还是低有效?
2.3.2 统一缩写规则
同一个项目里,缩写要统一。比如:
cnt表示计数器,不要一会儿用cnt,一会儿用counteren表示使能,不要混用en、enable、enaclr表示清零,不要混用clr、clear、rst
2.3.3 信号后缀表示信号类型
这个习惯是我从一位老工程师那里学来的,非常实用:
| 后缀 | 含义 | 示例 |
|---|---|---|
_n |
低有效信号 | rst_n, cs_n |
_p |
脉冲信号(持续一个周期) | wr_p, rd_p |
_d |
延迟一拍后的信号 | data_d, valid_d |
_r |
寄存器输出 | cnt_r, state_r |
_w |
线网信号 | data_w, ready_w |
避坑指南:我曾经在一个项目里,把复位信号命名为 reset,没有加 _n 后缀。结果综合工具默认把它当成高有效复位,生成了错误的网表。从那以后,所有低有效信号我都强制加 _n 后缀,再也没出过这种问题。
2.4 命名规范知识体系
下面这张图,是我自己整理的命名规范知识体系。你可以把它当作一个检查清单,每次写代码前扫一眼:
2.5 常见命名错误与避坑
最后,我把自己踩过的坑和见过的坑总结一下:
- 不要用 Verilog 关键字:比如
input、output、wire、reg这些,都不能作为信号名 - 不要用数字开头:比如
1st_data是非法的,应该写成data_1st - 不要用连续下划线:比如
data__valid,容易让人看花眼 - 不要大小写混用:比如
DataValid和data_valid在 Verilog 里是同一个信号,但混用会让人困惑 - 不要用过于通用的名字:比如
temp、tmp、flag,除非你真的不知道它干嘛用的
我曾经犯过的错:在一个多时钟域的项目里,我把两个不同时钟域的信号都命名为 data_ready。结果在跨时钟域同步时,代码里引用了错误的那个,导致数据丢失。从那以后,我强制要求:不同时钟域的信号,命名里必须带上时钟域标识,比如 data_ready_clk_a 和 data_ready_clk_b。
好了,命名规范这部分就先聊到这儿。记住一句话:你写的代码,是给人看的,顺便给工具跑。 把名字起好,就是对自己和同事最大的温柔。