VHDL first contacts

Now we're going to see some samples in order to get familiar with VHDL language.

Entity

The entity keyword is used to define a VHDL module top view that defines the ports of our circuit. And the direction of each port. In VHDL you can only have 1 entity in the same file (This differs a little from Verilog)

entity Componente is
   Port ( i1 : in  STD_LOGIC;
          i2 : in  STD_LOGIC;
          i3 : in  STD_LOGIC;
          o  : out  STD_LOGIC
   );
   end Componente;

What we're defining here is some circuit with 3 input ports (i1,i2,i3) and 1 output port (o), all of these ports are of the STD_LOGIC type

In VHDL we can have these possible directions:

In VHDL we have these common types:

The most used datatype is the std_logic and std_logic_vector wich have these possible values.

Ports in a VHDL module are signals that provides access to the external world.

Another important point is that our circuit might have a port that is actually a "bus" signal, that is a multiple-bit port size, for example we set the input i1 to 6 bits in size.

entity Componente is
   Port ( a  : in  STD_LOGIC_VECTOR(5 downto 0);          
          o  : out  STD_LOGIC
   );
   end Componente;

Numbers Syntax

In VHDL numbers are represented without any special characters, what differs is the base representation, let's see first how we define bit's and bit arrays.

One important point is that you can represent a hexadecimal value using the "x" word, for example we can use this command to pass the value "FF" to some variable.

D <= x"FC";

Description Level

Like other description languages VHDL can be used in the following levels:

We're going to learn each of these types of description.

Hardware description of a simple combinational circuit

We're going to see how to describe a circuit in the lowest level in VHDL, using gate logic, like the verilog tutorial we're going to describe the following circuit.

library IEEE;
   use IEEE.STD_LOGIC_1164.ALL;
   use IEEE.STD_LOGIC_ARITH.ALL;
   use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity comb_sample is
   Port ( a : in  STD_LOGIC;
          b : in  STD_LOGIC;
          c : in  STD_LOGIC;
          y : out  STD_LOGIC);
   end comb_sample;
architecture Behavioral of comb_sample is
   begin
     -- Concurrent statement it will be executed every time that a signal on the 
     -- left side changes.
     y <= (a and b) or (b and c) or (a and c);
   end Behavioral;

Let's explain the first new keyword "architecture" this command indicates that we're going to start the implementation part of our circuit, after that we can clearly see how the output "y" port will "receive" a value from the logical expression of the circuit.

Observe that all line begining with a "--" will be a comment and will be ignored by the VHDL synthesizer(compiler).

Other new command is the "library" and "use" those commands are like the C "#include", they force the module to use some library containing constant, function or types declarations.

Another important point is the "<=" operator it indicates an assign operation to some signal or port, in this case the "y" port. All assign operations are concurrent, it means that no matter the order that they appear in our source code, they can execute, for that it needs only that some signal on the right part of the expression, change it's value.

Basically from this sample we can observe the basic file structure of a VHDL module

Bits concatenation

In VHDL we use the "&" operator to concatenate bits forming bit vectors. In the sample bellow we're going to assign a 16 bits port with a bunch of signals concatenated.

signal MYBUS : std_logic_vector (15 downto 0);
signal STATUS :std_logic_vector (2 downto 0);
signal RW, CS1, CS2 :std_logic;
signal  MDATA :std_logic_vector ( 0 to 9);
MYBUS <= STATUS & RW & CS1 & SC2 & MDATA;

This sample create a 16 bit signal formed by STATUS + RW + CS1 + SC2 + MDATA and assign to the MYBUS signal.

Other sample:

MYARRAY (15 downto 0) <= “111101111” & MDATA (2 to 9); 

This sample assign the bit vector formed by "111101111" and the MDATA bits from position 2 to 9 to the MYARRAY signal.

Concurrency

As we said earlier a different point in HDL languages is that things can run in parallel, for instance, every assing operation will be executed if any signal from the right side of the statement change it's value, no matter the order that it appears in the source code. In VHDL all assign operations are concurrent.

Structural description

In VHDL we can use different modules arranged in a hierarquical form, so in bigger projects you can build each module at time and then arrange all the project with sub modules. In the sample bellow we're going to create the same combinational circuit using structural description. In this sample I will include the description of each module.

First the and module

entity and_2 is
 Port ( A : in  STD_LOGIC;
        B : in  STD_LOGIC;
        C : out  STD_LOGIC);
 end and_2;
architecture Behavioral of and_2 is
begin
  C <= A and B;
end Behavioral;

Now the or module

entity or_3 is
 Port ( A : in  STD_LOGIC;
        B : in  STD_LOGIC;
        C : in  STD_LOGIC;
        D : out  STD_LOGIC);
 end or_3;
architecture Behavioral of or_3 is
begin
  D <= A or B or C;
end Behavioral;

Now the top module including all of the sub modules.

entity comb_struct is
 Port ( a : in  STD_LOGIC;
        b : in  STD_LOGIC;
        c : in  STD_LOGIC;
        y : out  STD_LOGIC);
 end comb_struct;
architecture Behavioral of comb_struct is
       -- Sub modules declaration
       component and_2
       port (a,b : in std_logic; 
             c : out std_logic);
       end component;
       component or_3
       port (a,b,c : in std_logic;
             d : out std_logic);
       end component;
       -- Signal declaration
       signal IM1, IM2, IM3 : std_logic;
       begin
       U0: and_2 port map (a, b,IM1);
       U1: and_2 port map (b, c,IM2);
       U2: and_2 port map (a, c,IM3);
       U3: or_3  port map (IM1,IM2,IM3,y);
end Behavioral;

In this code we see 2 new keywords, the "component" and the "port map". The "component" keyword used in the arqchitecture part is used to declare a new module that will be instantiated in the current module it's syntax is the same of entity. Now the "port map" is used to instantiate the module and make the proper connections between the current module and the instantiated module.

