Skip to content

Verilog · Chapter 4 · Foundations

Lexical Conventions in Verilog

Before a simulator or synthesis tool can make sense of your design, a lexer breaks the source text into small pieces called tokens. Every Verilog file is built from a handful of token kinds, including whitespace, comments, identifiers, keywords, numbers, strings, and operators. This chapter is a working engineer's map of those categories. It shows where each one appears in real RTL, what the language actually requires, what the tools quietly forgive, and what they refuse to accept. You will learn the small set of lexical habits that separate clean, production-grade Verilog from code that happens to simulate but causes trouble later. Each category links to a dedicated sub-page that drills into it deeper.

Foundation20 min readVerilogLexicalSyntaxIdentifiersNumbersOperators

Chapter 4 · Page 1.4 · Foundations

1. The Engineering Problem

You open a Verilog file from a colleague and the first ten lines are this:

snippet.v — production RTL, opening lines
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns/1ps
`default_nettype none
 
// AXI4-Lite slave — controls the PHY reset state machine.
// Owner: alice@example.com  ·  Spec: docs/phy_ctrl/spec.md
 
module phy_ctrl #(
    parameter int CFG_W      = 32,
    parameter [CFG_W-1:0] RESET_VALUE = 32'hDEAD_BEEF
) (
    input  wire              s_axi_aclk,

Before any RTL meaning exists, the simulator and the synthesiser have to parse that text. They do it the same way every compiler does: a lexer scans the source one character at a time, recognises whitespace, comments, identifiers, keywords, numbers, strings, operators, and punctuation, and produces a sequence of tokens that the parser then assembles into modules, ports, and statements.

The lexer is the first thing your code talks to. If your code violates the lexer's rules — a number written as 32hDEADBEEF instead of 32'hDEADBEEF, an identifier starting with a digit, a `define placed inside a module block, an unescaped special character in a string — the simulator and synthesiser stop at the first failed lex, before any of your design intent gets considered.

This chapter is the working engineer's map of the lexer's vocabulary: every token category, what the spec requires, what the tools accept, what they don't, and the small set of lexical habits that distinguish production-grade Verilog from code that almost works.

2. Why Lexical Conventions Matter

