VHDL tutorial
based on Xilinx Spartan 3 starter kit board

by Fabrice Derepas.
I do not know anything about hardware but it seems to be fun.
These few pages gives the opportunity for newbies (like me) to learn some stuff about VHDL and hardware design. This tutorial is based on the very affordable ($99) Spartan 3 starter kit board from Xilinx.
Example nameDevice used
FFFF7 segment led
AnnE7 segment led and clock
SquaresVGA port and clock
Ram testerSRAM
Bouncing ballVGA port, clock and SRAM

FFFF

The goal of this first step is to write "FFFF" on the seven-segment LED display.
This example is simple since the only thing is to do, is to send a constant signal to the right pins. Here is the code explained. First of all include standard declarations (do not think too much, just copy this at the beginning of each VHDL file):
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
Then declare an entity named ffff with only an output vector of 7 (6 down to 0) elements which are either '0' or '1':
entity ffff is
    Port ( LED : out std_logic_vector(6 downto 0));
end ffff;
Then describes the actions of this entity, in an architecture (named for instance Behavioral). Its job will be to write "0001110" (which represents the letter "F" on a LED) to the outgoing vector. Comments starts with the -- characters and ends with the end of line.
architecture Behavioral of ffff is

begin
  LED <= "0001110";   -- writes the 'F' pattern to the led.
end Behavioral;
The value "0001110" for letter comes from an arbitrary mapping I have chosen, shown on the schema below: first digit is for segment a, 2nd for f, 3rd for e, 4th for d, ... and 7th for g.
This arbitrary mapping is described in a separate file, You just have to assign elements of the LED vector to the right pins of the FPGA. I used the following mapping, in a file named for instance constraints.ucf:
NET "LED<0>"  LOC = "E14"  ;
NET "LED<1>"  LOC = "G13"  ;
NET "LED<2>"  LOC = "N15"  ;
NET "LED<3>"  LOC = "P15"  ;
NET "LED<4>"  LOC = "R16"  ;
NET "LED<5>"  LOC = "F13"  ;
NET "LED<6>"  LOC = "N16"  ;

AnnE

The goal of this exercise is to write "AnnE" on the seven-segment LED display.

This task is similar to the previous one. Since seven-segment LED displays are timed multiplexed, we will need "something" to tell us to write the first character, then something which tells to write the second and so on. This will be given by a clock. Here is our entity:

entity vhdlmodule is
    Port ( CLKIN : in std_logic;
           AN3 : inout std_logic;
           AN2 : inout std_logic;
           AN1 : inout std_logic;
           AN0 : inout std_logic;
           LED : out std_logic_vector(6 downto 0));
end vhdlmodule;
First of all we have an input signal which is a clock. Then we have the AN0, ... , AN3 signals which tell the board which of the four digit to light up. And then we have the LED signal as in previous example.

For the clock signal we use the 50Mhz clock provided on the board. Unfortunately this is too quick to switch beteen digits on the led panel. So we will be using a counter named CTR, each time the counter reaches 2^13 we change the digit to be displayed, an we reset the counter. This whole computation is put in something called a "process". Since this process will only be triggered by changes of CLKIN, process declaration is process(CLKIN). We say that the sensitivity list of this process is CLKIN.
architecture Behavioral of vhdlmodule is

signal CTR : STD_LOGIC_VECTOR(12 downto 0);
begin			
  Process (CLKIN)
  begin
    if CLKIN'event and CLKIN = '1' then
      if (CTR="0000000000000") then
        if (AN0='0') then 
          AN0 <= '1';	 
          LED <= "0101011";             -- the letter n
          AN1 <= '0';
        elsif (AN1='0') then 
          AN1 <= '1';	 	 
          LED <= "0101011";             -- the letter n
          AN2 <= '0';
        elsif (AN2='0') then 
          AN2 <= '1';	 
          LED <= "0001000";             -- the letter A
          AN3 <= '0';
        elsif (AN3='0') then 
          AN3 <= '1';
          LED <= "0000110";             -- the letter E
          AN0 <= '0';
        end if;
      end if;
      CTR<=CTR+"0000000000001";
      if (CTR > "1000000000000") then   -- counter reaches 2^13
        CTR<="0000000000000";
      end if;
    end if; -- CLK'event and CLK = '1' 
  End Process;
End Behavioral;
Here is the mapping of signals on the pins of the board:
NET "AN0"  LOC = "D14"  ;
NET "AN1"  LOC = "G14"  ;
NET "AN2"  LOC = "F14"  ;
NET "AN3"  LOC = "E13"  ;
NET "CLKIN"  LOC = "T9"  ;
NET "LED<0>"  LOC = "E14"  ;
NET "LED<1>"  LOC = "G13"  ;
NET "LED<2>"  LOC = "N15"  ;
NET "LED<3>"  LOC = "P15"  ;
NET "LED<4>"  LOC = "R16"  ;
NET "LED<5>"  LOC = "F13"  ;
NET "LED<6>"  LOC = "N16"  ;
Whole vhdl file

