4. 模块命名规范:顶层模块命名、子模块命名、实例化命名、信号命名前缀/后缀规则
模块命名这事儿,看着简单,其实特别容易踩坑。我见过不少项目,代码写得很漂亮,但一看模块名,乱七八糟的,根本分不清谁是谁。说白了,命名规范就是给代码贴标签,标签贴好了,后面的人一看就懂,不用猜。
我个人习惯,命名规范要解决三个问题:这个模块是干什么的?它在哪个层级?它跟谁连? 下面我一个个说。
4.1 顶层模块命名:一眼看出这是老大
顶层模块,就是整个芯片或整个子系统的入口。命名要体现它的功能归属和层级地位。
我建议的格式:<项目名>_<功能名>_top
举个例子:
// 项目名:pll_clk_gen
// 功能:时钟生成
// 顶层命名
module pll_clk_gen_top (
input wire clk_ref,
input wire rst_n,
output wire pll_clk_out,
output wire pll_locked
);
为什么加 _top?因为你在仿真脚本、综合脚本里,一眼就能找到顶层。我在项目中遇到过,有人把顶层模块叫 pll_clk_gen,结果子模块也叫 pll_clk_gen,两个名字一模一样,综合工具直接报错。嗯,这种低级错误,加个 _top 就能避免。
4.2 子模块命名:层级清晰,功能明确
子模块命名,我习惯用 <父模块名>_<子功能名> 的格式。这样从名字就能看出它在哪个层级下面。
比如,PLL 时钟生成模块下面,可能有这几个子模块:
// 父模块:pll_clk_gen_top
// 子模块命名
module pll_clk_gen_pll_core ( ... ); // PLL 核心
module pll_clk_gen_divider ( ... ); // 分频器
module pll_clk_gen_glitch_free( ... ); // 无毛刺切换
module pll_clk_gen_status ( ... ); // 状态监控
你看,每个子模块前面都带着 pll_clk_gen_,一看就知道是同一个父模块下的。你想想看,如果子模块叫 divider,那这个 divider 到底是哪个模块的?分不清。
我曾经在一个项目里,看到有人把子模块命名为 u_div、u_pll,结果代码里一堆 u_xxx,根本不知道哪个是哪个。后来重构的时候,光改名字就花了两天。所以,子模块名一定要带父模块前缀。
4.3 实例化命名:跟模块名区分开
实例化命名,说白了就是给模块的“实例”起个名字。很多人喜欢用 u_xxx 的格式,我个人不太推荐。为什么?因为 u_ 太通用,容易重复。
我建议的格式:u_<模块名>,但模块名要去掉父模块前缀。
看个例子:
// 模块定义
module pll_clk_gen_divider ( ... );
// 实例化
pll_clk_gen_divider u_divider (
.clk_in (clk_pll),
.clk_out(clk_div)
);
这里实例名叫 u_divider,而不是 u_pll_clk_gen_divider。为什么?因为实例名太长,代码可读性反而下降。而且,在父模块内部,你本来就知道这个实例属于哪个模块,所以用 u_divider 就够了。
u_ 开头,模块名用 _top 或功能名结尾,两者自然区分开。
4.4 信号命名前缀/后缀规则:一看就知道类型
信号命名,我习惯用前缀表示方向或类型,后缀表示功能或属性。这样,看到信号名就能猜出它大概是什么。
下面是我常用的规则:
| 前缀/后缀 | 含义 | 示例 |
|---|---|---|
clk_ |
时钟信号 | clk_ref, clk_pll |
rst_ |
复位信号 | rst_n, rst_sync |
en_ |
使能信号 | en_pll, en_div |
cfg_ |
配置信号 | cfg_div_ratio, cfg_sel |
_n |
低电平有效 | rst_n, en_n |
_i |
输入信号 | clk_ref_i, cfg_i |
_o |
输出信号 | pll_clk_o, locked_o |
_b |
双向信号 | data_b, sda_b |
举个例子,一个完整的 PLL 模块端口信号:
module pll_clk_gen_top (
input wire clk_ref_i, // 参考时钟输入
input wire rst_n, // 复位,低有效
input wire [7:0] cfg_div_ratio_i, // 分频比配置
output wire pll_clk_o, // PLL 时钟输出
output wire locked_o // 锁定指示
);
你看,每个信号名都带着前缀或后缀,一看就知道是输入还是输出,是时钟还是配置。我在项目中遇到过,有人把输入输出信号混着命名,结果写 testbench 的时候,老是搞错方向。后来我强制团队用 _i、_o 后缀,再也没出过这种问题。
clk_ref_i_o 这种,既带 _i 又带 _o,反而让人困惑。一个信号只加一个方向后缀就够了。
4.5 知识体系结构图
下面这张图,把模块命名的核心逻辑串起来了。你可以把它当作一个检查清单:
这张图从顶层到底层,把命名规范串成了一条线。你写代码的时候,照着这个顺序检查一遍,基本不会出错。
4.6 避坑指南
最后,分享几个我踩过的坑:
- 不要用数字开头:比如
1st_pll,Verilog 语法不支持。我见过有人这么写,编译直接报错。 - 不要用关键字:比如
module、input、wire这些,千万别当信号名用。我曾经在代码里看到wire module,嗯,编译直接挂了。 - 不要用拼音:比如
pll_shuru、pll_shuchu,看着很别扭。统一用英文,哪怕简单点也行。 - 大小写统一:我习惯用小写加下划线,比如
pll_clk_out。不要混用,比如PllClkOut和pll_clk_out同时出现,容易混淆。