Skip to content

Verilog · Chapter 4.2 · Lexical Conventions

Comment Implementation in Verilog

Verilog supports two comment forms, a line comment and a block comment, and the syntax matches C exactly. The rules themselves are simple, but the discipline around them is what separates clean RTL from messy code. This lesson covers the precise lexical rules for each form, the block-comment nesting trap that bites almost every beginner at least once, and the comment-hygiene habits that working teams enforce so code stays readable and searchable. It also explains pragmas, the specially formatted comments that lint, synthesis, and coverage tools read as instructions, such as lint waivers, synthesis directives, and coverage controls. By the end you will know not just how to write comments, but how professional RTL teams use them to communicate intent and guide their tools.

Foundation14 min readVerilogSyntaxStyleComments

Chapter 4 · Page 4.2 · Lexical Conventions

The Two Comment Forms

Verilog inherits C's comment syntax exactly. Two forms; nothing more.

comments-basics.v — the two forms
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Line comment — runs from `//` to the end of the line.
assign y = a & b;   // can also be at the end of a code line.
 
/* Block comment — runs from /* to the next */
   and can span multiple lines. */
 
/*
 * Conventional multi-line block: padded leading-`*` on every line,
 * used at the top of every file for copyright, author, change log.
 */

Per IEEE 1364-2005 §3.3, both forms are stripped by the simulator's tokeniser before the parser sees the source. The tools never see comments. Use them generously.

Lexical Rules

The exact rules every comment obeys.

Line comments

  • Begin with //.
  • End at the next newline (\n, \r, or \r\n).
  • The newline itself is not part of the comment — it's preserved as a whitespace token.
  • A line comment can appear anywhere a token can appear.
line-comments-anywhere.v — placements that all work
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Top of file
module my_block ( // after the module name
    input  wire clk,    // after a port declaration
    input  wire rst_n,  // ...
    output reg  data_q
); // after the port list
 
    // Standalone block — separates sections of code
 
    always @(posedge clk) begin
        data_q <= 1'b0;  // after a statement
    end // after `end`
 
endmodule  // after `endmodule`

Block comments

  • Begin with /*.
  • End at the first */ (greedy from left).
  • Everything in between — including newlines — is part of the comment.
  • A block comment can appear anywhere whitespace can appear.
block-comments-mid-expression.v — block comments inline
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Block comments can split an expression — used for inline documentation
assign sum = a /* operand A */ + b /* operand B */ ;
 
// Or wrap a parameter
my_block #(
    .WIDTH (/* parameterised */ 8),
    .DEPTH (/* memory depth */ 1024)
) u_dut ( ... );

In practice, inline block comments in expressions are rare — they hurt readability. Use them at the file and block level, not inside a single statement.

The Nesting Trap

This is the one beginner trap every Verilog engineer hits once. Block comments do not nest.

nesting-trap.v — the canonical bug
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ❌ BUG — looks like a nested comment, isn't
/* outer comment
   /* inner comment */     // this `*/` ends the OUTER block comment
   still part of the outer */  // this text is now CODE (and is a syntax error)
 
// Compiler error: "syntax error near 'still'"
// Reason: the first `*/` closed the only block comment that was open.

The simulator's tokeniser scans left-to-right. The opening /* opens one comment. The first */ it encounters closes that comment. The text after the intended closing */ is then parsed as code, producing a confusing error message that doesn't point at the actual problem.

Two safe alternatives

nesting-fix.v — patterns that work
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ✅ Pattern 1 — use line comments for the inner annotation
/* outer comment block
   // inner comment that doesn't close the outer
   still part of the outer */
 
// ✅ Pattern 2 — temporarily comment-out a block of code with `ifdef
`ifdef NEVER_DEFINED
    // ... any code here, including `/* */` blocks ...
    /* this block stays inside the ifdef */
`endif

