3、命名规范(下):参数与宏定义命名、状态机命名、文件命名规范
好,咱们接着聊命名。上一节我们把信号和模块的命名捋了一遍,这一节我重点说说参数、宏定义、状态机还有文件命名。这些地方看着不起眼,但踩坑的人真不少。我自己就吃过亏,所以今天把这些经验掏出来跟你分享。
3.1 参数与宏定义命名:别让常量变成“魔法数字”
先问个问题:你写代码的时候,有没有直接用过 #10、8'd255 这种硬编码?嗯,我刚入行那会儿也这么干。后来有一次做项目,一个时钟分频系数在代码里出现了七八次,改一个地方漏了三个,仿真结果全乱套。从那以后,我养成了一个习惯——所有常量,必须用参数或宏定义给它起个名字。
参数命名:全大写 + 下划线
我个人习惯用 `define 做全局宏定义,用 parameter 做模块内部参数。命名风格统一为全大写字母,单词之间用下划线隔开。
推荐风格:
// 宏定义
`define DATA_WIDTH 8
`define CLK_FREQ_MHZ 50
`define MEM_DEPTH 1024
// 参数
parameter P_AXI_ADDR_WIDTH = 32;
parameter P_FIFO_DEPTH = 256;
parameter P_TIMEOUT_CYCLES = 1000;
为什么用全大写?因为一眼就能看出来这是个常量,不是信号。我在项目中见过有人把参数写成小写,结果跟普通信号混在一起,代码读起来特别费劲。
注意:宏定义没有作用域限制,整个文件都可见。所以命名一定要加前缀,避免冲突。比如项目名缩写 + 下划线:`define PROJ_A_DATA_WIDTH。
参数命名要带单位或含义
我曾经接手过一个代码,里面有个参数叫 P_CNT。你猜这是计数器的最大值还是初始值?猜不出来吧。后来我花了半天才从上下文里推断出来。所以我的建议是:参数名里最好带上单位或明确含义。
// 不好的命名
parameter P_CNT = 100;
// 好的命名
parameter P_TIMEOUT_MS = 100; // 超时时间,单位毫秒
parameter P_FIFO_DEPTH = 256; // FIFO深度
parameter P_DIV_FACTOR = 5; // 分频系数
3.2 状态机命名:让状态一目了然
状态机是数字芯片设计的核心。我见过最糟糕的状态机命名是什么?S0、S1、S2…… 你想想看,这种命名谁能看懂?每次看代码都得对着状态转移图,效率极低。
状态命名:状态含义 + _ST
我个人习惯用 状态含义_ST 的格式。比如一个 SPI 控制器的状态机:
// 状态定义
localparam IDLE_ST = 3'b000;
localparam START_ST = 3'b001;
localparam DATA_ST = 3'b010;
localparam WAIT_ACK_ST = 3'b011;
localparam DONE_ST = 3'b100;
你看,IDLE_ST 一看就知道是空闲状态,DATA_ST 是数据传输状态。根本不需要查文档。
小技巧:状态变量名用 state 或 current_state,下一状态用 next_state。这样写 always 块的时候,代码结构特别清晰。
状态机编码:用 localparam 别用 `define
状态定义一定要用 localparam,不要用 `define。为什么?因为 localparam 只在当前模块有效,不会污染全局命名空间。我曾经在一个大项目里看到有人用 `define 定义状态,结果两个模块的状态名冲突了,编译报错查了半天。
// 正确做法
localparam [2:0] IDLE_ST = 3'b000;
localparam [2:0] START_ST = 3'b001;
localparam [2:0] DATA_ST = 3'b010;
// 错误做法(不推荐)
`define IDLE_ST 3'b000
`define START_ST 3'b001
3.3 文件命名:模块名就是文件名
文件命名看似简单,但团队协作时最容易出乱子。我记得有一次,一个同事把同一个模块的 RTL 代码和 testbench 都放在一个文件里,文件名还叫 test.v。结果版本管理的时候,根本分不清哪个是哪个。
核心原则:一个文件只放一个模块
文件名必须与模块名完全一致。比如模块名叫 spi_master,那文件名就是 spi_master.v。这样你看到文件名就知道里面是什么模块,不需要打开文件看。
| 模块名 | 文件名 | 说明 |
|---|---|---|
| uart_tx | uart_tx.v | UART发送模块 |
| uart_rx | uart_rx.v | UART接收模块 |
| fifo_sync | fifo_sync.v | 同步FIFO |
| top_chip | top_chip.v | 顶层模块 |
文件后缀要统一
我建议用 .v 作为 Verilog 文件后缀,.sv 作为 SystemVerilog 文件后缀。别混用,也别用 .vl、.vlg 这种奇怪的后缀。工具链识别起来也方便。
文件命名规范总结:
- 文件名 = 模块名 + 后缀
- 全部小写,单词用下划线分隔
- 一个文件只放一个模块
- testbench 文件加
_tb后缀,如spi_master_tb.v - 顶层文件用
top_前缀,如top_chip.v
3.4 知识体系总览
下面这张图把这一节的核心内容串起来了。你可以把它当作一个快速参考,写代码的时候对照着看。
3.5 避坑指南
最后,我把自己踩过的坑总结一下,你写代码的时候多留个心眼。
我曾经犯过的错:
- 用
`define定义状态,结果两个模块状态名冲突,编译报错查了一下午。 - 参数名写
P_CNT,三个月后自己都忘了这个参数是干嘛的。 - 文件名跟模块名不一致,版本管理时找不到对应文件。
- 状态机用
S0、S1命名,代码评审被同事吐槽了一整天。
嗯,这些教训都是用加班换来的。你记住了,以后就能少走弯路。命名规范看着是小事,但好的命名习惯能让你的代码质量提升一个档次。说白了,规范的命名就是给自己和同事省时间。