Three reasons every working RTL engineer carries the lexical layer in their head:

  1. The lexer is the first gate. Every downstream tool — simulator, synthesiser, lint, formal — runs the same lexer on the same source. A lexical error stops every tool at the same point. A clean lexical layer is the cheapest verification you do.
  2. The lexer hides bugs in plain sight. 32'hDEAD_BEEF is the same as 32'hDEADBEEF (underscores are spacers). 32'd16 is not the same as 32d16 (the apostrophe is mandatory). Every typo that the lexer accepts becomes a downstream functional bug that lives in the netlist. Knowing what the lexer will accept is how you avoid encoding bugs as legal numbers.
  3. The lexer reveals project conventions. A glance at a colleague's file tells you whether they're using `default_nettype none, whether they're using parametric widths, whether their comments are doxygen-style, whether their identifiers follow snake_case or camelCase. The lexical layer is the visible part of the project's coding standard.

A lint failure on lexical issues is the cheapest CI failure your project will ever have. Every tool flags lexical issues at parse time, before simulation even starts, before synthesis loads a library. Get the lexical layer right and every later stage runs faster.

3. Mental Model

The working test for any lexical question: what would the lexer's output token stream be for this input? If you can answer that, you know whether the input is legal and what the tools will see.

4. Visual Explanation — Lex → Parse → Elaborate → Generate

The Verilog source-to-netlist pipeline has the lexer at its front:

The Verilog compilation pipeline — where the lexer fits

data flow
The Verilog compilation pipeline — where the lexer fitsSource (.v / .sv)your Verilog textLexertokenise — keywords, numbers, IDs…Parserbuild AST from tokensElaboratorresolve parameters + hierarchySim / Synthproduce waveforms or gates
The lexer is the first transform in every Verilog tool — simulator, synthesiser, lint, formal verifier — they all run the same lexical analysis before any RTL semantics enter the picture. A lexical error blocks every downstream stage uniformly.

The seven token categories the IEEE 1364 lexer recognises:

Token categoryWhat it isSub-page
WhitespaceSpaces, tabs, newlines, carriage returns — separators with no semantic value4.1 White Space Requirements
Comments// single-line and /* multi-line */ — ignored by the parser4.2 Comment Implementation
Operators+, -, *, &&, <<, ===, … — the arithmetic / logical / relational set4.3 Operator Usage
Numbers / Literals8'hA5, 32'b1010_0011, 1'b0, 100, 1.5e-9 — sized and unsized literals4.4 Number Representation
Strings"hello, world" — character sequences inside double quotes4.5 String Handling
IdentifiersNames you choose — clk, data_q, payload[7:0] — letters / digits / _ / $4.6 Identifier Declaration
KeywordsReserved words — module, always, assign, wire, reg, …4.7 Keyword Usage

Plus two cross-cutting families:

  • System tasks and functions — names that begin with $ ($display, $monitor, $random, $readmemh). Always testbench / tool-controlled.
  • Compiler directives — names that begin with a backtick (`define, `include, `timescale, `default_nettype). Processed by a preprocessor pass that runs before the lexer-proper.

The rest of this chapter walks each category, links to the deep-dive sub-page, and notes the working-engineer detail every reviewer expects you to know.

5. Whitespace, Comments, and Newlines

Verilog is whitespace-insensitive. Spaces, tabs, and newlines separate tokens but carry no other semantic meaning — assign y=a&b; is identical to assign y = a & b; to the lexer. The convention that working RTL houses follow is to use whitespace generously for readability:

whitespace-style.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Cramped — legal but unreadable
assign y=a&b;
always@(posedge clk)q<=d;
 
// Industry style — generous, scannable
assign y = a & b;
 
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) q <= 1'b0;
    else        q <= d;
end

Comments come in two flavours:

comment-styles.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Single-line comment — runs to end of line
assign y = a & b;   // can also appear at end of line
 
/*
 * Multi-line comment.
 * Cannot nest — /* this would not work */
 */

The lexer strips all comments before passing tokens to the parser; the parser never sees them. That makes comments free at runtime — write them generously.

Deep dive: 4.1 White Space Requirements and 4.2 Comment Implementation.

6. Identifiers and Keywords

6.1 Identifiers

Names you choose for signals, modules, parameters, instances. The IEEE 1364 rules:

  • Must start with a letter (a–z, A–Z) or underscore (_).
  • May contain letters, digits, underscores, and $ (the $ is reserved for system tasks, so avoid it in user identifiers).
  • Case-sensitiveData and data are different identifiers.
  • No length limit in the spec; tools typically enforce a project-wide convention (≤ 60 characters is common).
  • Cannot match a reserved keyword (next subsection).
identifier-examples.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire        clk;           // legal — letter start
wire        rst_n;          // legal — underscore inside
wire        data_q;         // legal
wire        _internal_w;    // legal — underscore start
wire        bus32;          // legal — digit inside, not at start
// wire     2bus;           // ILLEGAL — starts with a digit
// wire     wire;           // ILLEGAL — `wire` is a reserved keyword

Verilog also supports escaped identifiers — names that start with \ and end with whitespace, allowing arbitrary characters in between:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire \bus[31:0] ;       // identifier is exactly "bus[31:0]"
wire \1bus ;            // legal escaped identifier — backslash escape

Escaped identifiers exist mainly for tool-generated names (in gate-level netlists, where the original RTL's bus[31:0] slice gets flattened into a single net). Avoid them in handwritten RTL.

6.2 Keywords

The IEEE 1364 reserved-keyword list is fixed — about 115 words you cannot use as identifiers. The canonical groups:

  • Module structuremodule, endmodule, function, endfunction, task, endtask, generate, endgenerate, primitive, endprimitive.
  • Net and variable typeswire, tri, tri0, tri1, trireg, wand, wor, reg, integer, real, time, realtime, supply0, supply1.
  • Portsinput, output, inout.
  • Behaviouralalways, initial, begin, end, if, else, for, while, case, casex, casez, endcase, default, repeat, forever, disable.
  • Continuous and proceduralassign, deassign, force, release.
  • Logicaland, or, nand, nor, not, xor, xnor, buf.
  • Strength and parametersstrong0, strong1, weak0, weak1, pull0, pull1, highz0, highz1, parameter, localparam, defparam, specparam.

A keyword used as an identifier produces an immediate compile error. The working habit: prefix every internal signal with a noun (data_, count_, state_) rather than reaching for short generic words that might collide.

Deep dives: 4.6 Identifier Declaration and 4.7 Keyword Usage.

7. Numbers and Literals

Verilog numbers are the lexical layer with the most teeth — getting them wrong is the most common first-week bug. The format:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[size]'[base][value]

Where:

  • size — width in bits (e.g. 8, 32). Optional — if omitted, the number defaults to a 32-bit unsigned integer (the 32-bit default trap, covered in 4.4).
  • base'b / 'B (binary), 'o / 'O (octal), 'd / 'D (decimal), 'h / 'H (hexadecimal). The apostrophe is mandatory.
  • value — digits in the chosen base; underscores between digits are spacers (32'hDEAD_BEEF).
number-examples.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
8'hA5            // 8-bit hex      → 10100101
4'b1010          // 4-bit binary   → 1010
32'd16           // 32-bit decimal → 0...010000
1'b0             // 1-bit zero
1'b1             // 1-bit one
32'h0            // 32-bit zero — use this for parametric reset values
8'hxx            // 8-bit unknown — all bits X
8'bzzzz_zzzz     // 8-bit high-impedance — tri-state release
32'hDEAD_BEEF    // underscores are spacers — same as 32'hDEADBEEF
{32{1'b0}}       // replication — 32 copies of 1'b0 = 32-bit zero
{WIDTH{1'b1}}    // parametric — N copies of 1; sizes correctly for any WIDTH

The X and Z literals are essential for testbench code and for explicit tri-state release:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
8'bxxxx_xxxx     // 8-bit unknown — every bit is X (test that consumers handle X)
1'bz             // 1-bit high-impedance — release a tri-state line

Common pitfalls:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 32-bit default trap — unsized literal is 32-bit signed
wire [7:0] addr;
assign addr = 8 + 1;          // legal — but `8 + 1` is 32-bit (sign-extended to fit)
assign addr = 8'd8 + 8'd1;    // safer — both operands explicitly 8-bit unsigned
 
// Missing apostrophe — number is treated as identifier
wire foo;
assign foo = 32d16;           // ❌ ERROR — `d` interpreted as part of identifier
assign foo = 32'd16;          // ✅ legal — explicit decimal literal
 
// Missing base specifier — number is decimal
wire [7:0] mask = 'hFF;       // ✅ legal — unsized 32-bit hex 0xFF, truncated to 8 bits
wire [7:0] mask = 'FF;        // ❌ ERROR — no base specifier

Deep dive: 4.4 Number Representation covers all twelve canonical literal forms, the 32-bit default trap, width-extension rules, and sign-vs-unsigned behaviour.

8. Strings

Strings are sequences of characters inside double quotes:

string-examples.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$display("hello, world");
$display("integer value = %0d", count_q);
$display("hex value = %h, time = %0t", data, $time);
$display("multi-line\nstring with\ttabs");

Three things to know:

  1. Strings are testbench-only. No synthesisable RTL construct accepts a string. Strings appear in $display, $monitor, $write, $readmemh format strings, and parameter declarations used by behavioural models — never in synthesisable port-level signal names.
  2. Escape sequences follow C conventions: \n (newline), \t (tab), \\ (backslash), \" (double quote), \<octal> (octal byte).
  3. Format specifiers in $display / $monitor echo the C printf family: %d (decimal), %h (hex), %b (binary), %o (octal), %c (char), %s (string), %t (time), %0d / %0h (zero-pad-suppressed).

Deep dive: 4.5 String Handling covers escape sequences, format-specifier nuances, and the $sformatf family.

9. Operators

Verilog has a rich operator set — arithmetic, logical, bitwise, relational, equality, reduction, shift, conditional, concatenation, replication. The lexer recognises each one as a distinct token; the parser assigns precedence per the IEEE 1364 grammar (similar to C with a few additions for hardware).

operator-categories.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Arithmetic
+   -   *   /   %   **
 
// Logical (1-bit boolean result)
!   &&   ||
 
// Bitwise (per-bit, vector-width result)
~   &   |   ^   ~^   ^~
 
// Relational
<   >   <=   >=
 
// Equality
==   !=        // 4-state, X/Z-propagating
===  !==       // 4-state, X/Z-comparing (case-equal / case-not-equal)
 
// Reduction (apply operator across all bits of a vector → 1-bit result)
&   |   ^   ~&   ~|   ~^
 
// Shift
<<   >>   <<<   >>>
 
// Conditional
?:
 
// Concatenation and replication
{}   {n{}}

The operators every working engineer carries reflexes for:

OperatorWhat it doesHardware shape
& / | / ^ (bitwise)Per-bit AND / OR / XOR across two vectorsAn array of AND / OR / XOR gates
& / | / ^ (reduction)AND / OR / XOR across all bits of one vectorA tree of gates
&& / ||Logical AND / OR — operands treated as boolean (zero vs non-zero)A 1-bit result, used in if / ?:
== vs ===Equality with vs without X/Z propagation== produces X if either side has X; === is exact bit-pattern compare (sim-only)
<<< / >>>Arithmetic shifts — sign-extend on right-shift signed operandDifferent gates than logical << / >>
?:Ternary — c ? a : bA 2-to-1 multiplexer
{a, b, c}Concatenation — bit-glueA wire bundle, zero-gate

The single most useful idiom every RTL author writes daily:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
{WIDTH{1'b0}}           // parametric all-zero literal — sizes correctly
{WIDTH{1'b1}}           // parametric all-one literal
{a[7:0], b[7:0]}        // concatenate two 8-bit vectors → 16-bit value
{8{a[0]}}               // sign-extend / replicate one bit eight times

Deep dive: 4.3 Operator Usage covers every operator's precedence, the 4-state X-propagation rules, signed vs unsigned operand handling, and the difference between bitwise / logical / reduction families.

10. System Tasks, Functions, and Compiler Directives

Two cross-cutting token families that don't fit the seven-category grammar but show up in every working file.

10.1 System tasks and functions

Names that start with $. Provided by the simulator (not by the user) and never synthesisable.

FamilyExamplesUse
Display$display, $write, $strobe, $monitor, $sformatfPrint to simulation log
Time$time, $realtime, $timeformatRead or format simulation time
Control$finish, $stop, $exitEnd / pause simulation
File I/O$readmemh, $readmemb, $writememh, $fopen, $fwriteMemory init, log file dump
Random$random, $urandom, $urandom_range, $dist_normalStimulus generation
Test control$assertion, $cover, $past, $rose, $fellFormal / assertion helpers (SystemVerilog)

Every $ name is a tool feature; none of them survive synthesis. The working habit: use them freely in testbenches; never put them inside a synthesisable module.

10.2 Compiler directives

Names that start with a backtick (`). Processed by the preprocessor — a pass that runs before the lexer proper. Conceptually similar to C's #define / #include.

