VHDL templates

If you are looking for well-written code to analyze, or coding examples, please check also our Free IP section where you can find the source code of many IPs, which are reasonably simple to understand while implementing all the concepts needed for designing complex functions.

We have dedicated this page to Code Templates, so beginners or more advanced designers can simply cut & paste from the snippets. There is enough code here to be used in 90% of VHDL code (both RTL and Simulation).

Beginners: don’t miss some Application Notes especially “Writing Test benches”.

Note : the code below is compatible with all (decent) synthesis tools (it does not use VHDL 2008 constructs).

RTL Libraries

Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

Simulation Libraries

  use STD.TEXTIO.all;
Library IEEE;
use IEEE.std_logic_TEXTIO.all;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

Resynchronization Flip-Flop

Ar <= A when rising_edge(Clk);

Synchronous Process Template

process (Rst,Clk) -- NOTHING ELSE in the sensitivity list !
-- variables (if any) go here
begin
if Rst='1' then
-- EVERY signal assigned below must be reset here !
<<<signals receive CONSTANT values at reset here >>>
elsif rising_edge(Clk) then
<<< Your code goes here (only)  >>>
end if;
end process;

Tri-state output

Qtri <= Dout when OE='1' else 'Z';

Multiplexer

Q <= A0 when Sel='0' else A1;

Direct Instantiation

Don’t use “components” !

QUAD_inst: {{Entity work.}}QUAD(RTL) port map (
Rst => Rst,
Clk => Clk,
A   => Ar,
B   => Br,
Cnt => Position,
Dir => open
);

Cannot read outputs

We assume you don’t use VHDL 2008 (for reasons found in our Coding Style Guide), so you will not be able to read an output signal.

