A binary operator is an operator that takes exactly two arguments. Examples of such are addition or multiplication and, of course, the familiar Boolean logical operators we use in VHDL: **and**, **or** **nand** **nor** **xor** and **xnor**.

The two operands may be simple Boolean values on an operator’s left- and right-hand sides: `op_a or op_b`

. Or `op_a`

and `op_b`

may be sub-expressions that must be calculated before the main OR expression. However, if you’ve done Boolean algebra manually, you may have discovered that we sometimes don’t need to calculate both operands to know that answer.

Consider the OR operator, which takes a left- and a right-hand operand: `op_a or op_b`

. If we calculate one of the operand sides and it evaluates to `true`

, there’s no need to look at the other side because the result will be `true`

regardless of the other value. That’s how the OR operator works; if either of the expressions is true, the result is true.

## Short-circuit evaluation as a performance optimization

Many programming languages use this information to improve performance. Where possible, they will only evaluate the right-hand operator if the left-hand operator doesn’t have a decisive value. It’s called short-circuit evaluation.

VHDL also uses short-circuit evaluation, but only for some operations and data types.

The operators are: **and**, **or**, **nand**, and **nor**.

And it will only use short-circuiting when operating on these two data types: **bit** and **boolean**. It will not short-circuit when using the std_logic type because it’s not predefined in VHDL (it’s defined in the `ieee.std_logic_1164`

package).

## Function side effects

In most cases, you won’t notice that short-circuit evaluation is taking place. And that’s a good thing! It’s speeding up the simulator without causing any issues.

The problems only surface if the right-hand operator is a function that changes a variable or signal. And when that happens, it can lead to strange bugs that will leave you scratching your head unless you are aware of short-circuiting VHDL operators.

We say that a function has side effects if it can access objects not coming through the parameter list. If we declare a function in the declarative region of a process, it can read or write to any variable or signal that the process can access, but only if we declare it as an *impure* function.

As soon as we write the VHDL keyword `impure`

before the function declaration, we can manipulate objects not coming through the function’s parameter list. That’s opposed to a regular *pure* function, which is guaranteed to return the same value whenever you call it with a given set of arguments.

### Using boolean type

To demonstrate short-circuit evaluation in action, I’ve created the example process below that we will try out. It has an impure function that updates a value as a side effect (line 10). The function also prints a line of text every time it’s called that we can observe in the simulator’s transcript.

Finally, we call the function twice in the process. First, with a left-hand argument that won’t cause the right-hand argument not to run (line 20), and then with a left-hand argument `is_even(2)`

(line 24) that evaluates to `true`

, meaning that the simulator can short-circuit the operation and omit running the right-hand argument `is_even(3)`

.

TEST_PROC : process variable check_count : integer := 0; impure function is_even(number : integer) return boolean is variable r : boolean; begin r := number rem 2 = 0; check_count := check_count + 1; report "Check #" & integer'image(check_count) & " is_even(" & integer'image(number) & ") = " & boolean'image(r); return r; end function; begin report "Test_A = " & boolean'image( is_even(1) or is_even(3) ); report "Test_B = " & boolean'image( is_even(2) or is_even(3) ); wait; end process;

As we can see from the printout below, the function was only called three times. The last right-hand argument never got evaluated due to short-circuiting of the OR operator.

# ** Note: Check #1 is_even(1) = false # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Check #2 is_even(3) = false # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Test_A = false # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Check #3 is_even(2) = true # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Test_B = true # Time: 0 ns Iteration: 0 Instance: /test_tb

### Using bit type

As mentioned, VHDL’s short-circuit operations only apply to `boolean`

and `bit`

types. For completeness, I’ve created an example that uses `bit`

instead of `boolean`

. The highlighted lines in the code below are the ones I have modified.

