vhdl_workshop:lab_5

LAB 5: A Decoder

The camera has a different button for each exposure time, i.e. the data signals from the keypad are transferred via a 10-bit data bus. If a button is pressed, the corresponding wire is set to 1. The exposure times are calculated according to the following formula:

time(button) = 2^(-button), for button=0..9 (time in seconds).

The result (1/1 s, 1/2 s, 1/4 s, …, 1/512 s) shall be shown on the 7-segment display. Thus, another decoder is needed. We will not introduce any new VHDL concepts in this exercise but suggest to try out different implementations and to observe their impact on the synthesis result. The block diagram of the new module is rather simple.

The keypad decoder

As already mentioned, the functionality of this module is fairly simple. The denominator of the following calculation shall be coded in a suitable form for the 7-segment display.

2^(-button) = 1 / (2^(button) ) = 1/1, 1/2, 1/4, 1/8, ..., 1/512 (button = 0..9)

It is up to you to decide about the behaviour when more than one button is pressed. There are basically two options: Either the buttons have a certain priority, or the user input is ignored.

The input data is generated by an external keypad and consists of 10 wires, one per button. Thus a std_ulogic_vector is used for the KEYPAD signal. The decoded values shall be processed by the display logic, i.e. T_DIGITS is the most suitable data type.

  • Create new files for the decoder entity and its architectures if you want to try out different implementations. Remember that synthesis tools ignore the VHDL configuration and use the most recent architecture.
  • Generate a testbench for the new design and add stimuli that cover “illegal” user input (more than one button pressed simultaneously).
  • Compile the VHDL code (package→entity→architectures→configurations).
  • Simulate the configurations and look out for differences in the behaviour.
  • Synthesize the architectures and find out about their area and maximum delay from input to output.
DECODER_CASE.VHD
architecture RTL_CASE of DECODER is
begin
  process(KEYPAD)
  begin
    case KEYPAD is
      when "1000000000" => KEY <=;
      when "0100000000" => KEY <=;
      when "0010000000" => KEY <=;
      when "0001000000" => KEY <=;
      when "0000110000" => KEY <=;when "0000000001" => KEY <=;
    end case;
  end process;
end RTL_CASE;
DECODER_IF.VHD
architecture RTL_IF of DECODER is
begin architecture
  process(KEYPAD)
  begin process
    if KEYPAD(0) = '1' then KEY <=;
    elsif KEYPAD(1) = '1' then KEY <=;
    elsif KEYPAD(2) = '1' then KEY <=;
    elsif KEYPAD(3) = '1' then KEY <=;
    elsif KEYPAD(4) = '1' then KEY <=;elsif KEYPAD(9) = '1' then KEY <=;
    else KEY <=;
    end if;
  end process;
end RTL_IF;
DECODER.VHD
use work.P_DISPLAY.all;
 
entity DECODER is
  port (KEYPAD : in std_ulogic_vector(9 downto 0);
        KEY    : out T_DIGITS);
end DECODER;
TB_DECODER.VHD
library ieee;
use ieee.std_logic_1164.all;
use work.P_DISPLAY.all;
 
entity TB_DECODER is
end TB_DECODER;
 
architecture TEST of TB_DECODER is
  component DECODER
    port (KEYPAD : in std_ulogic_vector (9 downto 0);
          KEY    : out T_DIGITS);
  end component;
 
  signal W_KEYPAD : std_ulogic_vector (9 downto 0);
  signal W_KEY : T_DIGITS;
 
begin
  DUT : DECODER
    port map (KEYPAD => W_KEYPAD,
              KEY    => W_KEY);
 
  STIMULI : process
  begin
    W_KEYPAD <= "1000000000";
    -- KEY = (5,1,2) (this works in both architectures!!)
    wait for 20 ns;
 
    W_KEYPAD <= "1111100000";
    -- KEY = (0,3,2) (if)
    --       (0,0,0) (case)
    wait for 20 ns;
 
    W_KEYPAD <= "0000100000";
    -- KEY = (0,3,2) (both architectures)
    wait for 20 ns;
 
    for I in 9 downto 0 loop
      W_KEYPAD <= (others => '0');
      W_KEYPAD(I) <= '1';
      -- KEY = (5,1,2),(2,5,6),(1,2,8),(0,6,4),(0,3,2),
      --       (0,1,6),(0,0,8),(0,0,4),(0,0,2),(0,0,1)
      --       (both architectures)
      wait for 20 ns;
    end loop;
    wait;
  end process STIMULI;
end TEST;
 
configuration CFG_TB_DECODER of TB_DECODER is
  for TEST
  end for;
end CFG_TB_DECODER;

LAB 4, 5, 6, 7, 8, 9, 10, 11

P_DISPLAY.VHD
library ieee;
use ieee.std_logic_1164.all;
 
