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 知识体系总览

下面这张图把这一节的核心内容串起来了。你可以把它当作一个快速参考,写代码的时候对照着看。

命名规范(下)知识体系 参数与宏定义 状态机命名 文件命名 全大写 + 下划线 带单位或含义 宏定义加项目前缀 状态含义 + _ST 后缀 用 localparam 定义 state / next_state 命名 文件名 = 模块名 一个文件一个模块 统一后缀 .v / .sv 核心思想:见名知意,一看就懂

3.5 避坑指南

最后,我把自己踩过的坑总结一下,你写代码的时候多留个心眼。

我曾经犯过的错:

  • 用 `define 定义状态,结果两个模块状态名冲突,编译报错查了一下午。
  • 参数名写 P_CNT,三个月后自己都忘了这个参数是干嘛的。
  • 文件名跟模块名不一致,版本管理时找不到对应文件。
  • 状态机用 S0、S1 命名,代码评审被同事吐槽了一整天。

嗯,这些教训都是用加班换来的。你记住了,以后就能少走弯路。命名规范看着是小事,但好的命名习惯能让你的代码质量提升一个档次。说白了,规范的命名就是给自己和同事省时间。

可可精品资料库