TEST_PROC : process variable check_count : integer := 0; impure function is_even(number : integer) return bit is variable r : boolean; begin r := number rem 2 = 0; check_count := check_count + 1; report "Check #" & integer'image(check_count) & " is_even(" & integer'image(number) & ") = " & boolean'image(r); if r then return '1'; else return '0'; end if; end function; begin report "Test_A = " & bit'image( is_even(1) or is_even(3) ); report "Test_B = " & bit'image( is_even(2) or is_even(3) ); wait; end process;

The printout below shows that the last and redundant right-hand argument is never evaluated, as in the previous example.

# ** Note: Check #1 is_even(1) = false # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Check #2 is_even(3) = false # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Test_A = '0' # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Check #3 is_even(2) = true # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Test_B = '1' # Time: 0 ns Iteration: 0 Instance: /test_tb

### Using std_logic type

Now let’s try changing the previous example to use `std_logic`

instead of `bit`

types. VHDL doesn’t support short-circuit operations for `std_logic`

because it’s not a predefined type.I have highlighted the changed lines in the code below.

TEST_PROC : process variable check_count : integer := 0; impure function is_even(number : integer) return std_logic is variable r : boolean; begin r := number rem 2 = 0; check_count := check_count + 1; report "Check #" & integer'image(check_count) & " is_even(" & integer'image(number) & ") = " & boolean'image(r); if r then return '1'; else return '0'; end if; end function; begin report "Test_A = " & std_logic'image( is_even(1) or is_even(3) ); report "Test_B = " & std_logic'image( is_even(2) or is_even(3) ); wait; end process;

The printout below shows an additional line we haven’t seen in any previous runs: `Check #4 is_even(3) = false`

. It’s evident that the right-hand argument of the second OR statement got evaluated. The `is_even`

function was called four times because there was no short-circuiting of the OR statement.

# ** Note: Check #1 is_even(1) = false # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Check #2 is_even(3) = false # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Test_A = '0' # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Check #3 is_even(2) = true # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Check #4 is_even(3) = false # Time: 0 ns Iteration: 0 Instance: /test_tb # ** Note: Test_B = '1' # Time: 0 ns Iteration: 0 Instance: /test_tb

## How about synthesis?

Now that you know how short-circuit evaluation works in VHDL, you may wonder if it has any implications for synthesis. The answer is: **no**. It’s only a simulation optimization.

However, the synthesis tool will optimize away any redundant logic it discovers in your design, regardless of short-circuit evaluation rules.

Consider the example below where the rightmost AND operator relies on the result from a sub-expression which is a tautology (always true). The simulator would never have looked at the right-hand argument with short-circuit evaluation. But what will the synthesis tool do?

entity operation is port ( a, b, c : in bit; y : out bit ); end operation; architecture rtl of operation is begin -- ((b and c) or (b nand c)) = always true y <= ((b and c) or (b nand c)) and a; end architecture;

After elaboration, we get the circuit we described, also containing the redundant logic that always evaluates to true:

But after synthesis, which is where optimization takes place, we see that the tool has correctly identified and removed the tautology. The simplified expression is simply `y <= a`

:

Optimization saved resources, but it wasn't due to short-circuit evaluation. In fact, we will get the same result if we swap the operands and even if we use `std_logic`

or any other similar data type:

library ieee; use ieee.std_logic_1164.all; entity operation is port ( a, b, c : in std_logic; y : out std_logic ); end operation; architecture rtl of operation is begin -- ((b and c) or (b nand c)) = always true y <= a and ((b and c) or (b nand c)); end architecture;

## Final thoughts

Short-circuit evaluation isn't something I think about when coding VHDL. It was more so while I was working as a C++ programmer. Then I would always place the most complex part of the expression on the right-hand side to benefit from any potential short-circuiting.

When coding VHDL, my focus is on writing correct and readable code. And since this optimization only applies to simulations using **boolean** and **bit** types, I don't give it much thought.

However, it's good to know about it if you stumble on one of those strange bugs caused by the right-hand operand not calling a function because of short-circuit evaluation.