package P_DISPLAY is
  type T_DIGITS  is array (2 downto 0) of integer range 0 to 10;
  type T_DISPLAY is array (2 downto 0) of std_ulogic_vector(6 downto 0);
 
  constant SEG_0 : std_ulogic_vector(6 downto 0):= "0111111";
  constant SEG_1 : std_ulogic_vector(6 downto 0):= "0000011";
  constant SEG_2 : std_ulogic_vector(6 downto 0):= "1101101";
  constant SEG_3 : std_ulogic_vector(6 downto 0):= "1100111";
  constant SEG_4 : std_ulogic_vector(6 downto 0):= "1010011";
  constant SEG_5 : std_ulogic_vector(6 downto 0):= "1110110";
  constant SEG_6 : std_ulogic_vector(6 downto 0):= "1111110";
  constant SEG_7 : std_ulogic_vector(6 downto 0):= "0100011";
  constant SEG_8 : std_ulogic_vector(6 downto 0):= "1111111";
  constant SEG_9 : std_ulogic_vector(6 downto 0):= "1110111";
  constant SEG_E : std_ulogic_vector(6 downto 0):= "1111100";
 
  -- pragma translate_off
  procedure DISPLAY_DIGIT(signal DISPLAY:T_DISPLAY);
  -- pragma translate_on
end P_DISPLAY;
 
-- pragma translate_off
use std.textio.all;
package body P_DISPLAY is
  ------------------------------
  --
  --            111111111112222
  --   123456789012345678901234
  --  1  #5##    ####    ####
  --  2 #    #  #    #  #    #
  --  3 4    0  #    #  #    #
  --  4 #    #  #    #  #    #
  --  5  #6##    ####    ####
  --  6 #    #  #    #  #    #
  --  7 3    1  #    #  #    #
  --  8 #    #  #    #  #    #
  --  9  #2##    ####    ####
  --
  ------------------------------
 
  constant ACTIVE_SEG: character := '#';
  constant EMPTY_SEG:  character := ' ';
 
  -- Width and height of a digit
  constant WIDTH:  integer := 8;
  constant HEIGHT: integer := 9;
 
  -- Datatypes to store the complete display in a matrix
  subtype T_MATRIX_ROW  is bit_vector (1 to 3*WIDTH);
  type    T_DISP_MATRIX is array (1 to HEIGHT) of T_MATRIX_ROW;
 
  -- Definition of the appearance of the 6 segments
  subtype T_DIGIT_ROW is bit_vector(1 to WIDTH);
  type    T_SEG_DEF   is array (1 to HEIGHT) of T_DIGIT_ROW;
  type    T_DIGIT_DEF is array (0 to 6) of T_SEG_DEF;
  constant DIGIT_DEF: T_DIGIT_DEF :=(("00000000", --   ....
                                      "00000010", --  .    #
                                      "00000010", --  .    0
                                      "00000010", --  .    #
                                      "00000000", --   ....
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000"),--   ....
 
                                     ("00000000", --   ....
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --   ....
                                      "00000010", --  .    #
                                      "00000010", --  .    1
                                      "00000010", --  .    #
                                      "00000000"),--   ....
 
                                     ("00000000", --   ....
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --   ....
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00111100"),--   #2##
 
                                     ("00000000", --   ....
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --   ....
                                      "01000000", --  #    .
                                      "01000000", --  3    .
                                      "01000000", --  #    .
                                      "00000000"),--   ....
 
                                     ("00000000", --   ....
                                      "01000000", --  #    .
                                      "01000000", --  4    .
                                      "01000000", --  #    .
                                      "00000000", --   ....
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000"),--   ....
 
                                     ("00111100", --   #5##
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --   ....
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000"),--   ....
 
                                     ("00000000", --   ....
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00111100", --   #6##
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000", --  .    .
                                      "00000000"));--  ....
 
  procedure DISPLAY_DIGIT(signal DISPLAY:T_DISPLAY) is
    file OUTFILE : TEXT is out "display.txt";
 
    variable L : line;
    variable DISP_TEXT: string(T_MATRIX_ROW'range);
 
    variable DISP_MATRIX: T_DISP_MATRIX;
    variable COL_L: integer;
    variable COL_R: integer;
 
  begin
    -- Clear the display
    DISP_MATRIX := (others => (others => '0'));
 
    -- Loop over all digits
    for DIGIT in T_DISPLAY'range loop
      -- Calculate the left- and rightmost columns
      COL_L := DIGIT*WIDTH + 1;
      COL_R := DIGIT*WIDTH + WIDTH;
 
      -- Loop over all segments
      for SEGMENT in 0 to 6 loop
        if DISPLAY(DIGIT)(SEGMENT) = '1' then
          -- Copy the matrix to the final display
          -- Loop over all rows of the display
          for ROW in T_SEG_DEF'range loop
            DISP_MATRIX(ROW)(COL_L to COL_R) :=
              DISP_MATRIX(ROW)(COL_L to COL_R) or
              DIGIT_DEF(SEGMENT)(ROW);
          end loop;
        end if;
      end loop;
    end loop;
 
    -- Write the matrix to a textfile
    for I in T_DISP_MATRIX'range loop
      -- Start with an empty line
      DISP_TEXT := (others => EMPTY_SEG);
 
      for J in T_MATRIX_ROW'range loop
        if DISP_MATRIX(I)(J) = '1' then
          DISP_TEXT(J) := ACTIVE_SEG;
        end if;
      end loop;
 
      -- Write the line
      write(L, DISP_TEXT);
      writeline(OUTFILE, L);
    end loop;
 
  end DISPLAY_DIGIT;
end P_DISPLAY;
-- pragma translate_on