Functions And Procedure
Functions:
In Verilog we have bitwise reduction operators like & or | that can directly reduce a multi-bit signal into a single bit by performing AND or OR across all bits. For example, if a signal is 8 bits wide, using &signal gives one output bit that is the AND of all 8 bits, and |signal gives one output bit that is the OR of all 8 bits. VHDL does not provide these reduction operators directly, so we often write a function to perform the same operation. The function takes the multi-bit input, executes the bitwise ANDing or ORing across the vector, and returns a single bit as the result. In hardware, this function will be synthesized into gates. The advantage of writing such a function is that we can reuse it in many places without having to repeat the same piece of code again and again, just like we do in C programming. So, a function in VHDL is essentially a reusable piece of code that returns one value, which makes designs cleaner and avoids repetition.
Example: 8 bit Parity Detector
VDHL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity parity_detector_8 is
port (
A : in STD_LOGIC_VECTOR(7 downto 0);
Parity_odd: out STD_LOGIC; -- '1' when number of ones in A is odd
Parity_even: out STD_LOGIC -- '1' when number of ones in A is even
);
end entity;
architecture rtl of parity_detector_8 is
-- Function declared inside architecture (no package)
function reduce_xor(vec : STD_LOGIC_VECTOR) return STD_LOGIC is
variable r : STD_LOGIC := '0';
begin
for i in vec'range loop
-- xor accumulative reduction
r := r xor vec(i);
end loop;
return r; -- returns '1' if odd number of '1's else '0'
end function;
begin
-- Call the function: reduce_xor returns odd parity
Parity_odd <= reduce_xor(A);
Parity_even <= not reduce_xor(A);
end architecture;
Elaborated Diagram:

Synthesis:

Simulation:

Here we can see an example of a parity detector, which is a simple form of error detection circuit. Parity checking is widely used in functional safety and is an important method in industry. There are other methods such as CRC, SECDED, and sniffers, but parity is a very general and basic circuit. In this example, we implemented both even and odd parity checks. Since parity checks may be required many times in a design, we wrote the logic as a function. Whenever the function is called, it executes according to its declaration and is synthesized into hardware. This way, the same function can be reused multiple times without rewriting the code.
Where Functions are declared?
There are two ways to declare a function in VHDL. The first way is to put the function inside a package. This makes it reusable, because any other design can use the package and call the function without writing it again.
VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
package reduction_pkg is
function reduce_and(vec : STD_LOGIC_VECTOR) return STD_LOGIC;
function reduce_or(vec : STD_LOGIC_VECTOR) return STD_LOGIC;
function reduce_xor(vec : STD_LOGIC_VECTOR) return STD_LOGIC;
end package;
package body reduction_pkg is
function reduce_and(vec : STD_LOGIC_VECTOR) return STD_LOGIC is
variable result : STD_LOGIC := '1';
begin
for i in vec'range loop
result := result and vec(i);
end loop;
return result;
end function;
function reduce_or(vec : STD_LOGIC_VECTOR) return STD_LOGIC is
variable result : STD_LOGIC := '0';
begin
for i in vec'range loop
result := result or vec(i);
end loop;
return result;
end function;
function reduce_xor(vec : STD_LOGIC_VECTOR) return STD_LOGIC is
variable result : STD_LOGIC := '0';
begin
for i in vec'range loop
result := result xor vec(i);
end loop;
return result;
end function;
end package body;
Package used in Design main file:
VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use work.reduction_pkg.all; -- import package
entity reduction_example is
port (
A : in STD_LOGIC_VECTOR(7 downto 0);
Y_and : out STD_LOGIC;
Y_or : out STD_LOGIC;
Y_xor : out STD_LOGIC
);
end entity;
architecture rtl of reduction_example is
begin
-- Function calls
Y_and <= reduce_and(A); -- equivalent to Verilog &A
Y_or <= reduce_or(A); -- equivalent to Verilog |A
Y_xor <= reduce_xor(A); -- equivalent to Verilog ^A
end architecture;
Elaborated Diagram:

Synthesis:

Simulation:

The second way is to declare the function inside the architecture of an entity. In this case, the function is only available for that entity and cannot be reused elsewhere. We have seen this in the above parity detector example.
​
Note: Function declaration will be at compilation time but synthesize and execution will be at Function calling time.
When functions can do the job, why procedures?
In VHDL, a function and a procedure look similar, but they are not used for the same purpose. A function always gives back one single value, just like solving a math problem and writing one final answer. Inside a function, the keyword return means “this is my final answer,” and that value is passed back to wherever the function was called.
​
A procedure is different. A procedure can produce multiple outputs at the same time, and those outputs are passed through its out or inout parameters. That’s why procedures are chosen when you need more than one result. Unlike a function, a procedure does not send a value back with the return keyword. Instead, the outputs are already given through the parameters.
​
So what does return mean inside a procedure? In this case, return simply means “stop the execution here and exit the procedure.” It does not give back a value. If you write return; in the middle of a procedure, any code written after that line will be skipped, and control will move back outside the procedure.
​
Think of it like this:
-
A function is like an answer sheet where you must write exactly one final result before submitting.
-
A procedure is like a teacher explaining to many students at once, giving different answers to each. If the teacher suddenly says “class dismissed” (that’s the return;), the lesson stops right there, but the answers already given to the students remain with them.
VHDL
library ieee;
use ieee.std_logic_1164.all;
entity proc_example is
port(
data : in std_logic_vector(7 downto 0);
high4 : out std_logic_vector(3 downto 0);
low4 : out std_logic_vector(3 downto 0)
);
end entity;
architecture rtl of proc_example is
procedure split_nibbles (
signal x : in std_logic_vector(7 downto 0);
signal upper : out std_logic_vector(3 downto 0);
signal lower : out std_logic_vector(3 downto 0)
) is
begin
-- If input is all zero, just exit early
if x = "00000000" then
upper <= "0000";
lower <= "0000";
return; -- stop procedure here, skip rest
end if;
-- Otherwise split into upper and lower 4 bits
upper <= x(7 downto 4);
lower <= x(3 downto 0);
end procedure;
begin
-- Procedure call
process(data)
begin
split_nibbles(data, high4, low4);
end process;
end rtl;
If data = "00000000", the procedure assigns both outputs to "0000" and then return; exits immediately. The rest of the body is skipped.
If data is not zero, the procedure continues and assigns the upper 4 bits and lower 4 bits normally.