两种写法
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
*/
结果就是在意想不到的地方报语法错误,而那一行看起来完全没问题。
需要禁用一段代码时,优先选择以下方式之一:
-
在每一行前加
//。 大多数编辑器按一个快捷键就能做到。 -
使用预处理器保护:
`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 文件头注释应该包含什么?
大多数团队会在每个文件顶部放一个简短的文件头:模块名、一句话说明用途、端口概要、作者和日期,以及修订历史。具体格式各不相同,重要的是任何人打开文件时,不读正文就能知道它是做什么的。大型设计还会在每个端口声明旁边为每个信号写注释。