Squares

The goal of this exercise is to draw on a VGA screen squares with a side of 8 pixels as represented in the above picture (click to enlarge).

This exercise is very similar to the previous one. We need a time input to know when to display a pixel on the VGA screen. We will be using the 50Mhz clock clk50_in. We will write on the red, green and blue signal. We will have to provide vertical and horizontal sync information. So our entity declaration looks like:

entity clockmodule is
  port(clk50_in  : in std_logic;
       red_out   : out std_logic;
       green_out : out std_logic;
       blue_out  : out std_logic;
       hs_out    : out std_logic;
       vs_out    : out std_logic);
end clockmodule;
For the architecture part we will need three signals:
  • a 25Mhz clock, because the Spartan-3 Starter Kit Board User Guide gives VGA timing for a 25Mhz pixel frequency
  • an horizontal_counter to count the number of pixels per line
  • a vertical_counter to count the number of lines to dislay on the screen.
  • So the architecture declaration starts with:
    architecture Behavioral of clockmodule is
    
    signal clk25              : std_logic; -- the 25Mhz clock
    signal horizontal_counter : std_logic_vector (9 downto 0);
    signal vertical_counter   : std_logic_vector (9 downto 0);
    
    This time in the body of the architecture we will find two processes:
  • one to compute the clk25 signal, using clk50_in,
  • one to compute all out signals from clk25.
  • begin
    
    -- generate a 25Mhz clock
    process (clk50_in)
    begin
      if clk50_in'event and clk50_in='1' then
        if (clk25 = '0') then
          clk25 <= '1';
        else
          clk25 <= '0';
        end if;
      end if;
    end process;
    
    process (clk25) 
    begin
      if clk25'event and clk25 = '1' then
        if (horizontal_counter >= "0010010000" ) -- 144
        and (horizontal_counter < "1100010000" ) -- 784
        and (vertical_counter >= "0000100111" ) -- 39
        and (vertical_counter < "1000000111" ) -- 519
        then
          red_out <= horizontal_counter(3) 
    	            and vertical_counter(3);
          green_out <= horizontal_counter(4) 
    	            and vertical_counter(4);
          blue_out <= horizontal_counter(5) 
    	            and vertical_counter(5);
        else
          red_out <= '0';
          green_out <= '0';
          blue_out <= '0';
        end if;
        if (horizontal_counter > "0000000000" )
          and (horizontal_counter < "0001100001" ) -- 96+1
        then
          hs_out <= '0';
        else
          hs_out <= '1';
        end if;
        if (vertical_counter > "0000000000" )
          and (vertical_counter < "0000000011" ) -- 2+1
        then
          vs_out <= '0';
        else
          vs_out <= '1';
        end if;
        horizontal_counter <= horizontal_counter+"0000000001";
        if (horizontal_counter="1100100000") then
          vertical_counter <= vertical_counter+"0000000001";
          horizontal_counter <= "0000000000";
        end if;
        if (vertical_counter="1000001001") then		    
          vertical_counter <= "0000000000";
        end if;
      end if;
    end process;
    
    end Behavioral;
    
    Whole VHDL file

    Ram tester

    This exercice is just to test the SRAM. The idea is to display some values read in the ram on the leds.
    I read the ISSI 61LV25616AL Data sheet. I did not managed to get the data in 10ns but in only 20ns. I guess I missed something. If you have the correct way to do it just tell me :)
    The global clock used is the default 50 Mhz clock with a DLL. We use a single process which has 4 states given by the value of counter ctr:
  • 1: send read information on the SRAM, go to state 2,
  • 2: display read information on the led, go to state 3,
  • 3: send write information, go to state 4,
  • 4: erase values and go to state 1.

  • Vhdl Placement

    Bouncing ball

    Ok, now we would like to draw something on the screen which is read from the SRAM memory. This enables us more freedom, because we design separate blocks with a given role:
  • there will be a block named vga_ctrl in charge of displaying SRAM data on the VGA port.
  • the role of another block named graphic_gen will be to write in the SRAM the patterns we want to display.
  • Of course we will need a dedicated block to write to the memory, let us name it ram_ctrl, and clocks will be managed in a fourth block named clk_gen. This gives the following picture:
    The pattern written in the SRAM by the graphic_gen module will be a ball bouncing on the borders of the screen. To make it simpe the ball will be represented by a single pixel (in the style of the pong game).
    Back to my home page