Lesson 05: Operands, Expressions, and Operators
variable = expression ;
expression ⇒ <operand1> <operator> <operand2>
5.1 Operands and Expressions
Operands
Operands are the basic units on which operations are performed in expressions. In Verilog, operands can be:
- Constants: These are fixed values.
- Integer constants: 123, -45
- Real constants: 3.14, -2.71
- String constants: "Hello"
- Bit-string constants: 4'b1010 (binary), 8'h1A (hexadecimal)
- Variables: Named storage locations.
- Register variables: reg [3:0] my_reg;
- Net variables: wire [7:0] my_wire;
- Bit-selects: Selecting a specific bit from a vector.
- my_reg[2]
- Part-selects: Selecting a range of bits from a vector.
- my_reg[3:1]
- Concatenations: Combining multiple expressions.
- {a, b, c}
- Function calls: Invoking functions.
- my_function(a, b)
Expressions
Expressions in Verilog are combinations of operators and operands that evaluate to a value. They can be classified into different types based on their usage:
- Primary Expressions: Basic expressions involving constants, variables, bit-selects, part-selects, concatenations, and function calls.
- Examples: a, 4'b1010, my_reg[3], {a, b, c}
- Unary Expressions: Involve a single operand and a unary operator.
- Examples: ~a, !b, -c
- Binary Expressions: Involve two operands and a binary operator.
- Examples: a + b, c && d, e | f
- Ternary (Conditional) Expressions: Use the conditional operator ? : to choose between two values based on a condition.
- Example: a ? b : c
- Syntax: condition ? true_value : false_value
5.2 Operators
Special Operators
Special Operators
Operator | Description | Example | |
---|---|---|---|
[ ] | Bit-select or part-select | ||
( ) | Parentheses | ||
? : | Conditional | sel? m : n | if sel is true, return m; else return n |
{ } | Concatenation | {m,n} | concatenate m to n, creating a larger vector |
{{ }} | Replication | {n{ }} | replicate inner concatenation n-times |
Bit-Selects
A bit-select selects a single bit from a wire, register, or parameter vector. The value of the expression in brackets ([ ]) selects the bit you want from the vector. The selected bit must be within the declared range of the vector. The following simple example shows a bit-select with an expression.
wire [7:0] a, b, c;
assign c[0] = a [0] & b[0];
Part-Selects
A part-select selects a group of bits from a wire, register, or parameter vector. Unlike the bit-select operator, the part-select expression must be constant-valued in the Verilog language. If a variable is declared with ascending or descending indices, the part selected (when applied to that variable) must be in the same order when applied to that variable.
You can also write the expression in the example of the wire operands, as shown in the example below.
assign c[7:0] = a[7:0] & b[7:0];
Conditional Operator
The conditional operator has three operands separated by two operators in the following format:
(cond_expr) ? true_expr : false_expr ;
The true_expr or the false_expr is evaluated and used as a result depending on whether cond_expr evaluates to true or false.
Concatenation Operator
Concatenation combines one or more expressions to form a larger vector. In the Verilog language, you indicate concatenation by listing all expressions to be concatenated, separated by commas, in curly braces ({ }). Any expression except an unsized constant is allowed in a concatenation. For example, the concatenation {1'b1, 1'b0, 1'b0} yields the value 3'b100.
Unsized constant numbers are not allowed in concatenations because the size of each operand in the concatenation is needed to calculate the complete size of the concatenation.
Replicate Operator
The replicate operator in Verilog is a convenient way to repeat a specific bit or bit vector multiple times. It is denoted by { } and is often used to create larger bit vectors from smaller ones by replicating them.
Syntax
The syntax for the replicate operator is as follows:
{repeat_count{expression}}
- repeat_count: The number of times the expression should be repeated.
- expression: The bit or bit vector to be replicated.
{ 3{2'b10} } = 6'b101010
Examples of concatenation operators
// A = 1'b1; B = 2'b00; C = 2'b10; D = 3'b110;
Y = {B, C}; // Result Y is 4'b0010
Y = {A, B, C, D, 3'b001}; // Result Y is 11'b10010110001
Y = {A, B[0], C[1]}; // Result Y is 3'b101
Use concatenation to change A[15:0] to swap the upper 8-bits with the lower 8-bits, giving the value to vector W.
W = { Z[7:0], Z[15:8] };
Examples of replication operators
// A = 1'b1; B = 2'b00; C = 2'b10; D = 3'b110;
Y = { 4{A} }; // Result Y is 4'b1111
Y = { 4{A}, 2{B} }; // Result Y is 8'b11110000
Y = { 4{A}, 2{B}, C}; // Result Y is 10'b1111000010
Y = {4{2'b10}} | {4{2'b01}}; // Result Y is 8'b11111111
Arithmetic Operators
Arithmetic Operators
Arithmetic operators perform simple arithmetic on operands. The Verilog arithmetic operators follow.
Operator | Description | Example | |
---|---|---|---|
+ | Binary addition | m + n | add n to m |
- | Binary subtraction | m - n | subtract n from m |
* | Binary multiplication | m * n | multiply m by n |
/ | Binary division | m / n | divide m by n |
% | Modulus (remainder) | m % n | modulus of m / n (remainder of m / n) |
** | Power (exponent) | m ** n | m to the power of n (new in Verilog-2001) |
Arithmetic Operation Rules
For most operators (there are exceptions), all operands in the expression are used to determine how the operation is performed:
- If any operand is real, then floating-point arithmetic will be performed.
- If any operand is unsigned, then unsigned arithmetic will be performed.
- If all operands are signed, then signed arithmetic will be performed.
- An operand can be "cast" to signed or unsigned using the $signed and $unsigned system functions (added in Verilog-2001).
Binary operators take two operands
A = 4'b0011; B = 4'b0100; // A and B are register vectors
D = 6; E =4; F = 2; // D, E and F are integer
A + B // Add A and B. Evaluates to 4'b0111
B - A // Subtract A from B. Evaluates to 4'b0001
A * B // Multiply A and B. Evaluates to 4'b1100
D / E // Divide D by E. Evaluates to 1. Truncates any fractional part.
F = E ** F; // E to the power F, yields 16
Operand bit has x value
If any operand bit has a value x, then the result of the entire expression is x. This seems intuitive because if an operand value is not known precisely, the result should be an unknown.
in1 = 4'b101x;
in2 = 4'b1010;
sum = in1 + in2; // sum will be evaluated to the value 4'bx
Examples of modulus operators
Modulus operators produce the remainder from the division of two numbers. They operate similarly to the modulus operator in the C programming language.
13 % 3 // Evaluates to 1
16 % 4 // Evaluates to 0
-7 % 2 // Evaluates to -1, take the sign of the first operand
7 % -2 // Evaluates to +1, take the sign of the first operand
Unary (Sign) Operators
Unary (Sign) Operators
The operators + and - can also work as unary operators. They are used to specify the positive or negative signs of the operand. Unary + (positive) or - (negative) operators have higher precedence than the binary + (addition) or - (subtraction) operators.
Operator | Description | Example | |
---|---|---|---|
+ | Positive number | +m | positive m |
- | Negative number | -m | negative m (2's complement) |
-6 // Negative 6
+3 // Positive 3
Negative numbers
Negative numbers are represented as 2's complement internally in Verilog. It is advisable to use negative numbers only of the integer or real in expressions. You should avoid negative numbers of the type <sss>'<base><nnn> in expressions because they are converted to unsigned 2's complement numbers and hence yield unexpected results.
// Advisable to use integer or real numbers
-10 / 5 // Evaluates to -2
// Do not use number of type <sss>`<base><nnn>
-'d10 / 5 // Is equivalent (2's complement of 10) / 5 = (2^32 - 10)/5
// when 32 is the default machine word width.
// The evaluates to an incorrect and unexpected result.
Bitwise Operators
Bitwise Operators
Bitwise operators act on the operand bit by bit. The Verilog bitwise operators follow.
Operator | Description | Example | |
---|---|---|---|
~ | Bitwise not | ~m | invert each bit of m |
& | Bitwise and | m & n | logic AND each bit of m with each bit of n |
| | Bitwise or | m | n | logic OR each bit of m with each bit of n |
^ | Bitwise xor | m ^ n | logic exclusive-or each bit of m with each bit of n |
^~ or ~^ | Bitwise xnor | m ~^ n | logic exclusive-nor each bit of m with each bit of n |
Each bit is operated, resulting in the size of the largest operand, and the smaller operand is left extended with zeros to the size of the bigger operand.
Examples of bitwise operators
// X = 4'b1010, Y = 4'b1101, Z = 4'b10x1
~X // Negation. Result is 4'b0101
X & Y // Bitwise and. Result is 4'b1000
X | Y // Bitwise or. Result is 4'b1111
X ^ Y // Bitwise xor. Result is 4'b0111
X ^~ Y // Bitwise xnor. Result is 4'b1000
X & Z // Result is 4'b10x0
Boolean Logical Operators
Boolean Logical Operators
Logical operators generate a 1 or a 0, according to whether an expression evaluates to TRUE (1) or FALSE (0). The Verilog logical operators follow.
Operator | Description | Example | |
---|---|---|---|
! | Logical NOT | !m | is the m not true? (1-bit True/False result) |
&& | Logical AND | m && n | are both m and n true? (1-bit True/False result) |
|| | Logical OR | m || n | are either m or n true? (1-bit True/False result) |
- Logical operators always evaluate to a 1-bit value. 0 (false), 1 (true), or x (ambiguous).
- If an operand is not equal to zero, it is equivalent to a logical 1 (true condition).
If an operand equals zero, it equals a logical 0 (false condition).
If any operand bit is x or z, it is equivalent to x (ambiguous condition) and is normally treated by simulators as a false condition. - Logical operators take variables or expressions as operands.
Using parentheses to group logical operations is highly recommended to improve readability. Also, the user does not have to remember the precedence of operators.
Examples of boolean logical operators
// Logical operations
A = 3; B = 0;
A && B // Evaluates to 0. Equivalent to (logical-1 && logical-0)
A || B // Evaluates to 1. Equivalent to (logical-1 || logical-0)
!A // Evaluates to 0. Equivalent to not(logical-1)
!B // Evaluates to 1. Equivalent to not(logical-0)
// Unknowns
A = 2'b0x; B = 2'b10;
A && B // Evaluates to x. Equivalent to (x && logical-1)
// Expressions
(a == 2) && (b == 3) // Evaluates to 1 if both (a == 2) and (b == 3) are true
// Evaluates to 0 if either is false
Logical operator vs bitwise operators
It is important to distinguish bitwise operators ~, &, and | from logical operators !, &&, ||. Logical operators always yield a logical value 0, 1, x, whereas bitwise operators yield a bit-by-bit value. Logical opertors perform a logical operation, not a bit-by-bit operation.
// X = 4'b1010, Y = 4'b0000
X | Y // Bitwise operation. Result is 4'b1010
X || Y // Logical operation. Equivalent to 1 || 0. Result is 1
Reduction Operators
Reduction Operators
Reduction operators take a single vectored (multiple-bit) operand, perform the appropriate bit-wise reduction on all bits of the operand, and return a single-bit result. The Verilog reduction operators follow:
Operator | Description | Example (m = 5'b10101) | ||
---|---|---|---|---|
& | Reduction AND | & m | AND all bit in m together (1-bit result) | => 1'b0 |
| | Reduction OR | | m | OR all bit in m together (1-bit result) | => 1'b1 |
~& | Reduction NAND | ~& m | NAND all bit in m together (1-bit result) | => 1'b1 |
~| | Reduction NOR | ~| m | NOR all bit in m together (1-bit result) | => 1'b0 |
^ | Reduction XOR | ^ m | XOR all bit in m together (1-bit result) | => 1'b1 |
~^ or ^~ | Reduction XNOR | ~^ m | XNOR all bit in m together (1-bit result) | => 1'b0 |
Verilog has a special syntax restriction on using both reduction and bitwise operators within the same expression — even though the reduction operator has higher precedence, parentheses must be used to avoid confusion with a logical operator.
a & (&b)
a | (|b)
Examples of reduction operators 1
For example, the four bits of a are anded together to produce y1.
module Reduction (a, y1, y2, y3, y4, y5, y6); input [3:0] a; output y1, y2, y3, y4, y5, y6; reg y1, y2, y3, y4, y5, y6; always @(a) begin y1 = &a; //reduction and : y1 = a[0] & a[1] & a[2] & a[3] y2 = |a; //reduction or : y2 = a[0] | a[1] | a[2] | a[3] y3 = ^a; //reduction xor : y3 = a[0] ^ a[1] ^ a[2] ^ a[3] y4 = ~&a; //reduction nand: y4 = a[0] ~& a[1] ~& a[2] ~& a[3] y5 = ~|a; //reduction nor : y5 = a[0] ~| a[1] ~| a[2] ~| a[3] y6 = ~^a; //reduction xnor: y6 = a[0] ~^ a[1] ~^ a[2] ~^ a[3] end endmodule
Examples of reduction operators 2
// X = 4'b1010
&X // Equivalent to 1 & 0 & 1 & 0. Results in 1'b0
|X // Equivalent to 1 | 0 | 1 | 0. Results in 1'b1
^X // Equivalent to 1 ^ 0 ^ 1 ^ 0. Results in 1'b0
// A reduction xor or xnor can be used for even or odd parity
// generation of a vector.
Shift Operators
Shift Operators
A shift operator takes two operands and shifts the value of the first operand right or left by the number of bits given by the second operand.
The Verilog shift operators follow.
Operator | Description | Example | |
---|---|---|---|
<< | Shift left | m << n | shift m left n-times and fill with zeros |
>> | Shift right | m >> n | shift m right n-times and fill with zeros |
<<< | Arithmetic shift left | m <<< n | shift m left n-times, filling with 0 (new in Verilog-2001) |
>>> | Arithmetic shift right | m >>> n | shift m right n-times; fill with values of sign-bit if the expression is signed otherwise fill with 0 (Verilog-2001) |
The logical shift will insert 0's to the vacated bits on the left or right. On the other hand, arithmetic shift right (>>>) will sign extend, padding the left side with the value of the MSb (sign bit) when shifting to the right. The left shift is the same between both logical (<<) and arithmetic (<<<) since zeros are inserted in both cases.
Examples of shift operators
// X = 4'b1100
X >> 1 // Result is 4'b0110. Shift right 1 bit. 0 filled in MSb position.
X << 1 // Result is 4'b1000. Shift left 1 bit. 0 filled in LSb position.
X << 2 // Result is 4'b0000. Shift left 2 bits.
X <<< 1 // Result is 4'b1000.
X >>> 1 // Result is 4'b1110.
X >>> 3 // Result is 4'b1111.
X >>> 0 // Result is 4'b1100.
integer a, b, c; // Signed data type
a = 0;
b = -10; // 11111...10110 binary
c = a + (b >>> 3); // Results in -2 decimal, due to arithmetic shift
Relational Operators
Equality and Relational Operators (return X if an operand has X or Z)
Relational operators compare two operands and result in a 1-bit scalar boolean value. A true comparison evaluates to 1; a false comparison evaluates to 0. All comparisons assume unsigned quantities. The circuitry synthesized for relational operators is a bit-wise comparator whose size is based on the sizes of the two operands.
The Verilog relational operators follow.
Operator | Description | Example | |
---|---|---|---|
== | Logical equality | m == n | is m equal to n? (1-bit True/False result) |
!= | Logical inequality | m != n | is m not equal to n? (1-bit True/False result) |
> | Logical greater than | m > n | is m greater than n? (1-bit True/False result) |
>= | Logical greater than or equal to | m >= n | is m greater than or equal to n? (1-bit True/False result) |
< | Logical less than | m < n | is m less than n? (1-bit True/False result) |
<= | Logical less than or equal to | m <= n | is m less than or equal to n? (1-bit True/False result) |
If there are any unknown(x) or high-impedance (z) bits in the operands, the expression takes an unknown value (x). These operators function exactly as the corresponding operators in the C programming language.
Examples of relational operators
// A = 4; B = 3;
// X = 4'b1010; Y = 4'b1101; Z = 4'b1xxx;
A <= B // Evaluates to a logical 0
A > B // Evaluates to a logical 1
Y >= X // Evaluates to a logical 1
Y < Z // Evaluates to an x
Identity Operators (Compare Logic Values 0, 1, x, and z)
Operator | Description | Example | |
---|---|---|---|
=== | m === n | is m identical to n? (1-bit True/False result) | |
!=== | m !== n | is m not identical to n? (1-bit True/False result) |
It is important to note the difference between the logical equality operators (==, !=) and identity operators (===, !==).
- == Operator (Equality)
- The == operator is used for equality comparison.
- It performs a bitwise comparison of two values.
- It treats x (unknown) and z (high impedance) values as 'don't care' during the comparison.
- === Operator (Case Equality):
- The === operator is used for case equality comparison.
- It performs a bitwise comparison and considers x and z values as part of the comparison.
- If either operand contains an x or z, they must match exactly for the result to be true.
== can be synthesized into hardware (xor gate), but === cannot be synthesized as x is not a valid logic level in digital; it has a logic value between 0 and 1. And z is not any logic itself; it shows the disconnection of the circuit.
Examples of equality operators
Equality example:
a = 4'b0101; // 5 in binary b = 4'b0101; // 5 in binary result = (a == b); // result will be 1 (true) a = 4'b0101; // 5 in binary b = 4'b010x; // x in the least significant bit result = (a == b); // result will be 1 (true) because 'x' is treated as 'don't care'
Case equality example:
a = 4'b0101; // 5 in binary b = 4'b0101; // 5 in binary result = (a === b); // result will be 1 (true) a = 4'b0101; // 5 in binary b = 4'b010x; // x in the least significant bit result = (a === b); // result will be 0 (false) because 'x' must match exactly
Assignments
Assignments
The assignment is the basic mechanism for getting values into nets and registers. An assignment consists of two parts, a left-hand side, and a right-hand side, separated by the equal sign (=). The right-hand side can be any expression that evaluates a value. The left-hand side indicates the variable to which the right-hand side is assigned. The left-hand side can take one of the following forms, depending on whether the assignment is a continuous assignment or a procedural assignment:
Legal Left-hand Side Forms in Assignment Statements | |
---|---|
Statement | Left-hand Side |
Continuous Assignment | Net (vector or scalar) Constant bit-select of a vector net Constant part-select of a vector net Constant word selection of memory |
Procedural Assignment | Register (vector or scalar) Bit-select of a vector register Constant part-select of a vector register Memory element Concatenation of any of the above four items |
Continuous Assignment
Continuous Assignment (Dataflow Modeling)
All assignment statements (with keyword assign) outside of an always block are called continuous assignments. They are always active, concurrent, occur simultaneously, and are not sequential. The output of the operation on the Right-Hand Side (RHS) of the = symbol is continuously assigned to the variable on the Left-Hand Side (LHS)
// assign net = expression;
assign a = b & c;
LHS must be a scalar or vector of nets, and assignment must be performed outside procedure statements.
You can read "Lesson 09: Dataflow Modeling" for detailed information about the continuous assignment.
Procedural Assignment
Procedural assignments occur within procedures such as always, initial, task, and function and are used to place values onto variables. LHS must be a scalar or vector of registers and only active (evaluated and loaded) when control is transferred to it. After that, the value of the register remains until it is reassigned by another procedural assignment.
There are two types of procedural assignments:
- Blocking Assignment
Blocking assignments are executed in the order specified in the sequential block, i.e., a blocking assignment waits for a previous blocking assignment of the same time to complete before executing.
// register = expression
b = a; // assign a value to b, now b become to a
c = b; // assign b (value a) to c. The final results: c = a, b = a.
- Non-Blocking Assignment
Nonblocking assignments are executed concurrently within the sequential blocks, i.e., a nonblocking assignment executes without waiting for other nonblocking assignments to occurring at the same time to complete
// register <= expression
b <= a; // assign a to b
c <= b; // assign old b to c. The final result: c = b, b = a
Procedural Continuous Assignments
Procedural Continuous Assignments are also called Quasi-Continuous Assignments. Procedural statements allow expressions to be continuously assigned to nets or variables of two types.
- assign ... deassign
- force ... release
assign ... deassign
Similar to procedural assignment, however, the quasi-continuous assignment becomes active and stays active from the point of the assignment until deactivated through reassignment. When active, quasi-continuous assignment overrides any procedural assignment to the register.
begin
...
assign register = expressions; // Activate quasi-continuous
...
register = expression2; // No effect. Overridden by active quasi-continuous
...
assign register = expression3; // Becomes active and overrides previous quasi-continuous
...
deassign register; // Disable quasi-continuous
...
register = expression4; // Executed
...
end
force ... release
These are similar to the assign - deassign statements but can also be applied to nets and variables. The LHS can be a bit-select of a net, part-select of a net, variable, or net but cannot be the reference to an array and bit/part select of a variable. The force statement will override all other assignments made to the variable until it is released using the release keyword.
reg i, a, b; initial begin force i = a & b; ... release i; end
Procedural continuous assignments place the assign inside an always/initial block. It is not synthesizable and should not be used. It is used for very special modeling cases.