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:
- IEEE 1364 (pure Verilog) — ~120 keywords. Used by
.vsource files. - IEEE 1800 (SystemVerilog) — ~250 keywords. Used by
.svsource 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)
module | endmodule | macromodule | primitive |
endprimitive | function | endfunction | task |
endtask |
(generate / endgenerate / genvar join in IEEE 1364-2001 — see the version walk.)
Port Direction & Net Types (12)
input | output | inout | wire |
reg | tri | tri0 | tri1 |
triand | trior | wand | wor |
supply0 | supply1 |
Variable Types (5)
integer | real | realtime | time |
event |
Procedural Blocks (4)
initial | always | begin | end |
Control Flow (12)
if | else | case | casex |
casez | endcase | default | for |
while | forever | repeat | disable |
Continuous Assignment & Force (4)
assign | deassign | force | release |
Event Control (4)
posedge | negedge | wait | or |
(or is overloaded — also a gate primitive. Context disambiguates.)
Gate Primitives (~22)
and | or | nand | nor |
xor | xnor | not | buf |
bufif0 | bufif1 | notif0 | notif1 |
cmos | rcmos | nmos | pmos |
rnmos | rpmos | tran | rtran |
tranif0 | tranif1 | rtranif0 | rtranif1 |
pullup | pulldown |
Compile-Time Constants (4)
parameter | localparam | specparam | defparam |
Modifiers (3)
signed | unsigned | automatic |
Specify Blocks & Timing (10)
specify | endspecify | ifnone | pulsestyle_ondetect |
pulsestyle_onevent | showcancelled | noshowcancelled | edge |
scalared | vectored |
UDP (User-Defined Primitives, 4)
table | endtable | primitive | endprimitive |
Misc (5)
generate | endgenerate | genvar | library |
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:
| Revision | Year | Notable additions |
|---|---|---|
| IEEE 1364-1995 | 1995 | Baseline — the ~90 keywords above |
| IEEE 1364-2001 | 2001 | generate, endgenerate, genvar, localparam, signed, unsigned, automatic, library, config, include, ifnone, showcancelled, noshowcancelled, pulsestyle_* |
| IEEE 1364-2005 | 2005 | Minor 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:
| Theme | Example keywords |
|---|---|
| 2-state data types | bit, byte, shortint, int, longint, logic |
| OOP | class, endclass, extends, super, this, new, virtual, pure, static, local, protected, final |
| Interfaces & programs | interface, endinterface, program, endprogram, modport, clocking, endclocking |
| Assertions | assert, assume, cover, property, endproperty, sequence, endsequence, expect |
| Coverage | covergroup, endgroup, coverpoint, cross, bins, illegal_bins, ignore_bins |
| TLM & messaging | mailbox, semaphore, chandle, string, enum, struct, union, typedef, packed |
| Misc OOP / DPI | import, export, package, endpackage, extern, context, pure, void, do |
| Procedural | do (while-do loop), break, continue, return |
| Constrained random | rand, 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:
`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_keywordsAllowed argument values:
"1364-1995"— the original Verilog set"1364-2001"— addsgenerate,localparam,signed,unsigned, etc."1364-2001-noconfig"— 2001 minus the config-related keywords"1364-2005"— addsuwire"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:
| Word | Verilog (1364) status | SystemVerilog (1800) status | Where it bites |
|---|---|---|---|
bit | Identifier — common name for "one bit of something" | 2-state single-bit type keyword | Almost every legacy RTL uses bit as a variable name somewhere |
class | Identifier | OOP keyword | Class-name parameters in old testbenches |
string | Identifier | First-class string type | Legacy reg-vector string holders named string |
int | Identifier | 2-state 32-bit signed type | Loop counters named int |
do | Identifier — sometimes used as "do-this" flag | do { } while loop keyword | Legacy state-machine do signals |
logic | Identifier — common short name | 4-state single-bit type | Anywhere logic was a user-chosen name |
interface | Identifier (rare but legal) | Interface block keyword | Bus-bridge IP that named ports/regs interface |
package | Identifier (rare) | Package block keyword | Compilation-organisation rename |
event | 1364 keyword already — not a porting trap | Still a keyword (same purpose) | Not a porting issue |
time | 1364 keyword | 1800 keyword | Both standards reserve it — not a porting issue |
The mitigation is universal: rename. bit → bit_sel; string → name_str; do → do_step; int → idx. 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.
- Naming a reg
timeorsigned. Reg declarationreg timeparses 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. - Porting
.vto.svand breaking onbit. Code that usedbitas a 1-bit register name compiles fine as Verilog and breaks immediately as SystemVerilog. Either rename tobit_q/bit_d, or wrap the legacy module in`begin_keywords "1364-2005". - Forgetting
`end_keywords. A`begin_keywordswithout a matching`end_keywordskeeps 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. - Using
and/oras variable names. Both are gate-primitive keywords. The escaped-identifier form (\and) works lexically but creates code reviewers will reject. - 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).
//-----------------------------------------------------------
// 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
endmoduleWhat'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-introducedlogic. 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.
Related Tutorials
- 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/unsignedkeywords. - RTL Designing — Chapter 3; where the keyword-discipline patterns earn their keep across a full module.