sub_mod_label: sub_module_name port map (ports)

Behavioral description

Now we will learn how to describe circuits using statements(if,while,for,case)looking more like a normal computer language like python or C, also the coding will be sequential, rather than paralel. In order to create behavior code we must use the "process" keyword. All code inside the process code can be considered to run in serial.

process_label: process (sensivity list)
begin

end process;

The sensivity list store all signals that will trigger the process, once the process is triggered, all commands between begin .. end will work serially. Behavior description can be used to model all kind of digital circuit (Combinational, Sequential), we're going to model the same combinational circuit using behavioral description.

entity behav_mod is
 Port ( a : in  STD_LOGIC;
        b : in  STD_LOGIC;
        c : in  STD_LOGIC;
        y : out  STD_LOGIC);
 end behav_mod;
architecture Behavioral of behav_mod is
begin
  behav : process (a,b,c)
  begin
    y <= (a and b) or (b and c) or (a and c);
  end process;
end Behavioral;

One important point to observe when modeling combinational circuit is to include all of it's inputs on the sensivity list, this is done to avoid the synthesizer to infer a memory element.

Other important feature of behavior description is to describe sequential circuits in this sample we're going to learn the most simple sequential circuit the flip-flop D.

entity mod_ffd is
   Port ( RESET : in  STD_LOGIC;
          CLK : in  STD_LOGIC;
          Q : out  STD_LOGIC;
          NQ : out  STD_LOGIC;
          D : in  STD_LOGIC);
   end mod_ffd;
architecture Behavioral of mod_ffd is
begin
   -- FlipFlop D sample (By the way that's a comment)
   process(RESET, CLK, D)
   begin
   if RESET = '1' then
     Q <= '0';
     NQ <= '1';
   elsif rising_edge(CLK) then
     Q <= D;
     NQ <= not D;
   end if;
   end process;
end Behavioral;  

One point to remember, always that one condition in the behavior code is not defined a memory element will be infered, in the previous example we have the rising_edge function that will return true when we have a rising edge pulse in the CLK signal, in this case nothing is defined when we got different edges, so in this case a memory element will be defined.

Generic command

The generic command is used to define generic parameters that can chage watheaver we instantiate in a top module, for example we're going to define a generic register of n bits and use it in some top module.

entity gen_reg is
  generic(n: natural := 16); -- 16 is default value
  Port ( 
    I : in  std_logic_vector(n-1 downto 0);
    CLK : in  std_logic;
    load : in  std_logic;
    clear : in  std_logic;
    O : out  std_logic_vector(n-1 downto 0)
  );
end gen_reg;
architecture Behavioral of gen_reg is
  -- Signal to hold register value.
  signal reg_o : std_logic_vector(n-1 downto 0);
begin
  register_behavioral : process (I,CLK,load,clear)
  begin
    if clear = '1' then
      reg_o <= (others => '0'); -- Turn zero all the bits of reg_o 
    elsif rising_edge(CLK) then
      if load = '1' then
        reg_o <= I;
      end if;
    end if;
  end process;
   
  -- Concurrent statement
  O <= reg_o;
end Behavioral;

The generic statement is used to create a parameter "n" that will define the size of the register, in order to use this generic register we must instatiate it in a top module.

entity top_mem_mod is
 Port ( CLK  : in  STD_LOGIC;
        I    : in  STD_LOGIC_VECTOR (15 downto 0);
        load : in  STD_LOGIC;
        O    : out  STD_LOGIC_VECTOR (15 downto 0));
 end top_mem_mod;
architecture Behavioral of top_mem_mod is
constant size_reg : natural := 16; 
begin
  U0 : entity WORK.gen_reg generic map (n => size_reg) port map(I,CLK,load,'0',O);
end Behavioral;

When using the "generic map" statement we can change the generic parameter before doing the port map. Other detail shown here is the instatiation without the component declaration.

Finite State machines

State machine is a general sequential circuit composed of (states, transitions and actions) The current state is based on the past states of the system and/or some input. A transition indicates a state change and is described by a condition that would need to be fulfilled to enable the transition. An action is a description of an activity that is to be performed at a given moment. For example if we are on state "opened" and the close_door condition is true we go to the "closed" state and the first action that we make is the "close door" action. There are two types of State machines

Mealy description

This will show how to describe a mealy state machine, first we will present the state machine diagram. This state machine will detect the 101 binary value.

entity mealy_test is
 Port ( clk : in  STD_LOGIC;
        x : in  STD_LOGIC;
        clear : in STD_LOGIC; 
        z : out  STD_LOGIC);
 end mealy_test;
architecture Behavioral of mealy_test is
   -- Define a new type
   type tStates is (reset, got1, got10);
   signal next_state, current_state : tStates;
   begin
   process (clk,x,clear)
   begin
     if clear = '1' then
       next_state <= reset;
     else
       if rising_edge(clk) then
         case next_state is 
           when reset =>
             if x = '1' then
               next_state <= got1;
             elsif x = '0' then
               next_state <= reset;
             end if;
   
           when got1 =>
             if x = '1' then
               next_state <= got1;
             elsif x = '0' then
               next_state <= got10;
             end if;
   
           when got10 =>
             if x = '1' then
               next_state <= got1;
             elsif x = '0' then
               next_state <= reset;
             end if; 
         end case;
       end if; 
     end if; 
   end process;
   
-- This will run in parallel, observe that the output depends on the input and the state
   z <= '1' when (next_state = got10 and x='1') else '0'; 
end Behavioral;

Moore machine description

A Moore has outputs dependent on the current state, a good example is a counter, in this sample we will show a simple 2 bit counter, wich response should look like this....