Pattern 2 is what every working RTL engineer reaches for when "commenting out" a region of code that itself contains block comments. The `ifdef NEVER_DEFINED is conventional — readers recognise it as a "disabled block" marker.

Comment Hygiene — The Working Discipline

The lexical rules are the easy part. The discipline of what to write in a comment is what separates good RTL from textbook RTL.

Comments explain why, not what

The code already says what it does. A comment that restates the code is noise.

hygiene-1.v — bad vs good comments
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ❌ Bad — restates the code
assign y = a & b;          // assigns a AND b to y
 
// ❌ Bad — adds nothing
count_q <= count_q + 1;    // increment count_q
 
// ✅ Good — explains the engineering decision
// Increment only on the leading edge of `valid` to avoid double-counting
// retried transactions that re-assert mid-burst.
count_q <= count_q + 1;

Comments document intent, not history

The Git log carries history. Comments carry intent.

hygiene-2.v — keep intent, drop history
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ❌ Bad — historical commentary in source
assign mask = 8'hAA;       // changed from 0xFF on 2024-03-15 per Alice's review
 
// ✅ Good — the intent, not the timeline
assign mask = 8'hAA;       // even-bit positions only — odd positions are reserved
                            // for vendor extensions (see spec §3.4.2).

Comments cite specs

When the RTL implements a specific clause of a public spec, cite it.

hygiene-3.v — cite the spec
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ✅ Good — anchored to the spec
// AXI4 §A3.3.1 — WLAST asserts on the last beat of a write burst.
// The counter loads AWLEN+1, decrements on each accepted beat, and
// asserts WLAST when it reaches 1.
assign w_last = (beats_remaining_q == 5'd1);

The cited spec section makes the comment auditable — a reviewer can verify the implementation against the named clause.

File-level header comments

Every working RTL file starts with a header. The format every team converges on:

hygiene-4.v — file header convention
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
/* ───────────────────────────────────────────────────────────────────
 * Module       : counter_4b
 * Description  : 4-bit synchronous up-counter with async-low reset.
 * Author       : VLSI Mentor (rtl@vlsimentor.com)
 * Created      : 2026-04-15
 *
 * Interfaces   : clk / rst_n / en input · q[3:0] output
 * Clock domain : single (clk)
 * Reset        : async-low (rst_n)
 *
 * Spec ref     : project-spec §2.3.1
 * Lint waivers : (none)
 * ─────────────────────────────────────────────────────────────────── */
 
`timescale 1ns/1ps
`default_nettype none
 
module counter_4b ( ... );
    ...
endmodule

Some teams add a copyright notice. Some add a change log (despite the "don't put history in comments" rule — the change log is the exception every team accepts). Some add a tool-version stamp. The exact fields vary; the existence of a structured header doesn't.

Tool Pragmas Hidden Inside Comments

This is the part of comments most beginners don't realise exists. Several EDA tools encode pragmas — directives the tool reads — inside specially-formatted comments. The Verilog parser sees them as comments; the tool sees them as instructions.

Lint waivers

A lint waiver tells the lint tool "I know this rule fires here; suppress it." Most lint tools (SpyGlass, Conformal, Meridian) use a comment pragma.

pragmas-1.v — SpyGlass waiver syntax
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// spyglass disable_block W116
// W116 fires on combinational `for` loops, but we know the unroll is safe here.
for (i = 0; i < 8; i = i + 1) begin
    masked[i] = data[i] & enable_mask[i];
end
// spyglass enable_block W116

Without the waiver, the lint tool would flag every run. With the waiver, it stays silent for just that block. Waivers must include a justification comment — un-justified waivers are a code-review block at every modern team.

Synthesis pragmas

Synthesis tools accept pragmas that fine-tune the synthesised hardware.

pragmas-2.v — Synopsys DC synthesis pragmas
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// synopsys translate_off
// This block runs in simulation but is invisible to synthesis.
initial $display("debug build");
// synopsys translate_on
 
reg [3:0] data_q;
// synopsys keep_signal_name "data_q"
// Tells DC not to rename `data_q` during optimisation — useful when
// the signal name shows up in waveforms and post-silicon debug.
 
// synopsys async_set_reset rst_n
// Marks rst_n as the async reset, even when sensitivity-list rules
// would otherwise produce a different inference.

Other tools use different syntax (// genus, // dc, etc.). The pragma is not portable between tools — every tool reads only its own pragmas and ignores comments it doesn't recognise.

Coverage directives

Coverage tools use pragmas to exclude regions from coverage analysis.

pragmas-3.v — coverage directives
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// VCS coverage off
// This assertion is only used in debug builds — don't count it in coverage.
assert property (@(posedge clk) !overflow_q);
// VCS coverage on

Why pragmas are inside comments

Two reasons:

  1. Lexical compatibility. Pragmas inside comments mean the file remains parseable by any simulator or synthesiser, even ones that don't recognise the pragma. The unrecognising tool just ignores the comment.
  2. Cross-tool portability. A file with // spyglass disable_block W116 lints clean in SpyGlass and is invisible to every other tool. No #ifdef gymnastics needed.

Comments in Generated Files

When tools generate Verilog (synthesis output, IP-generator output, behavioural-model exports), the generated file usually has a header comment identifying the source.

generated.v — typical machine-generated header
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//-----------------------------------------------------------------------------
// Generated by: Synopsys Design Compiler X-2024.06
// Generation time: 2026-04-15 14:32:51 PDT
// Source: rtl/counter_4b.v
// SDC constraints: constraints/counter_4b.sdc
// Library: tsmc5_svt.db
//
// DO NOT EDIT THIS FILE BY HAND — re-run synthesis instead.
//-----------------------------------------------------------------------------
 
module counter_4b ( ... );
    DFFRX1 \count_q_reg[0] ( .D(...), .CK(clk), .RN(rst_n), .Q(...) );
    ...
endmodule

The "do not edit by hand" warning is non-negotiable — the file gets regenerated on every synthesis run, so any hand edit gets blown away. Treat generated files as build artifacts, not as source.

Common Mistakes

Six failure modes every Verilog engineer encounters.

  1. Unclosed block comments. /* ... without a */ swallows the rest of the file. Every editor highlights this; pay attention to the highlighting.
  2. Nested block comments. Covered above — the inner */ closes the outer.
  3. Restating-the-code comments. Noise in the source. Removed on every review.
  4. Comment drift. The code changes, the comment doesn't. The comment now lies. Lies are worse than no comment at all — they actively mislead. Every code review should ask "does the comment still match the code?"
  5. Un-justified lint waivers. A // spyglass disable_block W116 with no explanation is a maintenance hazard. The reviewer (or the future you) has to re-derive why the waiver is safe. Always include a justification.
  6. History in comments. "Changed by Alice on 2024-03-15" belongs in git log, not in the source. Comments are read by every reader; history is read by the engineer specifically auditing changes.

Interview Insights

Summary

Verilog has exactly two comment forms — line (//) and block (/* */) — both inherited unchanged from C. The lexical rules are simple; the discipline is the harder part: comments explain why not what, document intent not history, cite specs when implementing them, and follow the team's file-header convention. The non-obvious layer is that EDA tools encode pragmas inside specially-formatted comments — lint waivers, synthesis pragmas, coverage directives — every working RTL engineer learns to read alongside the language itself. The next sub-topic covers identifiers — naming rules, the case-sensitivity gotcha, and the rare-but-real escaped-identifier form.

Beat coverage notes: all required beats present. ⏱ Timing Observation, 🧠 Simulator Scheduling Insight callouts omitted — comments are pure-text-rules; no timing or scheduling angle. The 🚀 RTL Design Insight callout is folded into the comment-hygiene section's "intent vs history" framing.