VHDL coding tips and tricks: VHDL: How to use Packages in your design - with Example code!

Wednesday, March 3, 2010

VHDL: How to use Packages in your design - with Example code!

What is a Package?

A Package is a VHDL file, which can be used to contain user defined data types,constants, functions, procedures etc. A single package can be shared across many VHDL designs.

Uses of Packages:

1) To keep user defined functions and procedures in a common place:

Consider you are implementing a big project with lot of different VHDL designs connected with each other. Some of these modules might need code parts dealing with the same functionality. For example a Binary to BCD converter might be used in many modules.

Without a package file, you would have to copy and paste this function, in each and every module which is using it. But with the concept of packages, we just have to write the code once in a package file and then add just two lines in your modules to point towards the package contents.

2) To declare custom data types:

Some times, your design might have input or output ports which cannot be represented by a usual integer/std_logic_vector/unsigned type. In this case you can define the custom data type in the package and include the package name in the file.

For example, consider an 8 point FFT design with 8 complex inputs and 8 complex outputs. The entity port list would be too long if we don't have a custom data type for this. You might want to see this example to understand what I meant.

I will go into these points in a bit.

What is a Library?

A library is a collection of related packages. You might have not realized it already, but you have been already using packages and libraries in your designs.

The first two lines in most of your vhdl designs are normally this:

library ieee;
use ieee.std_logic_1164.all;

What are we doing here?

First we tell the compiler to use the library named ieee.
Then we tell the compiler to use the package named std_logic_1164 which is part of ieee library. This is how we normally use a package in a VHDL design.

For a custom written package, the compiler compiles the package into a default directory called work. Suppose we write a package called test_pkg, then to use it in your design we include the following two lines,

library work; use work.test_pkg.all;

How does a Package look like?

A package is declared in the following format:

package package_name is
     -- Declaration of
          -- types and subtypes
          -- subprograms
          -- constants, signals etc.
end package_name;


package body package_name is
     -- Definition of previously declared
        -- constants
        -- subprograms
     -- Declaration/definition of additional
        -- types and subtypes
        -- subprograms
        -- constants, signals and shared variables
end package_name;

An example Package: ( test_pkg.vhd ) 

--declare the libraries and packages which are used by this custom package
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

package test_pkg is --name of the package is "test_pkg"

--Define a data type called t1(totally 32 bits contains 3 different fields)
type t1 is  
    record
        a : unsigned(11 downto 0);  --12 bit field.
        b : unsigned(15 downto 0);  --16 bit field.
        c : unsigned(3 downto 0);   --4 bit field.
    end record;

--Declare a function named "add".
function xored (a2 : t1; b2: t1) return t1;

end test_pkg;   --end of package.

package body test_pkg is  --start of package body

--definition of function we declared above
--The function take two t1 data types and calculate the xor of each fields.
function  xored (a2 : t1; b2: t1) return t1 is
    variable temp : t1;
begin -- Just name the fields in order...
    temp.a:=a2.a xor b2.a;
    temp.b:=a2.b xor b2.b;
    temp.c:=a2.c xor b2.c;
    return temp;
end xored;
--end function

end test_pkg;  --end of the package body

In the above package, we declare a custom data type called t1 using a record. We also define a vhdl function named xored, which basically does xor operation between two of these newly declared data types.

To use this package in your vhdl design, save the above code in a file named test_pkg.vhd and add it to the current project and compile it. As mentioned earlier, all user packages are compiled to work directory by default.

An example on how to use the above package in your design: ( test.vhd )

--An example for a module using package..
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--note this line.The package is compiled to this directory by default.
--so don't forget to include this directory.
library work;
--this line also is a must. This includes the particular package into your program.
use work.test_pkg.all;

--Note, how we could use the custom data type in your entity as input and output.
entity test is
port (clk : in std_logic;
      a1 : in t1;
      b1 : in t1;
      c1: out t1
    );
end test;

architecture Behavioral of test is

begin

process(clk)
begin
    if(rising_edge(clk)) then
    --for doing xor operation at every positive edge of clock cycle.
    --Note how we used a custom function here.
        c1 <= xored(a1,b1);   
    end if;
end process;

end Behavioral;

Another advantage of using a package is that, by simply editing the data types or functions in the package body, you can alter the design specifications up to a level. For example, in the above design, changing the record type t1 wouldn't need any editing to be done in the test.vhd file at all. 

Packages help designers to modularize their designs more effectively.

3 comments:

  1. In the package outline you can define constants at the package declaration and also at the body (or declaration) part of the package.
    So, What is the difference between define a constant at the declaration of the package and define the same constant at the body of package?

    Thanks!

    ReplyDelete
  2. Function of test benches of vhdl

    ReplyDelete
  3. Error (12007): Top-level design entity "test_pkg" is undefined

    ReplyDelete