The short answer: Verilog is case-sensitive, statements end with a semicolon, and comments use // or /* */. Identifiers start with a letter or underscore. Numbers are written as size’base value, for example 8’hA5 or 4’b10x1, where the size is in bits and the base is b, o, d or h; underscores are ignored and x and z are allowed. Unsized numbers default to at least 32 bits, which is a common source of bugs.
These rules look boring, but small mistakes here cause real bugs: a number that silently loses bits, or an unsized constant that makes an expression 32 bits wide. I’ve put everything you need into one post, which replaces my older separate posts on syntax, lexical tokens and number representation.
- The basic rules
- Identifiers and keywords
- Numbers
- Strings
- Mistakes I see most often
- Directives and system tasks
Table of Contents
Part 1: The basic rules
- Verilog is case-sensitive:
dataandDataare different signals. Keywords are always lowercase. - Every statement ends with a semicolon. Blocks are grouped with
beginandend. - Whitespace (spaces, tabs, newlines) doesn’t matter except to separate tokens, so use it for readability.
- A Verilog file is made of tokens: comments, whitespace, operators, numbers, strings, identifiers and keywords.
// single-line comment
/* multi-line
comment (these can't be nested) */
module counter (input wire clk, output reg [3:0] q); // keywords are lowercase
always @(posedge clk) q <= q + 1'b1;
endmodule
Part 2: Identifiers and keywords
- An identifier can contain letters, digits, underscores and $, but must start with a letter or underscore.
- You can’t use a keyword (module, wire, reg, always…) as a name.
- Escaped identifiers start with a backslash and end with whitespace, like
\bus+index. You’ll mostly see them in netlists generated by tools, not in hand-written RTL.
My naming habits: lowercase with underscores, _n for active-low signals (rst_n), _q or _r for registered versions, and i_/o_ prefixes only if your team uses them consistently.
Part 3: Numbers

| Written as | Width | Value |
|---|---|---|
| 8’hA5 | 8 bits | 1010_0101 |
| 4’b10x1 | 4 bits | bit 1 is unknown |
| 8’bz | 8 bits | all high-impedance |
| 16’d255 | 16 bits | 0000_0000_1111_1111 |
| -8’sd5 | 8 bits, signed | −5 in two’s complement |
| ‘hFF | at least 32 bits | 255 (unsized) |
| 12 | at least 32 bits | 12, signed decimal |
- If the value has fewer bits than the size, it’s zero-extended on the left. If its leftmost digit is x or z, it’s extended with x or z instead.
- If the value has more bits than the size, it’s truncated, usually with only a warning:
4'd16becomes 0. - The minus sign goes before the size:
-8'd5, never8'd-5.
Part 4: Strings
Strings are in double quotes and stored as 8 bits per character. They’re mainly used in testbenches with $display:
reg [8*5:1] name; // room for 5 characters
initial begin
name = "adder";
$display("Testing %s", name);
end
Part 5: Mistakes I see most often
- Unsized constants in RTL.
count + 1makes the expression at least 32 bits;count + 1'b1keeps it narrow. Synthesis usually trims it, but lint tools will complain, and widths matter in comparisons. - Truncated constants.
3'd8is 0. Always check that the size fits the value. - Wrong base.
8'b255is an error; you meant8'd255or8'hFF. - Mixing case.
Clkandclkare two different signals, and an implicit wire may hide the mistake.
Part 6: Directives and system tasks
Two kinds of tokens have their own prefix. Compiler directives start with a backtick, like `define, `include and `timescale; system tasks and functions start with $, like $display and $finish. I cover both later in the series: parameters and defines and system tasks.
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
RTL Design problems · Daily challenge
Previous in my Verilog series: What is Verilog?. Next: Verilog data types.
FAQ
What does ‘b mean in Verilog?
It marks a binary number. ‘o is octal, ‘d decimal and ‘h hexadecimal. The number before the apostrophe is the width in bits.
What happens if I don’t give a size?
The number is treated as at least 32 bits. That’s fine for loop counters in testbenches, but in RTL it can widen expressions unexpectedly.
Can I use underscores in numbers?
Yes, anywhere except the first character. They’re ignored and make long values easier to read: 32’hDEAD_BEEF.
Is Verilog case-sensitive?
Yes. Signal names, module names and keywords are all case-sensitive, and keywords must be lowercase.
