VHDL coding tips and tricks: VHDL: Unveiling Component Instantiation and Port Mapping Techniques!

Monday, March 8, 2010

VHDL: Unveiling Component Instantiation and Port Mapping Techniques!

THIS ARTICLE WAS THOROUGHLY REWRITTEN ON 5th MAR 2024!

    Components are smaller blocks of digital circuits that implements a certain given functionality. They can be used as building blocks for larger designs. Each component can be thought of as a black box. We can perceive the surface texture and shape of the box, but we dont know what is inside or what or how things happens in there. This is known as abstraction. It simplifies the design process of large projects.

    Each component typically has its own interface, which includes input and output ports. or in some cases inout ports.  Instantiation refers to the process of creating an instance of a component within a larger VHDL design. The designer specifies how the ports of the component are connected to the signals or ports in the bigger design in which it is instantiated. This is known as port mapping

    Let's understand this with the help of a practical example. Imagine you need to build a full adder using two half adders. In this case, the full adder is called as top module and the half adder is called as submodule. Designs like this, where we implement a larger VHDL circuit using smaller sub-circuits, is called Hierarchical Modeling.

full adder using two half adders
    
    To implement the full adder using its half adder submodules, you first declare the submodules using component declarations. These declarations, which precede the begin keyword in the architecture statement, specify all submodules to be used within the top module.

architecture behavior of fulladder is
--sub-module(half adder) is declared as a component before the keyword "begin".
   component halfadder
    port(
         a : in std_logic;
         b : in std_logic;
         sum : out std_logic;
         carry : out std_logic
        );
    end component;

begin

    Next, you instantiate the half adders as many times as you need them. Each instance of a component should be assigned a unique name (HA1 and HA2 in the below example). These instantiations occur within the definition part of the architecture, following the begin keyword. 
    To integrate the component ports with the rest of the circuit, you utilize the port map keyword. This keyword links the names of architecture signals to be utilized in the submodule.

begin 
--the instantiations occur following the begin keyword.
--instantiate and do port mapping for the first half adder. HA1 : halfadder port map ( a => a, b => b, sum => s1, carry => c1 ); --instantiate and do port mapping for the second half adder. HA2 : halfadder port map ( a => s1, b => cin, sum => sum, carry => c2 );

    Thus concludes our exploration of components and their integration within VHDL designs. Now, let's delve into the various methods VHDL provides for implementing port mapping. By the end, I'll share my personal favorite approach.

The most straightforward method, as outlined earlier, is known simply as Component Instantiation.

1. Component Instantiation:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

--half adder entity
entity halfadder is
    port(a,b : in std_logic;
    sum,carry : out std_logic
    );
end halfadder;

architecture Gate_level of halfadder is

begin    
    sum <= a xor b;
    carry <= a and b;    
end architecture Gate_level;

----------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------

--top module(full adder) entity declaration
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity fulladder is
    port (a : in std_logic;
    b : in std_logic;
    cin : in std_logic;
    sum : out std_logic;
    carry : out std_logic
    );
end fulladder;
--top module architecture declaration.
architecture behavior of fulladder is
    --sub-module(half adder) is declared as a component before the keyword "begin".
    component halfadder
    port(
        a : in std_logic;
        b : in std_logic;
        sum : out std_logic;
        carry : out std_logic
    );
end component;
--All the signals are declared here,which are not a part of the top module.
--These are temporary signals like 'wire' in Verilog.
signal s1,c1,c2 : std_logic:='0';

begin
    --instantiate and do port map for the first half adder.
    HA1 : halfadder port map (
        a => a,
        b => b,
        sum => s1,
        carry => c1
    );
    --instantiate and do port map for the second half adder.
    --Note how I have changed the order of signals inside the port map.
    --This can be only be done when we use named association.
    HA2 : halfadder port map (
        b => cin,
        a => s1,
        carry => c2,
        sum => sum
    );
    carry <= c1 or c2;  --final carry calculation
    
