Skip to content

Verilog · Chapter 4.7 · Lexical Conventions

Keyword Usage in Verilog

A Verilog keyword is any identifier the lexer treats as a fixed token instead of a user-chosen name. The list is closed, meaning only a new language revision can add to it, and it grows from roughly 120 reserved words in pure Verilog to about 250 in SystemVerilog. This lesson gives you the complete catalogue grouped by purpose, walks through the additions made across the 1995, 2001, and 2005 revisions, and covers the larger SystemVerilog expansion. It also explains the compiler directives that let you lock a file to a specific keyword set, and the cross-standard porting traps that catch real production code when SystemVerilog reserves a word that older pure-Verilog code had used as an ordinary identifier.

Foundation14 min readVerilogKeywordsReserved WordsSyntax

Chapter 4 · Page 4.7 · Lexical Conventions

What Counts as a Keyword

IEEE 1364 §3.8 defines a keyword as a reserved identifier the lexer recognises as a fixed token. Once a word is on the keyword list, the simple-identifier form cannot use it (the escaped form technically can, via \time , but doing so is universally a code-smell — see Identifier Declaration). Every keyword belongs to exactly one of these categories — declaration, control-flow, module-structure, primitive, or directive.

The keyword list is closed — the language standard fixes it, and tool vendors are not free to extend. Adding "new" keywords requires a new IEEE revision (which is how signed, automatic, and localparam joined the language in 2001).

The Two-Tier Keyword System

There are two separate, layered keyword sets:

  1. IEEE 1364 (pure Verilog) — ~120 keywords. Used by .v source files.
  2. IEEE 1800 (SystemVerilog) — ~250 keywords. Used by .sv source files. Supersets 1364 plus adds class/interface/program / 2-state-type / assertions / coverage / TLM keywords.

A pure-Verilog file (.v) sees the 1364 set; a SystemVerilog file (.sv) sees the larger 1800 set. The set is determined by file extension or the `begin_keywords compiler directive (covered below).

Verilog (IEEE 1364) Keywords — Grouped

The complete pure-Verilog keyword list, grouped by purpose. Approximate counts per group in parentheses.

Module Structure (8)

moduleendmodulemacromoduleprimitive
endprimitivefunctionendfunctiontask
endtask

(generate / endgenerate / genvar join in IEEE 1364-2001 — see the version walk.)

Port Direction & Net Types (12)

inputoutputinoutwire
regtritri0tri1
triandtriorwandwor
supply0supply1

Variable Types (5)

integerrealrealtimetime
event

Procedural Blocks (4)

initialalwaysbeginend

Control Flow (12)

ifelsecasecasex
casezendcasedefaultfor
whileforeverrepeatdisable

Continuous Assignment & Force (4)

assigndeassignforcerelease

Event Control (4)

posedgenegedgewaitor

(or is overloaded — also a gate primitive. Context disambiguates.)

Gate Primitives (~22)

andornandnor
xorxnornotbuf
bufif0bufif1notif0notif1
cmosrcmosnmospmos
rnmosrpmostranrtran
tranif0tranif1rtranif0rtranif1
pulluppulldown

Compile-Time Constants (4)

parameterlocalparamspecparamdefparam

Modifiers (3)

signedunsignedautomatic

Specify Blocks & Timing (10)

specifyendspecifyifnonepulsestyle_ondetect
pulsestyle_oneventshowcancellednoshowcancellededge
scalaredvectored

UDP (User-Defined Primitives, 4)

tableendtableprimitiveendprimitive

Misc (5)

generateendgenerategenvarlibrary

These five all came in IEEE 1364-2001 except library / include which are configuration-related and rarely seen in RTL.

The IEEE 1364 Version Walk

Verilog has three IEEE revisions. Knowing which keyword landed when matters for porting old code:

RevisionYearNotable additions
IEEE 1364-19951995Baseline — the ~90 keywords above
IEEE 1364-20012001generate, endgenerate, genvar, localparam, signed, unsigned, automatic, library, config, include, ifnone, showcancelled, noshowcancelled, pulsestyle_*
IEEE 1364-20052005Minor cleanup; one new keyword: uwire. Mostly bug-fix revision.

Code labelled "Verilog 1995" predates generate and signed. Code labelled "Verilog 2001" is the modern baseline and what almost every tool defaults to.

The SystemVerilog (IEEE 1800) Expansion

SystemVerilog adds ~130 keywords on top of the 1364 set. The expansion falls into clear themes:

ThemeExample keywords
2-state data typesbit, byte, shortint, int, longint, logic
OOPclass, endclass, extends, super, this, new, virtual, pure, static, local, protected, final
Interfaces & programsinterface, endinterface, program, endprogram, modport, clocking, endclocking
Assertionsassert, assume, cover, property, endproperty, sequence, endsequence, expect
Coveragecovergroup, endgroup, coverpoint, cross, bins, illegal_bins, ignore_bins
TLM & messagingmailbox, semaphore, chandle, string, enum, struct, union, typedef, packed
Misc OOP / DPIimport, export, package, endpackage, extern, context, pure, void, do
Proceduraldo (while-do loop), break, continue, return
Constrained randomrand, randc, constraint, dist, inside, with, solve, before

Crucial porting fact: every one of these was a legal user identifier in IEEE 1364 1995/2001/2005. Move the file to .sv and the lexer suddenly rejects names like bit, class, do, string, event (wait — event was already a 1364 keyword), int, logic, interface. This is the #1 porting bug going from .v to .sv.

`begin_keywords / `end_keywords

To handle code that needs to live across the 1364/1800 boundary, IEEE provides a pair of compiler directives:

version-lock.v — pinning the keyword set per file
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`begin_keywords "1364-2005"
 
  // Inside this block the lexer uses the 1364-2005 keyword set
  // even if the file is a .sv file. `bit`, `class`, `string`,
  // `interface`, etc. are valid user identifiers here.
 
  module legacy_block (
      input  wire        clk,
      input  wire [7:0]  bit,     // ✅ legal — `bit` is not a 1364 keyword
      output reg  [7:0]  out
  );
      always @(posedge clk) out <= bit;
  endmodule
 
