Literals, vectors, and operators
Write unambiguous constants, manipulate bits, and avoid width and signedness pitfalls.
Giving constants a width
A Verilog constant can specify its width, base, and value:
8'b1010_0110
8'hA6
8'd166
12'o245These items do not all have the same width: the first three occupy 8 bits, while the last one occupies 12 bits. Underscores improve readability without changing the value.
The general form is width'basevalue. Common bases are b for binary, o for octal, d for decimal, and h for hexadecimal.
An unsized constant such as 5 follows implicit rules that can widen an expression or change its signed interpretation. In RTL, writing 4'd5 or 8'd5 makes the intent clearer.
Vectors and bit selection
wire [7:0] w_byte;
wire [3:0] w_high;
wire w_lsb;
assign w_high = w_byte[7:4];
assign w_lsb = w_byte[0];[7:0] declares an 8-bit vector. w_byte[0] selects one bit and w_byte[7:4] selects a contiguous part.
The [most_significant:least_significant] convention is common for buses. You should still read the declaration because Verilog also accepts ranges in the other direction.
Concatenating and repeating
Braces assemble several pieces:
assign w_packet = {w_header, w_payload};
assign w_mask = {4{2'b10}};The first line places w_header in the most significant bits. The second repeats 2'b10 four times and produces 8'b1010_1010.
Concatenation can also extend a signed number manually by copying its sign bit.
Four logic states
Verilog simulates the values 0, 1, x, and z:
xmeans an unknown value or a conflict;zmeans high impedance;0and1are known logic levels.
An x value is a useful warning in simulation. Replacing it too quickly with 0 can hide a missing reset or an uncovered branch.
Operator families
| Family | Examples | Result |
|---|---|---|
| arithmetic | +, -, * | calculation on numbers |
| bitwise | &, ` | , ^, ~` |
| logical | &&, ` | |
| comparison | <, >=, ==, != | one-bit result |
| reduction | &bus, ` | bus, ^bus` |
| shift | <<, >> | movement of bits |
& and && are not interchangeable. a & b works bit by bit; a && b tests whether the operands represent a true condition.
Widths and signed numbers
The result width depends on the operand widths and context. A carry can therefore be lost:
wire [7:0] i_a;
wire [7:0] i_b;
wire [8:0] o_sum;
assign o_sum = {1'b0, i_a} + {1'b0, i_b};The operands are extended to 9 bits before the addition. The final carry can then be preserved.
For signed calculations, declare objects with signed and check every extension. Mixing a signed vector with an unsigned vector can produce a surprising result.
Complete example
module word_tools (
input wire [7:0] i_word,
input wire signed [7:0] i_delta,
output wire [3:0] o_high_nibble,
output wire o_odd_parity,
output wire signed [8:0] o_delta_ext,
output wire [15:0] o_repeated
);
assign o_high_nibble = i_word[7:4];
assign o_odd_parity = ^i_word;
assign o_delta_ext = {i_delta[7], i_delta};
assign o_repeated = {
The XOR reduction is 1 when i_word contains an odd number of set bits. The extension of i_delta copies its sign bit.
Key points
- Giving constants a width avoids many ambiguities.
- A part-select preserves exactly the requested bits.
&works bit by bit, while&&forms a condition.- An addition must be widened before calculation if the carry is needed.
- Signed calculations require consistent declarations and extensions.
📝 Test your knowledge - Chapter quiz