Logic Design - VHDL Basic Coding Structure

    Hello my friends, it's me Drifter Programming again. Today we will get into VHDL Coding by talking about some of the Basics. I will show you the structure/layout of all VHDL Programs, the most important libraries/packages, statements for assignments and processes. So, without further do let's get started!

Coding Structure:

    The structure of each VHDL programm is pretty simple. We first define/import the packages that the Circuit will use (more into those later on). Then we specify the Inputs and Outputs of our Circuit inside of the Entity. And lastly, we write down the Architecture of our Circuit that defines how the Outputs are being assigned using Assignment Statements (more into that later on) or Processes (also later). The Architecture also contains Component and Signal Definitions. Components are a way of including already made VHDL Circuits in form of Components. Signals are Cables that the Circuit contains and need also definition.

So, the structure of a VHDL program looks like this:

-- import packages (-- is used for single-linecomments)
library ieee;
use ieee.std_logic_1164.all;
-- entity definition
entity entity_name is port(
    input_name: in std_logic; -- 1 bit input
    output_name: out std_logic_vector(1 downto 0) -- 2 bit output
-- architecture definition
architecture arch_name of entity_name is 
-- component and signal definition (both are optional, but signals are always needed)
    begin -- after that point we have assignments
    -- statements and processes
end arch; -- closure of architecture
-- we can have multiple ones

    You don't have to understand everything now! Next time we will get into some Simple Circuit examples so that you understand this syntax even more.

Important Packages:

    Packages in VHDL work like Libraries that we already know in languages like C or Java (include and import). In VHDL they again contain functions, data types and constants the same way as in other Languages. The 2 main packages that we will use are the so called std_logic_1164 package and the numeric_std package. We have to always include the ieee library, cause the other 2 packages are "sub-packages" of this library. 

So, the way we "import" such a package looks like this:

library ieee; // import library ieee
use ieee.std_logic_1164.all; // package std_logic_1164
use ieee.numeric_std.all; // package numeric_std

And why do we need them? Well, let's get more in depth into each of those two.


    The most important thing that this package gives us is a new datatype called std_logic. A std_logic bit can get more then the 2 standard values (0 and 1). Those new values give us more informations about some stuff that goes on inside of our Circuit.

Let's talk about each specific value:

  • 'U' means Unitialized, so we know that this signal has no been set to a specific value yet (like 0 or 1)
  • 'X' means Unknown, so it's impossible to determine the value. This mostly occurs when we have an Output or somewhere inbetween sometimes that doesn't get assigned to a value through Gates etc.
  • 'Z' means High Impedance
  • 'W' means Weak signal, so we can't determine if that value is 0 or 1
  • 'L' means Weak signal, that probably is 0
  • 'H' means Weak signal, that probably is 1
  • '-' means Don't Care
  • '0' is the standard logic 0
  • '1' is the standard logic 1

    So, using all those new values we can check output accurancy, detect errors, remove signals that are not needed inside of our Circuit and so on...

    We can also use basic boolean operations like OR, AND etc. between std_logic bits, cause the standard VHDL operations get overridden.


    I hope that from the name only you can understand why we need this library. Well the answer is simple. We don't need only Boolean Operations, but also other Arithmetic operations like multiplications, divisions when using std_logic signals. But, this is not needed in each and every Circuit and so std_logic_1164 overrides only the basic boolean operations and numeric_std overrides all the others so that we can use those also inside of our Circuit. This package also contains unsigned and signed data types, and also shift, rotate and comparison operations.

Statements for Assignments:

    Let's now get into the Statements that we use for Assignments. I will show you the when-else and with-select Statements. I will get into how the syntax is and when we use them.


    When-else is called the conditional signal assignment. That means that we check the value of a whole boolean expression and use comparison conditions like equality to give a value to a signal depending on this expression. So, for each signal value we have to give a condition.

A when-else statement looks like this:

a <= "00" when (b = '0' and c = '0') else
     "01" when (b = '1' and c = '0') else
     "10" when (b = '0' and c = '1') else
     "11"; -- this means that the value will be "11" 
           -- for each other case (here we have only 1 case)

    You can see that the equality symbol is different to what you where used to. You can also see that the way we assign signals is using <=. So, we can do a <= "00"; directly and do assigning directly or use stuff like this when-else statement or the following with-select statement or even processes.



    With-select is the so called selected signal assignment. This means that we check the value of a specific signal to define the value of another signal. So, this time we don't use whole expressions, but only check the value of another signal to assign values to another signal.

So, with-select looks like this:

with a select b <=
   '0' when "00",
   '1' when "01",
   '1' when "10",
   '0' when "11";

    You can see that the value assigned to signal/output b is determined by each possible value signal/input a can get.


    Processes are another story. Those are used when we want to run Code Sequentially inside of a Circuit. Inside of processes we can write statements like if-else, (switch) case that we use in C or even wait for some time in things like Sequential Circuits. A process gets activated when something specific happens. This something is being put inside of the Sensitivity List of our Process. We mostly put all the Inputs of our Circuit and in Sequential Circuits we also have to put the Clock so that the Process gets activated when a clock event occurs and maybe reset or set signals too! The Sensitivity List inside of the Process defines the functionality of our Circuit. 

A Process is writen pretty easily and looks like this:

process (a, b) -- a, b are sensitivity list signals
    -- sequential statements like if/else, case, wait
end process;

    We mostly write if/else or case statement inside of the process that work similar to the with-select and when-else statement we talked about previously. But, using processes we can now control when the assignment will happen. Let's take a look into both of them.


    The logic of an if/else statement is exactly the same as in C or Java. We check a condition (if) and when this conditions is true something happens, that mostly means assignment of a specific value to a signal. So, an if/else is similar to an when-else.

A simple if/else statement looks like this:

if (a = '0' and b = '0') then
    c <= '1';
elsif (a = '1' and b = '0') then
    c <= '1';
    c <= '0';
end if;


    Something that you should take care off is the elsif. It's not a typo, but you really have to write elsif! You also can see by know that most statement close in some way. The if statement closes with an end if; as you can see.


    The case works similar to an switch case. So, an case works exactly like a with-select statement, but has some little tweaks. We again check the value of signal and depending on this values we do assignments of another signal.

A case statement looks like this:

case s(1 downto 0) is  
    when "00" =>  x <= "0001" 
    when "01" =>  x <= "0010"
    when "10" =>  x <= "0100" 
    when "11" =>  x <= "1000"
 end case;

    So, after all that stuff I hope that you understood how we can assign Output Signal in many different ways. I don't expect of you that you can write down all that stuff by ourself directly, but that you have a small idea of why and when we use each of those statements. We will get more in depth next time when we write simple Circuits.

This is actually it! Hope you enjoyed this post and learned/remembered stuff about VHDL.

    Next time in VHDL we will write simple Basic Circuits using some of those different ways we talked about today. That way you will understand the implementation even better.

Until next time...Bye!

3 columns
2 columns
1 column