Values, types, expressions and operators
- Use four-state values, nets, procedural variables, sized literals, concatenation, replication, arithmetic, logical, bitwise, reduction, comparison, and shift operators
- Predict width and signedness effects
Explanation
Lexical details that prevent avoidable errors
Verilog identifiers are case-sensitive: dataReady and dataready name
different objects. Use consistent names, and keep comments and whitespace for
readability; they separate tokens but do not change the hardware meaning. An
ordinary identifier starts with a letter or underscore and may contain digits
after the first character. Escaped identifiers begin with a backslash and end
at whitespace, so they are usually avoided in RTL because they are awkward to
read and connect.
Four-state values
Verilog simulation uses 0, 1, x, and z.
xmeans the value is unknown or cannot be determined.zmeans high impedance, usually an undriven or tri-stated connection.
Do not force an x to zero merely to make a waveform look clean. Find whether it comes from missing reset, incomplete assignment, invalid address, multiple drivers, or a testbench race.
Nets and procedural variables
wire [7:0] sum_wire; // driven by assign or a module output
reg [7:0] sum_reg; // assigned inside initial or always
In Verilog, reg describes a procedural variable. The name does not guarantee a physical register. A combinational always block can drive a reg, while incomplete assignment can accidentally infer a latch.
Literals must communicate width
8'b1010_0110 // 8-bit binary
12'hA5C // 12-bit hexadecimal
6'd35 // 6-bit decimal
4'b10xz // four-state literal
Prefer explicit widths in RTL. Truncation drops high bits. Assigning a narrow unsigned value to a wider destination zero-extends it. Signed extension depends on the expression being signed.
reg signed [7:0] temperature;
wire signed [8:0] extended = {temperature[7], temperature};
Make signed conversion visible with $signed(...) or $unsigned(...) when mixed expressions would be unclear.
Operator groups
| Group | Examples | Meaning |
|---|---|---|
| Arithmetic | + - * / % |
Numeric calculation |
| Bitwise | & \| ^ ~ |
Operate independently on bits |
| Logical | && \|\| ! |
Treat operands as true or false |
| Reduction | &a, \|a, ^a |
Reduce a vector to one bit |
| Comparison | == != < <= > >= |
Compare values |
| Case equality | === !== |
Include x and z in comparison |
| Shift | << >> <<< >>> |
Move bits; arithmetic right shift needs signed intent |
| Conditional | c ? a : b |
Select one expression |
Use parentheses when a reader might need to remember precedence.
Concatenation and replication
wire [15:0] word = {high_byte, low_byte};
wire [31:0] mask = {4{8'hA5}};
wire [8:0] full_sum = {1'b0, a} + {1'b0, b};
The extended operands in full_sum preserve the carry. Assigning an 8-bit addition directly to nine bits can still lose the carry if the expression was evaluated at only eight bits.
Equality in design and checking
Logical equality (==) may return x when an operand contains unknown bits. Case equality (===) produces a definite test result and is useful in testbench checks. Case equality in synthesizable datapaths usually hides the wrong question: real gates do not implement a simulation-only unknown state.
Worked example
For unsigned a and b declared [7:0], calculate a carry-preserving sum:
wire [8:0] sum = {1'b0, a} + {1'b0, b};
wire carry = sum[8];
wire [7:0] result = sum[7:0];
For a=8'hFF and b=8'h01, sum is 9'h100, carry is one, and result is zero.
Diagnose signed extension before changing the destination width
Making the destination wider does not tell the simulator whether a bit pattern represents a positive or negative value. The operands must carry the intended signed interpretation into the calculation.
reg signed [7:0] a;
reg signed [7:0] b;
wire signed [8:0] signed_sum;
wire [8:0] zero_extended_sum;
assign signed_sum = {a[7], a} + {b[7], b};
assign zero_extended_sum = {1'b0, a} + {1'b0, b};
For a = -8'sd5 and b = 8'sd3, sign extension produces the nine-bit
two's-complement value 9'h1FE (−2). Zero extension instead produces
9'h0FE; the bit pattern is valid, but it describes a different number. When
an arithmetic result is surprising, check the declaration and extension of
each operand first, then inspect the intermediate result before its final
assignment.
| Review question | What to inspect |
|---|---|
| Can the mathematical result exceed the operand width? | Extend operands before the operation and size the intermediate result. |
| Is a negative value involved? | Confirm both operands are signed as intended and sign-extended. |
| Is only a slice assigned? | Confirm discarded bits are intentionally unused or captured as carry/status. |
| Does a test contain unknown bits? | Use a strict testbench comparison and locate the first source of x/z. |
Check yourself
Why does reg not necessarily mean a flip-flop? What difference between == and === matters in a self-checking testbench? How can an arithmetic carry be lost before assignment?