The workaround is to declare and work on an internal signal which can be of a different type as below.

  signal iCount : unsigned(Count'range); -- unsigned better than SLV internally
Begin -- architecture
Count <= std_logic_vector(iCount); -- SLV required at the interface (ports)

7-Segments decimal Decoder

process(Data)  
begin
Seg <= (others => '-'); -- optimize for A..F with don't care
case Data is
when  x"0"  => Seg <= "1111110";
when  x"1"  => Seg <= "0110000";
when  x"2"  => Seg <= "1101101";
when  x"3"  => Seg <= "1111001";
when  x"4"  => Seg <= "0110011";
when  x"5"  => Seg <= "1011011";
when  x"6"  => Seg <= "1011111";
when  x"7"  => Seg <= "1110000";
when  x"8"  => Seg <= "1111111";
when  x"9"  => Seg <= "1111011";
when others => null; -- required to cover the 6551 remaining cases
end case;
end process;

Demux / binary to one-hot

Process (i)
begin
MyVect <= (others=>'0');
MyVect(i) <= '1';  -- i is an integer (range)
end process;

Optimal One-Hot to Binary

In VHDL, this represents a quite challenging problem if you want to infer an efficient hardware implementation.
In Verilog, the use of a synthesis pragma was common.
In SystemVerilog, the unique case statement is the answer.

If you are not too concerned by the amount of logic generated, you can use an intuitive solution (a case statement). The optimal solution is below.

-- OneHotDecoder.vhd
-- ----------------------------------------------------
-- Optimal One-Hot to Binary Decoder
-- ----------------------------------------------------
-- (c) ALSE / Bertrand CUZEAU - FPGA.fr
--
Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
-- ---------------------------
Entity OneHotDecoder is
-- ---------------------------
port( Din  : in  std_logic_vector(7 downto 0);
Dout : out std_logic_vector(2 downto 0) );
End entity OneHotDecoder;
-- --------------------------------------
Architecture RTL of OneHotDecoder is
-- --------------------------------------
Begin

process (Din)
variable R : unsigned (Dout'range);
begin
R := (others=>'0');
for i in Din'range loop
if Din(i)='1' then
R := R or to_unsigned(i,R'length);
end if;
end loop;
Dout <= std_logic_vector(R);
end process;

End architecture RTL;

Butterfly (bus order swap)

-- Note that both vectors start/end at index 0
Process (MyVect)
begin
for i in MyVect'range loop
SwappedVect(i) <= MyVect(MyVect'high-i); 
end loop;
end process;

Power On Reset (POR)

When there is no (reliable) external reset input, it is very recommended to generate a global and general Reset of the entire FPGA at start up. This is the function of the simple module below. The “magic vector” 1010 (decimal value 10) is too small in cases of FPGA families in which Flip-Flops come up in an indetermined state (like several Actel families). It is however suitable for SRam FPGAs.

The Reset output will have to be resynchronized to all the different clock domains, but it’s another story…

-- ------------------------------------
--  Power On Reset with NO reset input
-- ------------------------------------
-- Author : Bert Cuzeau - ALSE
-- 10 clock cycles reset pulse issued at start up
-- Suitable for SRam FPGAs
Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

-- ----------------------------------------
Entity POR is
-- ----------------------------------------
Port ( Clock  : in  std_logic;   -- clock input
Reset  : out std_logic ); -- reset output
end entity POR;
-- ----------------------------------------
Architecture RTL of POR is
-- ----------------------------------------
signal Cnt    : unsigned (3 downto 0) := (others=>'0'); -- init for simulation
signal iRst_n : std_logic := '0'; -- init for simulation

Begin

Reset  <= not iRst_n;

process (Clock)
begin
if rising_edge(Clock) then
if Cnt /= 10 then -- If counter hasn't reached this value then
Cnt <= Cnt + 1;           -- keep counting
iRst_n <= '0';            -- and force the Reset
else
iRst_n <= '1';            -- release the Reset
end if;
end if;
end process;
end architecture RTL;

Synchronous Edge Detector

Synchronous Edge Detector

The input D signal can be asynchronous.

-- ------------------------------------------
Architecture RTL of EdgeDet is
-- ------------------------------------------

signal Dr, Drr : std_logic;

Begin

Dr   <= D  when rising_edge(Clk);
Drr  <= Dr when rising_edge(Clk);
Rose <= Dr  and not Drr;
Fell <= Drr and not Dr;
Changed <= Dr xor Drr;

end architecture RTL;

Simple Memory Inference

Please refer to ALSE training course.

--  RTL (synthesizable) Synchronous Single Port Memory
-- ----------------------------------------------------------
-- (c) ALSE -  http://www.alse-fr.com
-- Author  : Bert Cuzeau
-- Contact : info@alse-fr.com
-- Note    : Uses unconstrained arrays in port

Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

-- ------------------------------------------
Entity RamTest is
-- ------------------------------------------
port ( Clk    : in  std_logic;
Din    : in  std_logic_vector;
Dout   : out std_logic_vector;
WE     : in  std_logic;
RdAddr : in  std_logic_vector;
WrAddr : in  std_logic_vector
);
end RamTest;

-- ------------------------------------------
Architecture RTL of RamTest is
-- ------------------------------------------

subtype Data_t is std_logic_vector (Dout'range);
type    Mem_t  is array (0 to 2**RdAddr'length-1) of Data_t;
signal  Mem : Mem_t;

Begin

process (Clk)
begin
if rising_edge(Clk) then
Dout <= Mem (to_integer(unsigned(RdAddr))); -- Synchronous Read
if (WE = '1') then
Mem (to_integer(unsigned(WrAddr))) <= Din;
end if;
end if;
end process;

end architecture RTL;

Inference vs Instantiation is an important topic which is explained in our Training courses. The code above is normally suitable to all synthesis tools and FPGAs (that embark embedded memory blocks).

While it is possible to infer true Dual-Port memories with modern synthesis tools, instantiation is usually more recommended.

Simple RTL assertions

assert
not (rising_edge(Clk) and FifoWR='1' and Fifo_Full='1')
report "Fifo Write Violation !!!"
severity failure;

Note : it is not a PSL assertion, it shows how to take advantage of the VHDL “assert” statement and it does not require any specific simulator or feature.
PSL Assertions are more versatile and powerful (especially for their temporal layer).

In the same section…

Back to top