Return to HDL info page. VHDL Coding Guidelines (last edit: 7. June 2022)
Introduction
This document was created to provide VHDL users with a guideline for producing fast, reliable and reusable HDL code.
Some of the main issues when writing VHDL code is:

Table of Contents

  1. Top-Down Design
  2. Signals and Variables
  3. Packages
  4. Xilinx-Specific Code
  5. Altera-Specific Code
  6. Coding for Synthesis

Top-Down Design

HDL coding should start with a top-down design approach. Use a top-level block diagram to communicate to designers the naming required for signals and hierarchical levels. Signal naming is especially important during the debug stage. Consistent naming of signals, from top to bottom, will ensure that project manager A can easily recognize the signals written by designer B.

Behavioral and Structural Code

When creating synthesizable code (RTL), you should write two types of code: behavioral RTL (leaf-level logic inference, sub-blocks) and structural code (blocks) -- each exclusively in its own architecture. A simple example of behavioral RTL versus structural code is shown in Figure 1 and Figure 2, respectively.


  entity mux2to1 is
      port (
        a : in std_logic_vector(1 downto 0);
        sel : in std_logic;
        muxed : out std_logic
      );
    end mux2to1;
  
    architecture rtl of mux2to1 is
    begin
      muxed <= a(1) when sel = '1' else a(0);
    end rtl;
    
Figure 1: Behavioral Code


  entity mux4to1 is
      port (
        input : in std_logic_vector(3 downto 0);
sel : in std_logic_vector(1 downto 0);
muxed : out std_logic ); end mux4to1; architecture structural of mux4to1 is signal muxed_mid : std_logic_vector(1 downto 0); component mux2to1 port ( a : in std_logic_vector(1 downto 0); sel : in std_logic; muxed : out std_logic ); end component; begin mux2to1_1_0: mux2to1 port map ( a => input(1 downto 0), sel => sel(0), muxed => muxed_mid(0) ); mux2to1_3_2: mux2to1 port map ( a => input(3 downto 2), sel => sel(0), muxed => muxed_mid(1) ); mux2to1_final: mux2to1 port map ( a => muxed_mid, sel => sel(1), muxed => muxed ); end structure;
Figure 2: Structural Code
Rules

Declarations, Instantiations, and Mappings

It is important to use a consistent, universal style for such things as entity declarations, component declarations, port mappings, functions, and procedures.
Rules
The combination of these two rules will help eliminate common coding mistakes. Therefore, this combination will greatly enhance the ease of debugging a design at every stage of verification. A simple example is shown Figure 3. Obeying these rules will also increase the readability, and therefore the reusability.

  architecture structural of mux4to1 is
      ...
    begin
      mux2to1_1_0: mux2to1
      port map (
        a     => input(1 downto 0),
        sel   => sel(0),
        muxed => muxed_mid(0)
      );
      ...
    end mux4to1;
    
Figure 3: One Line Per Signal / Named Association

Comments

Liberal comments are mandatory to maintain reusable code. Although VHDL is sometimes considered to be self-documenting code, it requires liberal comments to clarify intent, as any VHDL user can verify.
Rules
Three primary levels of commenting:

  ---------------------------------------------------------------------------------------------------  
  -- Author: John Q. Smith Copyright Xilinx, 2001
  -- Xilinx FPGA - VirtexII
  -- Begin Date: 1/10/01
  -- Revision History Date Author Comments
  -- 1/10/01 John Smith Created
  -- 1/14/01 John Smith changed entity port address & data to addr & dat
  ---------------------------------------------------------------------------------------------------
  --Purpose:
  -- This entity/architecture pair is a block level with 4 sub-blocks. This is the processor control
  -- interface for the block level . So on, and so forth...
  ---------------------------------------------------------------------------------------------------
    
Figure 4: Header Template


  ---------------------------------------------------------------------------------------------------  
  -- demux_proc: this process dumultiplexes the inputs and registers the
  -- demultiplexed signals
  ---------------------------------------------------------------------------------------------------

  demux_proc : process(clk, reset)
  begin ...
  
