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,一会儿用 counter
  • en 表示使能,不要混用 en、enable、ena
  • clr 表示清零,不要混用 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 命名规范知识体系

下面这张图,是我自己整理的命名规范知识体系。你可以把它当作一个检查清单,每次写代码前扫一眼:

命名规范知识体系 模块命名 端口命名 信号命名 小写 + 下划线 功能_版本_类型 不超过20字符 方向前缀:i_ / o_ / io_ 功能名 + 方向前缀 避免关键字冲突 有意义的英文单词 统一缩写规则 后缀表示类型:_n/_p/_d/_r 核心原则:可读性 > 简洁性 宁可名字长一点,也不要让人猜 好的命名 = 好的设计习惯 = 少加班

2.5 常见命名错误与避坑

最后,我把自己踩过的坑和见过的坑总结一下:

  1. 不要用 Verilog 关键字:比如 input、output、wire、reg 这些,都不能作为信号名
  2. 不要用数字开头:比如 1st_data 是非法的,应该写成 data_1st
  3. 不要用连续下划线:比如 data__valid,容易让人看花眼
  4. 不要大小写混用:比如 DataValid 和 data_valid 在 Verilog 里是同一个信号,但混用会让人困惑
  5. 不要用过于通用的名字:比如 temp、tmp、flag,除非你真的不知道它干嘛用的

我曾经犯过的错:在一个多时钟域的项目里,我把两个不同时钟域的信号都命名为 data_ready。结果在跨时钟域同步时,代码里引用了错误的那个,导致数据丢失。从那以后,我强制要求:不同时钟域的信号,命名里必须带上时钟域标识,比如 data_ready_clk_a 和 data_ready_clk_b。

好了,命名规范这部分就先聊到这儿。记住一句话:你写的代码,是给人看的,顺便给工具跑。 把名字起好,就是对自己和同事最大的温柔。

可可精品资料库