Jump to content

Programmable Logic/VHDL Module Structure

From Wikibooks, open books for an open world

Module Structure Overview

[edit | edit source]

There are two methods of creating a model: top-down and bottom-up. A model can also be defined to varying levels of detail.

Being a modeling language, VHDL supplies a rich variety of constructs to fit the various methods and level of details used in modeling. Amongst these are: Entities, Architectures, Packages, and Libraries.

Signals

[edit | edit source]

Here signals are used for propagating the data through the different stages of the implementation. Sometimes these signals are used to instantiation of the components which we are using in our design. These behave like a road map for propagating the data.

Variables

[edit | edit source]

Syntax

[edit | edit source]
 variable variable_name : type;
 variable variable_name : type := initial_value;

Rules and Examples

[edit | edit source]
variable HEIGHT : integer := 8;
variable COND : boolean := true;
variable IN_STRING : string(1 to 80);
variable M,N : bit := '1';
variable I : integer range 0 to 3;

A variable may be given an explicit initial value when it is declared. If a variable is not given an explicit value, its default value will be the leftmost value ('left) of its declared type.

variable I : integer range 0 to 3;
-- initial value of I is 0
variable X : std_ulogic;
-- initial value of X is 'U'

Variables within subprograms (functions and procedures) are initialised each time the subprogram is called:

function PARITY (X : std_ulogic_vector)
                 return std_ulogic is
  variable TMP : std_ulogic := '0';
begin
    for J in X'range loop
        TMP := TMP xor X(J);
    end loop; --no need to initialise TMP
    return TMP;
end PARITY;

Variables in processes, except for "FOR LOOP" variables, receive their initial values at the start of the simulation time (time = 0 ns)

process (A)
    variable TMP : std_ulogic := '0';
begin
    TMP := '0';
    -- in this example we need to reset
    -- TMP to '0' each time the process 
    -- is activated
    for I in A'low to A'high loop
        TMP := TMP xor A(I);
    end loop;
    ODD <= TMP;
end process;

Generics

[edit | edit source]

SFC

Scalar Data Types

[edit | edit source]

Attributes

[edit | edit source]

Composite Data Types

[edit | edit source]

Attributes

[edit | edit source]

Entity

[edit | edit source]

An entity is used to describe the interface of the VHDL module to other modules. All signals entering or exiting a vhdl module must be declared in the entity declaration. An example of an entity describing the interface for a two input AND gate can be found below:

ENTITY and2 IS
    PORT(A : in std_logic;
         B : in std_logic;
         F : out std_logic
	);
END and2;

Inside the entity a port declaration can be found. There are the following kinds of ports:

Type Description
in Specifies a port that can be read from but not written to. A in port cannot be used on the left side of an assignment.
out Specifies a port that can be written to but not read from. A out port can only be used on the left side of an assignment
in/out Specifies a port that can be read and written to. An inout port is commonly used to describe tristate buses.
buffer Specifies a out port whose current value can be read from. The same functionality can be achieved by assigning to an internal signal in the VHDL code, reading from that signal when needed, and assigning the internal signal to the out port using a concurrent statement.

Architecture

[edit | edit source]

An architecture in VHDL describes how functionality of the module is implemented. Below is the architecture for a two input and gate:

ARCHITECTURE and2_rtl OF and2 IS
  -- Signal, component, type and constant declarations go here.
  ...
BEGIN
  -- Concurrent statements and processes go here
  F <= A and B;
  ...
END and2_rtl;

Types of Architecture Implementation

[edit | edit source]

An architecture can be implemented in different ways depending on its purpose.

Structural

[edit | edit source]

A structural implementation connects and instantiates other modules. It serves to organize and connect modules together. A strict structural implementation contains only other instantiated blocks wired together using port maps.

Behavioral

[edit | edit source]

A behavioral implementation describes how a modules should function using the full array of VHDL constructs available. Behaviorally designed modules are not necessarily synthesizable, but are useful for modeling and testing synthesizable modules.

Register Transfer

[edit | edit source]

A register transfer implementation describes the functionality of a module in terms of registers and the transformation of the data that flows between the registers. Register transfer implementations are commonly used to describe modules that are to be synthesized on actual devices.

Package

[edit | edit source]

A VHDL package is used to contain a group of reusable data types, functions, procedures, and other VHDL constructs. The basic syntax for a package is as follows:

PACKAGE package_name IS
...
END package_name;

PACKAGE BODY package_name IS
...
END package_name;

The package header is for the declaration of VHDL constructs. The package body is for their implementation. Not all constructs (such as data types) need an entry in the body. The body is commonly used for function and procedure implementation.

Libraries

[edit | edit source]

A library establishes a namespace for the modules to exist in. When compiled by a simulator or synthesis tool, every package and entity is compiled into a library. There is no way for a VHDL file to specify which library it is compiled into. This is determined by the tool used to compile the VHDL code. The default library that tools compile VHDL objects into is called work. This means the following statement is assumed when creating VHDL models:

library work;

VHDL models can use the library keyword to make libraries visible to a module and use objects from them. This is needed to include packages from other libraries and to directly instantiate entities from other libraries.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

The library keyword makes a library visible to a VHDL design. After the library is made visible, packages inside the library can be used. The next line states to make everything declared in the ieee.std_logic_1164 namespace declared in the current namespace. For example, this allows us to use the statement:

signal sQ: std_logic;

instead of

signal sQ: ieee.std_logic_1164.std_logic;

Instantiation of a VHDL Module

[edit | edit source]

There are two ways a VHDL modules can be instantiated. The original way, specified in VHDL '87, uses a component, port map, and corresponding entity declaration. The newer way, specified in VHDL '93, uses just the port map and corresponding entity declaration. Each one is useful under certain circumstances. For each method the following entity will be instantiated:

entity D_FF is
    port(
        clk : out std_logic;
        rst : out std_logic;
        D : in std_logic;
        Q : in std_logic
        );
end entity D_FF;

architecture rtl of D_FF is
  ...
begin
  ...
end architecture rtl;

With Components

[edit | edit source]
architecture rtl of uses_d_ff is
  ...
  component D_FF is
    port(
        clk : out std_logic;
        rst : out std_logic;
        D : in std_logic;
        Q : in std_logic
        );
  end component D_FF;
  ...
  signal sClk: std_logic;
  signal sRst: std_logic;
  signal sD:   std_logic;
  signal sQ:   std_logic;
  ...
begin
  u_D_FF: component D_FF
    port map(
        clk => sClk,
        rst => sRst,
        D   => sD,
        Q   => sQ
    );
  
  ...
end architecture rtl;

Without Components

[edit | edit source]

Not using a component shortens the syntax quite a bit. To instantiate D_FF above, the following code is used:

architecture rtl of uses_d_ff is
  ...
  signal sClk: std_logic;
  signal sRst: std_logic;
  signal sD:   std_logic;
  signal sQ:   std_logic;
  ...
begin
  u_D_FF: entity work.D_FF(rtl)
    port map(
        clk => sClk,
        rst => sRst,
        D   => sD,
        Q   => sQ
    );
  
  ...
end architecture rtl;

As long as the tools used support this syntax, not using a component is recommended because it improves code maintainability. When signals are added/removed and removed from entities, they only need to be added or removed from the entity declaration and the port map. Components are useful when what is being instantiated does not have a VHDL module. When instantiating Verilog designs in VHDL or using IP cores, this is sometimes necessary. This allows the VHDL compiler to create a "black box" when compiling the VHDL and connect the actual model to the black box later.