Madhava Vemuri, Ph.D.
Very High Speed Integrated Circuit Hardware Description Language (VHDL) is a hardware description language, created in 1983, by US Department of Defense for describing Very High Speed Integrated Circuits (VHSIC). It is a standard language used to describe digital and mixed-signal systems such as field-programmable gate arrays (FPGA) and integrated circuits (ICs). VHDL was first induced into IEEE standard 1076 in 1987 with a latest revision in 2019, with set of keywords to write the specifications. Over the years VHDL evolved to meet the requirements of the Electrical engineers and latest update was made in the year 2008.
Any digital logic circuit can be divided into 2 sections. Entity declaration and Architecture block. The entity consists of the port information of the circuit declared inside the keyword port. The inputs and outputs signals are specified using the keyword in and out respectively. The functioning of the circuit is described under the begin keyword inside the architecture.
ENTITY example IS
PORT(
input1, input2 : IN STD_LOGIC;
input2 : IN STD_LOGIC_VECTOR(1 DOWNTO 0);
ouput1 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0)
); -- Notice in the final line of port there is no semicolon
END example;
ARCHITECTURE example_arch OF example IS
-- signal object deceleration here
BEGIN
-- Lines describing the functioning
-- of the entity "example"
END example_arch;
The keywords are reserved word which cannot be used for other naming conventions. The signals, IO ports, entity, architecture names, creating objects (signals, variables) etc. must follow naming conventions. Each name declared is an Identifier to that specific element. The alphanumerical characters are used in creating identifiers, for instance "input1", "input3". VHDL language is case-insensitive,
The following use cases are equivalent and would give the same outcome: input1, iNpUt1, INPUT1.
The underline character __ is significant, and uses cases like are not equivalent: input1,input__1
The identifier cannot start with digit or underline character, use cases like these are not allowed: 1input, _1input, _input1
The identifier can be of any length, but does not allow spaces in between the characters: input_addr_2_reg_is_2_bit_and_how_are_you_doing_2day_
Tips for proper programming style:
Using appropriate naming conventions helps to go with the flow of the specification. Consider the case for Full-Adder, compared to the case (a), case (b) gives a sense into the signal and their behavior
(a)
(b)
Proper indentation increases the readability of the code. It helps to differentiate multiple sections in the program and aligns the comments, signal names etc.
ENTITY example IS
-->PORT(
----->input1, input2 : IN STD_LOGIC;
----->input2 : IN STD_LOGIC_VECTOR(1 DOWNTO 0);
----->ouput1 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0)
); -- Notice in the final line of port there is no semicolon
-->END example;
ARCHITECTURE example_arch OF example IS
-->-- signal object decleration here
-->BEGIN
----->-- Lines describing the functioning
----->-- of the entity "example"
-->END example_arch;
Give appropriate comments to the program. Anything written after -- is treated as comment in VHDL.
A digital logic circuit can be described in multiple levels of abstraction inside an architecture block. It can modelled based on its Dataflow, Behavior and structure.
Consider the following example of 2 input AND gate logic
0
0
1
1
0
1
0
1
0
0
0
1
Let us look at each case of modelling Individually.
Data flow modelling involves creating objects and assigning them with combinational logic. The objects are created using the keyword constant, variable, signal. followed by the datatype scalar, vector, enumerated, real, integer, time etc under the architecture and before the begin statements. The objects can be initialized with a specific value using the assignment operator ( := ), For instance in datatype := value, object is created with that value at initial time (t=0). The datatypes are supported are bit, bit_vector, std_logic, std_logic_vector, boolean etc
VARIABLE counter : INTEGER := 5; -- Notice the integer value is assigned without the use of inverted commas
CONSTANT pico_sec : time := 1ps;
SIGNAL clock : STD_LOGIC;
SIGNAL address : STD_LOGIC_VECTOR( 5 downto 0);
SIGNAL check : BOOLEAN := true;
Following is a snippet with signal initialization.
ARCHITECTURE example_arch OF example IS
-- signal static initialization examples using STD_LOGIC
SIGNAL signal_scalar_example1 : STD_LOGIC;
SIGNAL signal_scalar_example2 : STD_LOGIC := '0';
-- signal vector initialization examples using STD_LOGIC_VECTORS
SIGNAL signal_vector_example1 : STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL signal_vector_example2 : STD_LOGIC_VECTOR(7 DOWNTO 0):= (others => '1');
SIGNAL signal_vector_example3 : STD_LOGIC_VECTOR(0 TO 7):= "00000001"; -- MSB value holds 1
SIGNAL signal_vector_example4 : STD_LOGIC_VECTOR(7 DOWNTO 1):= "0000001";
BEGIN
--Lines describing the functioning
-- of the entity "example"
END example_arch;
signal_scalar_example1 is a scalar signal which is not initialized with any value so it holds unknown 'U' value
signal_scalar_example2 is initialized with '0' bit. Notice value binary zero is passed inside the single inverted comma ('bit_value')
signal_vector_example1 is not initialized with any value, so it holds unknown value 'U' for all bits
signal_vector_example2 is initialized with keyword others, assigns the same value to all signals in the vector, in this case binary one.
signal_vector_example3 vector is initialized using TO instead as DOWNTO which helps to differentiate between endian representation. In this case MSB index is 0 and LSB index is 7. Notice value binary 1 is passed inside the double inverted comma ("bit_value")
signal_vector_example4 vector has MSB index as 7 and LSB index as 1
The signal assignment is done using assignment operator <= The LHS of the assignment operator has signal to be assigned and the RHS of the operator has value to be assigned. signal_example <= signal_value. Here are few examples using the assignment operator:
signal_scalar_example1 <= '1'; -- Logic value '1' is stored in signal_example1
signal_scalar_example2 <= input1 AND input2; -- AND output value is assigned in this instance
signal_vector_example2(0) <= scalar1; -- each individual signal in a vector can be accessed using the parentheses and index
signal_vector_example3 <= b"0000_1010"; -- The lengthy binary values can be separated using underline _ The underline is ignored by the compiler, initial 'b' indicates the type of the value , other types which can be used are (O) Octal and (X) Hexadecimal
The architecture for the 2 bit and gate can be modelled as below
ENTITY and2 IS
PORT(
in1, in2 : IN STD_LOGIC
); -- Notice in the final line of port there is no semicolon
END and2;
ARCHITECTURE and2_arch OF and2 IS
-- signal object decleration here
BEGIN
out1 <= in1 and in2;
END and2_arch;
Behavior modelling describes the behavior of the circuit using concurrent (parallel) statements inside the process block. The signals which are listed in the process are sensitive to the circuit behavior. For instance
PROCESS (in1, in2) -- signals described in the parentheses is called as sensitivity list
BEGIN
-- Statements in the following lines are always executed when there is
-- a change of values in the signals listed in the senstivity list
END PROCESS;
The process block It uses different conditional statements like IF-ELSE, CASE,WHEN etc to describe the signal and circuit behavior. Let us look at way alternate way to create AND2 gate using different conditional statements
IF-ELSE STATEMENT:
-- Architecture of the and_gate using if else statements
ARCHITECTURE and2_arch OF and2 IS
BEGIN
PROCESS (in1,in2)
BEGIN
IF (in1 = '1' AND in2 = '1') THEN out1 <= '1';
ELSIF (in1 = '0' AND in2 = '0') THEN out1 <= '0';
ELSIF (in1 = '0' AND in2 = '1') THEN out1 <= '0';
ELSIF (in1 = '1' AND in2 = '0') THEN out1 <= '0';
ELSE out1 <= 'X';
END if;
END PROCESS;
END and2_arch;
CASE STATEMENT:
-- Architecture of the and_gate using case statements
ARCHITECTURE and2_arch OF and2 IS
SIGNAL concat : STD_LOGIC_VECTOR(1 DOWNTO 0);
BEGIN
PROCESS (in1,in2,concat) -- case is sensitive to inputs as well as concat signal
BEGIN
concat <= in1 & in2; -- & operator concatenates 2 signals and is assigned to a vector
CASE concat IS
WHEN "00" => out1 <= '0';
WHEN "01" => out1 <= '0';
WHEN "10" => out1 <= '0';
WHEN "11" => out1 <= '1';
WHEN OTHERS => out1 <= 'X'; -- default case
END CASE;
END PROCESS;
END and2_arch;
WHEN-ELSE STATEMENT:
-- Architecture of the and_gate using when statements
ARCHITECTURE and2_arch OF and_gate IS
BEGIN -- process is not used for when using WHEN conditional statements
out1 <= '0' WHEN (in1 = '0' AND in2 = '0') ELSE
'0' WHEN (in1 = '0' AND in2 = '1') ELSE
'0' WHEN (in1 = '1' AND in2 = '0') ELSE
'1' WHEN (in1 = '1' AND in2 = '1') ELSE
'X'; -- default case
END and2_arch;
Structural Modelling involves reusing the digital logic circuits to create architectures for newer circuits. The previous logic circuits are instantiated using port maps, and signals are connected to the ports of the instantiated logic. These connections can be made implicitly or explicitly using the assignment operator => . Before a digital logic circuit is instantiated, it is first declared as component inside of the architecture before the begin statement. The signal order inside the components is used to make implicit signal object assignments. Consider the case for bitwise 4 bit AND logic, we reuse the and_gate logic to create the 4 bit AND circuit ("and4_gate")
IMPLICIT Instantiation:
ENTITY and4_gate IS
PORT( in1, in2: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
out1 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0));
END and4_gate;
--Architecture of the and4_gate using implicit signal assignments
ARCHITECTURE and4_arch OF and4_gate IS
COMPONENT and_gate IS
port(in1,in2: IN STD_LOGIC;
out1 : OUT STD_LOGIC);
END COMPONENT;
BEGIN
ins0 : and_gate PORT MAP(in1(0),in2(0),out1(0));
ins1 : and_gate PORT MAP(in1(1),in2(1),out1(1));
ins2 : and_gate PORT MAP(in1(2),in2(2),out1(2));
ins3 : and_gate PORT MAP(in1(3),in2(3),out1(3));
END and4_arch;
EXPLICIT Instantiation:
ENTITY and4_gate IS
PORT( A, B: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
O : OUT STD_LOGIC_VECTOR(3 DOWNTO 0));
END and4_gate;
--Architecture of the and4_gate using implicit signal assignments
ARCHITECTURE and4_arch OF and4_gate IS
COMPONENT and_gate IS
port(in1,in2: IN STD_LOGIC;
out1 : OUT STD_LOGIC);
END COMPONENT;
BEGIN
ins0 : and_gate PORT MAP(in1 => A(0), in2 => B(0), out1 => O(0));
ins1 : and_gate PORT MAP(in1 => A(1), in2 => B(1), out1 => O(1));
ins2 : and_gate PORT MAP(in1 => A(2), in2 => B(2), out1 => O(2));
ins3 : and_gate PORT MAP(in1 => A(3), in2 => B(3), out1 => O(3));
END and4_arch;