directive-examples.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns/1ps              // simulation time-unit and precision
`default_nettype none           // disable implicit-net declaration
`include "common_defines.vh"    // textual include
`define RST_VAL 32'hDEAD_BEEF   // textual macro
`ifdef SIMULATION
    // testbench-only code
`endif
`undef RST_VAL                  // un-define

The directives every working RTL house puts at the top of every file:

DirectiveWhy
`timescale 1ns/1psSet simulation time unit and precision. Without this, simulator behaviour is undefined for #delays.
`default_nettype noneDisable the implicit-net rule. Catches typos at compile time instead of runtime.
`includeTextually include header files (*.vh, *.svh) that contain shared `defines, parameter packs, and macro definitions.

The directive every working RTL house avoids:

DirectiveWhy avoid
`define for constantsUse parameter or localparam instead — they respect scope, support types, and don't pollute the macro namespace globally.

`define has its place — for compile-time flags (`ifdef SIMULATION), for textual macros that genuinely cannot be expressed as parameters — but constant values belong in parameters, not in defines.

11. Punctuation and Special Characters

The lexer recognises a small set of punctuation tokens beyond operators:

TokenUse
;Statement terminator
,Separator (parameter / port list, concatenation)
( / )Grouping; parameter / port list delimiters
[ / ]Vector range / array index
{ / }Concatenation, replication, block delimiter (in some contexts)
:Range in vector / bit-select ([31:0]); ternary middle
.Hierarchy separator (u_dut.internal.signal); named port connection (.clk(clk))
@Sensitivity-list marker (always @(posedge clk))
#Delay or parameter-override marker (#10, mod #(.WIDTH(8)))
'Number-base separator (8'h1F)
\Escaped-identifier marker

These tokens have hard-coded grammatical roles — the lexer always returns each as the right token class regardless of where it appears.

12. Common Mistakes

Five lexical bugs that every Verilog beginner makes at least once:

  1. Forgetting the apostrophe in numbers. 32d16 is an identifier (32d16); 32'd16 is the integer 16 in a 32-bit unsigned literal. Tools error on the first; lint catches the second. Always write the apostrophe.
  2. The 32-bit default trap. 8 + 1 is a 32-bit signed expression, not a 4-bit one. Sized literals avoid the trap: 8'd8 + 8'd1.
  3. Using a keyword as an identifier. wire wire; is a compile error. wire wir; is a typo waiting to happen. The fix: explicit, descriptive identifier names that can't collide with reserved words.
  4. Implicit nets enabled. Without `default_nettype none, a typo (data_bsu instead of data_bus) silently creates a one-bit wire and your 32-bit signal becomes garbage. Every file gets the directive at the top.
  5. $display in synthesisable hierarchy. A $display inside a synthesisable module passes simulation, gets silently dropped by the synthesiser, and your silicon does something different. Testbench code lives in testbench files.

13. Lexical Cheatsheet

The single page you keep open while writing Verilog:

lexical-cheatsheet.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ────────────── Whitespace ──────────────
// Spaces, tabs, newlines — all equivalent. No semantic meaning.
 
// ────────────── Comments ──────────────
// single-line comment
/* multi-line
   comment */
 
// ────────────── Identifiers ──────────────
clk                     // legal — letter start
rst_n                   // legal — underscore inside
_internal_w             // legal — underscore start
bus32                   // legal — digit inside
\bus[31:0]              // escaped identifier — backslash then whitespace
 
// ────────────── Keywords (sample) ──────────────
module endmodule always initial begin end if else
wire reg input output inout assign
 
// ────────────── Numbers ──────────────
8'hA5                   // 8-bit hex     → 10100101
4'b1010                 // 4-bit binary  → 1010
32'd16                  // 32-bit decimal → 16
32'h0                   // 32-bit zero
32'hDEAD_BEEF           // underscores are spacers
8'bxxxx_xxxx            // 8-bit unknown
1'bz                    // 1-bit high-impedance
{WIDTH{1'b0}}           // parametric all-zero
{8{a[0]}}               // 8 copies of one bit
 
// ────────────── Operators ──────────────
+ - * / % **            // arithmetic
~  &  |  ^  ~^  ^~      // bitwise (per-bit on vectors)
&  |  ^  ~&  ~|  ~^     // reduction (across one vector → 1 bit)
!  &&  ||               // logical (1-bit boolean)
<  >  <=  >=            // relational
==  !=                  // equality (X/Z-propagating)
===  !==                // case-equal (exact bit-pattern, sim-only)
<<  >>  <<<  >>>        // shifts
?  :                    // ternary (a mux)
{ , }                   // concatenation
{n{ }}                  // replication
 
// ────────────── Strings ──────────────
"hello, world"          // double-quoted, testbench-only
"value = %0d"           // printf-style format
 
// ────────────── System Tasks ──────────────
$display $monitor $strobe $write
$time    $realtime
$finish  $stop
$readmemh $readmemb
$random  $urandom
 
// ────────────── Compiler Directives ──────────────
`timescale 1ns/1ps          // every file
`default_nettype none       // every file
`include "common.vh"
`define RST_VAL 32'h0
`ifdef SIMULATION ... `endif
 
// ────────────── Punctuation ──────────────
;  ,  ( )  [ ]  { }  :  .  @  #  '  \

14. Debugging Lexical Issues

When a Verilog file fails to compile with a lexical error — and they all look similar — the workflow:

  1. Read the error message. Every simulator and synthesiser names the file and line number of the failed lex. Open the file, find the line, identify the offending token.
  2. Check for missing apostrophe. 32d16 (identifier — not legal in most contexts) vs 32'd16 (literal 16). The most common lexical error.
  3. Check for missing `default_nettype none. If the error is "undeclared identifier," the typo passed the lexer but failed the parser. Add `default_nettype none and every typo becomes a clear lexical error.
  4. Check for keyword collision. wire wire; or reg case; — both fail. Rename your identifier.
  5. Check for unclosed comment / string. /* multi-line without */ swallows the rest of the file. "unterminated string swallows the rest of the line. Modern editors highlight these in red; trust the editor.
  6. Check for stray backtick. A backtick that isn't followed by a valid directive name (`xyz) confuses the preprocessor. Watch for backticks in identifiers or strings.
  7. Compile with -Wall-style verbosity. Lint tools (SpyGlass, Verilator's --lint-only) emit warnings for legal-but-suspicious lexical patterns — implicit nets, ambiguous literals, narrow-vs-wide operand mismatches.

15. Design Review Notes

What a senior RTL reviewer flags on the lexical layer:

  1. `default_nettype none and `timescale 1ns/1ps at the top of every file. Non-negotiable.
  2. parameter and localparam for constants, not `define. Defines pollute the global macro namespace and don't respect scope.
  3. Sized literals everywhere a vector signal is assigned. data_bus <= 8'h00; not data_bus <= 0;. The first is unambiguous; the second relies on width extension and can produce sizing warnings.
  4. Underscored numbers for readability. 32'hDEAD_BEEF not 32'hDEADBEEF. 32'b1010_0101_1100_0011 not 32'b1010010111000011. Group bits in nibbles.
  5. Consistent identifier convention. Project-wide snake_case (data_q, wr_addr) — never CamelCase mixed with snake_case in the same file. Lint enforces.
  6. _q / _d / _n suffix convention. _q for register state, _d for combinational next-state, _n for active-low. Lint flags identifiers that don't follow.
  7. No $display / $random / $readmemh in synthesisable RTL. Testbench code in *_tb.v files only.
  8. Comments name why, not what. // increment counter is noise; // count cycles since reset for telemetry is useful.

16. Interview Insights

The lexical layer comes up in every interview as a sanity check that you've actually read real Verilog code. Reason through the answers.

17. Learning Roadmap

Each lexical category in this chapter is the entry point to a deeper sub-page.

17.1 Sub-pages — drill each category

  • 4.1White Space Requirements — what spaces, tabs, and newlines actually do at the lexer level.
  • 4.2Comment Implementation — single-line, multi-line, nesting rules, doxygen style.
  • 4.3Operator Usage — every operator's precedence, X-propagation, signed-vs-unsigned semantics.
  • 4.4Number Representation — every literal form, the 32-bit default trap, width extension, sized vs unsized.
  • 4.5String Handling — escape sequences, format specifiers, the $sformatf family.
  • 4.6Identifier Declaration — naming rules, escaped identifiers, project conventions.
  • 4.7Keyword Usage — the full 115-keyword list, deprecated keywords, SystemVerilog additions.

17.2 What comes next

  • Chapter 5Variables & Data Types — nets vs variables, 4-state value system, scalar vs vector vs array. The type system every signal declaration draws from.
  • Chapter 5.1Physical Data Types — the net family.
  • Chapter 5.1.1wire and tri Nets — the two workhorse net keywords.

The lexical layer is the floor every later chapter stands on. Read 4.1–4.7 once before going deeper, then refer back to the sub-pages when a specific lexical question arises.

18. Exercises

Three exercises to lock the lexical vocabulary in.

Exercise 1 — Spot the lexical error

For each snippet below, identify the lexical error (or confirm the snippet is clean). Reason about which category the error falls in.

exercise-1a.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire 1bus;
assign 1bus = data[0];
exercise-1b.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
parameter WIDTH = 8;
wire [WIDTH-1:0] data;
assign data = 8d255;
exercise-1c.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module mux2 (
    input  wire        sel,
    input  wire [7:0]  a, b,
    output wire [7:0]  case
);
    assign case = sel ? a : b;
endmodule
exercise-1d.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define RST_VAL 32'h0
always @(posedge clk) begin
    state_q <= `RST_VAL;
    /* multi-line comment starts here
       and never closes ...
    state_q <= state_d;
end

What to produce. A one-line classification of each error (which lexical category) + a one-sentence fix.

Exercise 2 — Write the literals

Write the Verilog literal for each of the following values. Use the smallest legal width, base 'h where natural, and underscores for readability.

  1. The decimal value 255 as an 8-bit unsigned literal.
  2. The decimal value 65 535 as a 16-bit unsigned literal.
  3. A 32-bit all-zero reset value.
  4. A 32-bit all-ones value (sized parametrically as WIDTH = 32).
  5. An 8-bit all-X (unknown) value.
  6. A 4-bit high-impedance value.
  7. The hex value 0xDEADBEEF as a 32-bit literal, readable.
  8. The bit pattern 1010_0101_1100_0011 as a 16-bit binary literal, with nibble grouping.

Exercise 3 — Parse the directives

Given the file header below, identify (a) what each directive does, (b) which line would compile and which would not in a strict-lint configuration, and (c) which one line every working RTL house insists on putting at the top of every file.

exercise-3.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns/1ps
`default_nettype none
`include "common_defines.vh"
`define RST_VAL 8'h0
`define WIDTH   8
 
module wire_count #(
    parameter WIDTH = 8
) (
    input  wire              clk,
    input  wire              rst_n,
    input  wire              enable,
    output wire [WIDTH-1:0]  count_o
);
 
    reg [WIDTH-1:0] count_q;
 
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)      count_q <= `RST_VAL;
        else if (enable) count_q <= count_q + 1'b1;
    end
 
    assign count_o = count_q;
 
endmodule

Hint. One `define is a code smell because it duplicates a parameter. One directive is genuinely useful for testbench-vs-RTL conditional compilation. One directive is the project's safety net against implicit-net typos.

19. Summary

Verilog's lexical layer is the first thing your code talks to — every simulator and synthesiser runs the same lexer before any RTL semantics enter the picture. The seven token categories — whitespace, comments, identifiers, keywords, numbers, strings, operators — plus the two cross-cutting families (system tasks and compiler directives) compose every line of every working file.

The day-to-day discipline:

  • `default_nettype none and `timescale 1ns/1ps at the top of every file. Non-negotiable.
  • Sized literals everywhere8'h00 not 0, 32'hDEAD_BEEF not 3735928559. The 32-bit default trap costs hours of debug when you fall into it.
  • parameter for constants, not `define — types, scope, lint-visibility.
  • `default_nettype none catches typos. Every undeclared identifier becomes a compile error instead of a silent garbage signal.
  • No $display / $random / $readmemh in synthesisable RTL. Testbench code lives in testbench files.
  • Naming convention is part of the lexical layer. _q / _d / _n suffixes, snake_case, no keyword collisions.

Master the lexical layer once and never think about it again — every later chapter assumes you can read these tokens at glance. The seven sub-pages (4.1–4.7) drill each category deeper; Chapter 5 picks up with the type system that every signal declaration draws from.