Menu

Verilog 注释:单行、多行与文档风格

如何在 Verilog 中写单行和多行注释,以及数字电路设计者用来让模块在不断变大时依然易读的文档写法。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

两种写法

Verilog 恰好支持两种注释语法,都照搬自 C:

语法就这么多。没有 Python 那样的 #,没有 VHDL 那样的 --,也没有 Lisp 风格。只有 // 和 /* ... */。

什么时候用哪种

几乎每次你都会用 //。它更短,不会因为忘了结束符而出问题,而且和 Verilog 的书写方式天然契合(每行一个声明,注释写在同一行或相邻行):

output reg [7:0] data,  // byte we shift out on tx_serial
output reg       valid, // high while data is being transmitted
input  wire      ready  // downstream consumer is ready to accept

/* ... */ 主要用于两种情况:文件顶部的大段文件头,以及调试时临时禁用一段代码。禁用代码这种用法有风险,请继续往下读。

##“不能嵌套”的陷阱

块注释不能嵌套。如果你想注释掉一段已经包含块注释的代码,第一个 */ 会结束外层的块,而不是内层的:

/* outer
   /* inner */    // <-- this */ closes the outer comment
   still active in the source as far as the parser is concerned
*/

结果就是在意想不到的地方报语法错误,而那一行看起来完全没问题。

需要禁用一段代码时,优先选择以下方式之一:

  1. 在每一行前加 //。 大多数编辑器按一个快捷键就能做到。

  2. 使用预处理器保护:

    `ifdef DISABLED
        // code that should not compile
    `endif
    

第二种写法也是在一个文件里保留多种构建配置的方法。

综合指令:不只是注释的注释

厂商工具把特殊格式的注释当作额外的指令。仿真器仍然忽略它们,但综合工具会读取:

// synthesis translate_off
initial begin
    $display("simulator-only setup");
end
// synthesis translate_on

这两条 pragma 告诉综合工具“跳过这两个标记之间的所有内容”。具体写法因厂商而异(synthesis、synopsys、pragma、xilinx 等),请查阅你所用工具的文档。需要记住的是:Verilog 中的注释有时是有实际作用的。

文件头约定

长期维护的 Verilog 文件几乎都以一个文件头开始。具体格式由团队规定,下面是一个典型的例子:

// -----------------------------------------------------------------------------
// Module      : uart_tx
// Description : 8-N-1 UART transmitter. Accepts a byte on `data` when `valid`
//               is asserted and shifts it out on `serial_out`. `baud_tick`
//               must pulse once per baud period.
// Ports       : clk        - system clock
//               reset_n    - active-low synchronous reset
//               baud_tick  - 1-cycle pulse at each baud interval
//               data       - byte to transmit
//               valid      - asserted to start a transmission
//               serial_out - the wire that leaves the FPGA
//               busy       - high while a frame is in flight
// Author      : example@team
// Revision    : 2026-05-26 - initial version
// -----------------------------------------------------------------------------

module uart_tx (
    input  wire       clk,
    input  wire       reset_n,
    input  wire       baud_tick,
    input  wire [7:0] data,
    input  wire       valid,
    output reg        serial_out,
    output reg        busy
);
    // ... body ...
endmodule

重点不在装饰。重点在于一年后打开这个文件的人(很可能就是你自己)读上四行,就能知道它做什么、需要什么输入、产生什么输出。项目越大,这样做的回报越大。

真正有用的行内注释

一个常见的错误是去注释代码已经表达清楚的“做了什么”:

// bad - the comment restates the code
count <= count + 1;   // increment count

// better - the comment explains why this line is here
count <= count + 1;   // free-running cycle counter for timestamping

注释真正擅长的,是解释代码本身无法表达的“为什么”:某个古怪的编码遵循规范的哪一节,为什么某个寄存器比看上去多一位,为什么 default 分支设成 'x 而不是 '0。把注释用在这些地方,显而易见的事交给代码本身。

动手试试

下面这段代码包含了所有的注释形式。点击“运行”:输出不会让你意外,但文件结构值得一看:

现在你已经见过 Verilog 支持的所有注释形式。其余文档会按你预期的方式使用它们:长模块顶部放文件头,端口旁边写单行 //,只有需要记录一整段推理时才用块注释。

常见问题

Verilog 中如何写注释?

Verilog 支持两种注释风格,都继承自 C。// 开始的注释一直延续到行尾;/* ... */ 包住一个多行块。编译器会忽略标记之间的所有内容;无论是可综合的文件还是只用于仿真的文件,两种风格都同样有效。

Verilog 注释会被综合吗?

注释在解析之后就不存在了:综合工具和仿真器一样会把它们丢掉。唯一的例外是综合指令(pragma):像 // synthesis translate_off 这种特殊格式的注释,厂商工具会把它识别为指令。pragma 的语法因工具而异,不属于标准 Verilog。

Verilog 注释可以嵌套吗?

不可以。初学者想注释掉一段已经包含 /* ... */ 的代码时,常常在这里栽跟头:里面的第一个 */ 就会结束外层注释,剩下的部分又变回了有效代码。可以在每一行前加 //;如果确实需要禁用一大段,也可以用 `ifdef SOMETHING_FALSE / `endif 把整段包起来。

Verilog 文件头注释应该包含什么?

大多数团队会在每个文件顶部放一个简短的文件头:模块名、一句话说明用途、端口概要、作者和日期,以及修订历史。具体格式各不相同,重要的是任何人打开文件时,不读正文就能知道它是做什么的。大型设计还会在每个端口声明旁边为每个信号写注释。

Coddy 编程语言插图

和 Coddy 一起学编程

开始