`end_keywords

Allowed argument values:

  • "1364-1995" — the original Verilog set
  • "1364-2001" — adds generate, localparam, signed, unsigned, etc.
  • "1364-2001-noconfig" — 2001 minus the config-related keywords
  • "1364-2005" — adds uwire
  • "1800-2005" / "1800-2009" / "1800-2012" / "1800-2017" — the SystemVerilog revisions

`begin_keywords directives nest. Each `end_keywords pops the most recent push. Outside any explicit directive, the keyword set is determined by file extension (.v → 1364, .sv → 1800) or by the tool's command-line default.

Practical use: pinning legacy IP that uses (now-reserved) words like bit or do as identifiers, while the surrounding project compiles under SystemVerilog.

Cross-Standard Porting Traps

Five SystemVerilog keywords that were legal Verilog identifiers in widespread legacy code:

WordVerilog (1364) statusSystemVerilog (1800) statusWhere it bites
bitIdentifier — common name for "one bit of something"2-state single-bit type keywordAlmost every legacy RTL uses bit as a variable name somewhere
classIdentifierOOP keywordClass-name parameters in old testbenches
stringIdentifierFirst-class string typeLegacy reg-vector string holders named string
intIdentifier2-state 32-bit signed typeLoop counters named int
doIdentifier — sometimes used as "do-this" flagdo { } while loop keywordLegacy state-machine do signals
logicIdentifier — common short name4-state single-bit typeAnywhere logic was a user-chosen name
interfaceIdentifier (rare but legal)Interface block keywordBus-bridge IP that named ports/regs interface
packageIdentifier (rare)Package block keywordCompilation-organisation rename
event1364 keyword already — not a porting trapStill a keyword (same purpose)Not a porting issue
time1364 keyword1800 keywordBoth standards reserve it — not a porting issue

The mitigation is universal: rename. bitbit_sel; stringname_str; dodo_step; intidx. The escaped-identifier workaround (\bit ) works lexically but is a code-smell that every reviewer will flag.

Reserved-but-Rare: System Tasks vs Keywords

System tasks and functions ($display, $time, $random, $finish, $assertoff) are not keywords. They live in the $-prefix namespace — see Identifier Declaration §The $ Prefix and System Tasks. The lexer enforces the namespace separately:

  • Keywords: reserved at the lexer level, blocked even in escaped form (by convention).
  • System tasks: reserved by the $ prefix only, can never appear as a user identifier because users cannot start an identifier with $.
  • Compiler directives (`define, `include, `ifdef, `begin_keywords): reserved by the backtick prefix. Same prefix-based reservation pattern.

The three namespaces (keyword, $-prefixed, backtick-prefixed) are disjoint at the lexer level.

Common Mistakes

Five production keyword-related faults.

  1. Naming a reg time or signed. Reg declaration reg time parses as the time variable type, not as a 1-bit reg named "time". Tools usually flag this with a confusing error; the fix is rename.
  2. Porting .v to .sv and breaking on bit. Code that used bit as a 1-bit register name compiles fine as Verilog and breaks immediately as SystemVerilog. Either rename to bit_q / bit_d, or wrap the legacy module in `begin_keywords "1364-2005".
  3. Forgetting `end_keywords. A `begin_keywords without a matching `end_keywords keeps the keyword set in force for every subsequent file the compiler processes — including ones written under the assumption of a different set. Always pair the directives.
  4. Using and / or as variable names. Both are gate-primitive keywords. The escaped-identifier form (\and ) works lexically but creates code reviewers will reject.
  5. Mixing == and === semantics. Not strictly a keyword issue, but the === 4-state-equality operator is sometimes confused with a "keyword". It's an operator (see Operator Usage), not a reserved word.

Worked Example — A Module That Stays Portable Across .v and .sv

A small bus-interface register that avoids every common keyword collision so the same source compiles cleanly under both IEEE 1364 (.v) and IEEE 1800 (.sv).

rtl/portable_reg.v — keyword-collision-free across 1364 and 1800
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//-----------------------------------------------------------
// portable_reg — compiles identically as .v (IEEE 1364)
//                and as .sv (IEEE 1800)
//   conventions:
//     - no use of `bit`, `logic`, `int`, `string`, `class`,
//       `interface`, `package`, `do`, `program` as identifiers
//     - explicit `reg` / `wire` (1364 baseline) instead of `logic`
//     - parameters in ALL_CAPS
//-----------------------------------------------------------
module portable_reg #(
    parameter DATA_WIDTH = 8
) (
    input  wire                  clk,
    input  wire                  rst_n,
    input  wire                  load_en,     // not `do`
    input  wire [DATA_WIDTH-1:0] data_d,      // not `bit` / `logic`
    output reg  [DATA_WIDTH-1:0] data_q
);
 
    // local counter — not `int` / `integer-named-int`
    reg [3:0] cycle_count_q;
 
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_q        <= {DATA_WIDTH{1'b0}};
            cycle_count_q <= 4'b0;
        end else if (load_en) begin
            data_q        <= data_d;
            cycle_count_q <= cycle_count_q + 4'b1;
        end
    end
 
endmodule

What's right about this:

  • Every signal name is a multi-character non-keyword: data_d, data_q, cycle_count_q, load_en. None collide with IEEE 1364 or IEEE 1800 keywords.
  • Types are explicit (reg, wire) rather than the SystemVerilog-introduced logic. The module reads identically under both lexers.
  • The arithmetic uses 4'b1 (a sized literal) — see Number Representation — rather than relying on the unsized default that the standard treats slightly differently across revisions.

This is the discipline that lets IP libraries ship as .v files and link cleanly into either Verilog-only or SystemVerilog-only verification flows.

Interview Insights

Summary

Verilog keywords are the closed list of reserved identifiers the lexer treats as fixed tokens. IEEE 1364 (.v) reserves ~120 words; IEEE 1800 (.sv) reserves the same ~120 plus ~130 SystemVerilog additions (classes, interfaces, 2-state types, assertions, coverage). The keyword list is closed — only an IEEE revision can extend it, which is why generate and signed are 2001 additions and uwire is the lone 2005 addition. The `begin_keywords / `end_keywords directives let a file lock the keyword set per scope, primarily to handle legacy IP whose identifiers collide with SystemVerilog reservations like bit, class, do, int, logic, string, interface, and package. System tasks ($display, $time) and compiler directives (`define, `include) live in their own prefix-reserved namespaces — disjoint from the keyword list. The discipline is: pick non-collision names, prefer multi-character snake_case, and reach for `begin_keywords only when porting legacy IP. This closes the Lexical Conventions chapter — Chapter 5 picks up with data-types (the first real semantic layer above the lexer).

Beat coverage notes: all required beats present. ⏱ Timing Observation, 🧠 Simulator Scheduling Insight omitted — keywords are pure lexical rules; no timing or scheduling angle. The 🏗 Synthesis Concern documents the `begin_keywords mid-module behaviour.

  • Lexical Conventions — Chapter 4 overview; the parent layer.
  • Identifier Declaration — Chapter 4.6; the companion topic on how user names interact with the keyword namespace.
  • Operator Usage — Chapter 4.3; the operator catalogue distinct from the gate-primitive keywords (and / or / nand / …).
  • Number Representation — Chapter 4.4; literal-syntax depends on signed / unsigned keywords.
  • RTL Designing — Chapter 3; where the keyword-discipline patterns earn their keep across a full module.