Figure 5: Process, Function, and Procedure Header


  ---------------------------------------------------------------------------------------------------  
  -- demux_proc: this process dumultiplexes the inputs and registers the
  -- demultiplexed signals
  ---------------------------------------------------------------------------------------------------

  demux_proc : process(clk, reset)
  begin ...
    if reset = '1' then
      demux <= (others => '0');
    elsif rising_edge(clk) then
    -- demultiplex input onto the signal demux
      case (sel) is
        when '0' =>
          demux(0) <= input;
        when '1' =>
          demux(1) <= input;
        when others =>
          demux <= (others => '0');
      end case;
    end if;
  end process;
  
Figure 6: Inline Comments

Indentation

Proper indentation ensures readability and reuse. Therefore, a consistent style is warranted. Many text editors are VHDL-aware, automatically indenting for "blocks" of code, providing consistent indentation. Emacs and CodeWright are two of the most common editors that have this capability.
Figure 7 shows an example of proper indentation. Proper indentation greatly simplifies reading the code. If it is easier to read, it is less likely that there will be coding mistakes by the designer.
Rules

  -- purpose: to show proper indentation
  sample_proc : process (clk, reset)
    variable muxed_data_v : std_logic_vector (1 downto 0); --_v denotes a variable  
  begin -- process sample_proc
    if reset = '0' then
      for i in data'range loop
        data(i) <= (others => '0'); -- data is a 4x2 array
      end loop; --i
      muxed_data <= '0'
    elsif clk'event and clk = '1' then
      muxed_data_v := data(conv_integer(addr));
      case sel is
        when '0' =>
          muxed_data <= mux_data_v(0);
        when '1' =>
          muxed_data <= mux_data_v(1);
      end case; -- case sel is
    end if; -- if reset = '0'...
  end process sample_proc; 
  
Figure 7: Proper Indentation


Naming Conventions

Naming conventions maintain a consistent style, which facilitates design reuse. If all designers use the same conventions, designer A can easily understand and use designer B's VHDL code.

Entities, Architectures, Procedures, and Functions

Rules
Architectures do not need unique names because they are individually bound to a specific entity that has a unique name. Names for architectures should be rtl, to indicate a leaf-level sub-block, and structural, to indicate a block with no leaf-level logic - with only sub-blocks.
For entities with more than one architecture, use "rtl_xilinx" (or "structural_xilinx") for a Xilinx-specific architecture and "rtl_asic" (or "structural_asic") for an ASIC-specific architecture.

Signal Naming Conventions

For a design implemented in VHDL, an up-front specification of signal naming conventions should help you reduce the amount of non-conformity. The primary motivating factor is enhanced readability during the verification of the design. General signal naming conventions are listed below.
General Signal Naming Guidelines
Rules
The following rules specify the suggested nomenclature for other widely used signals Use of in and out is very confusing in text, especially at hierarchical boundaries.
Therefore, the use of _in and _out should be strictly monitored. If they must be used, be sure that _in indicates input, and, likewise, that _out is an output to the correct level of hierarchy. Figure 8 shows an example entity and the instantiation of that entity in a higher block. Here, data_in is connected to data_out, making the code confusing.


  entity in_out is
      port (
        data_in : in std_logic_vector (31 downto 0);
        data_out : out std_logic_vector(31 downto 0)
      );
  
    end entity in_out;
  
    ...
  
    in_out_inst: in_out
    port map (
      data_in => ram_data_out,
      data_out => ram_data_in
    );
    
Figure 8: Confusing _in and _out suffixes

Use _i to denote local signal names that are internal representations of an output port. This nomenclature is used to easily identify the internal signal that will eventually be used as an output port.
The counter in Figure 9 provides a simple example of an output port that cannot be read. The output port count cannot be incremented because it would require count to be read. The problem is solved in the example by incrementing the local internal signal count_i. Some designers try to overcome this problem by using the port as an inout; however, not all synthesis compilers will allow this unless it is three-stated. Declaring the signal to be of type buffer is another common trap. This complicates the code because all signals to which it connects also must be of type buffer. Not all synthesis vendors support the data-type buffer. In addition, data-type buffer does not have all of the required defined functions to perform common arithmetic operations.


  count <= count_i;
  
  process (clk, reset)
  begin
    if reset = '1' then
      count_i <= (others => '0');
    elsif rising_edge(clk) then
      count_i <= count_i + 1;
    end if;
  end process;
  
Figure 9: Internal Signals Representing Output Ports