end;

    What we did above is called named association. In named association, you specify the port name followed by the assignment operator (=>), followed by the actual signal, constant, or variable that you want to connect to that port. This allows for more clarity and reduces the likelihood of errors, especially in designs with numerous ports or when the order of ports might change over time. When using named association, the order in which signals are used within port map doesnt matter. 

Another method to do port mapping is called entity instantiation.

2. Entity Instantiation:

    One drawback of component instantiation is the requirement to duplicate your component declaration within your top modules, resulting in a surplus of repetitive code. This redundancy can be streamlined by employing the entity instantiation method. But how does it function?

    In VHDL, there is a special library called work that represents the current working library where your design files are compiled and stored during the simulation and synthesis process. When you compile your VHDL design files, the compiled design units are placed into the work library by default unless you explicitly specify a different library. 

    Entity instantiation capitalizes on this observation. How exactly? Let's examine the code rewritten using entity instantiation.

--top module(full adder) entity declaration
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity fulladder is
    port (a : in std_logic;
    b : in std_logic;
    cin : in std_logic;
    sum : out std_logic;
    carry : out std_logic
    );
end fulladder;
--top module architecture declaration.
architecture behavior of fulladder is

-- there is no need to declare any component here. Woah, less clutter!

--All the signals are declared here, which are not a part of the top module.
--These are temporary signals like 'wire' in Verilog.
signal s1,c1,c2 : std_logic:='0';

begin
    --instantiate and do port map for the first half adder.
    --'work' is a special folder. It is the default working library used by VHDL compilers.
    HA1 : entity work.halfadder port map (
        a => a,
        b => b,
        sum => s1,
        carry => c1
    );
    --instantiate and do port map for the second half adder.
    --Note how I have changed the order of signals inside the port map.
    --This can be only be done when we use named association.
    HA2 : entity work.halfadder port map (
        b => cin,
        a => s1,
        carry => c2,
        sum => sum
    );
    carry <= c1 or c2;  --final carry calculation
    
end;

    Immediately, you'll observe the significant reduction in clutter compared to the initial version of the code.

3. Positional association:

    We mentioned named association when doing port mapping for components. An alternative method is known as positional association. 

    Positional association relies on the order of the port declarations within the entity or component declaration. Here, signals in the top module are connected with ports of the component(signals in the submodule) based on their positional order in the entity declaration. 

    While positional association gets rid of some seemingly redundant looking code from the design, it comes at the cost of readability and increases the chances of missing errors. Experts recommend to use named association over positional association.

Here's how the code appears when utilizing positional association:

HA1 : entity work.halfadder port map (a,b,s1,c1);
HA2 : entity work.halfadder port map (s1,cin,sum,c2);

Note that, the order in which we write the signal names inside the brackets is very important here.

8 comments:

  1. If you have a big module and you are having many components then it is recommended to put these component definitions in a package file and call that package file in your port mapping file.This way you will have only instantiation stuff in top file.

    ReplyDelete
  2. @Ravindar : yeah..that's a nice way of handling components.. but I think entity instantiation is better than that method.If you are using entity instantiation then you need not even have component definitions in package.
    Read about entity instantiation here :
    http://vhdlguru.blogspot.com/2010/03/entity-instantiation-easy-way-of-port.html

    ReplyDelete
  3. "The order in which we write the signal names inside the brackets are important here.This method, if carefully written is a great way to reduce unnecassary length of the code."

    I disagree. I strongly believe this style should never be encouraged. Further, if you use a good text editor you can define code-folding points. this makes any large block of code the same as 1 line.

    The other reason to never use this style is because its easier to read and write code with the ports listed. Unless you are using notepad.

    ReplyDelete
  4. @Chris : You can disagree and use the coding style you like.But I am giving that option to coders.Let them choose the method themselves.

    ReplyDelete
  5. how to do cyclic pumping of 1024 point in FFT implementation on FPGA , genric code of vlsi for this

    ReplyDelete
  6. How to use component syntax if you want to use another architecture of halfadder?
    Sometimes I have different architectures of the same entity (sometimes slight different implementation for check how it works. By default ISE uses last one. Have you any clue without making new entity?

    ReplyDelete
  7. How it can be simulated if there is no code for half adder?

    ReplyDelete