vhdl_workshop:lab_3

This is an old revision of the document!


LAB 3: A 7-Segment Display Driver

As the data has to appear on a 7-segment display it is not sufficient just to multiplex the incoming signals. The information also has to be transformed from a binary representation into a vector of seven driver signals, one per display element. In order to enhance the readability of the VHDL code, constants should be used to represent the various numbers, i.e.

constant SEG_8 : std_ulogic_vector(6 downto 0):= "1111111";

The package P_DISPLAY contains the definitions SEG_0 till SEG_9. They define which segment to drive for which number. The additional definition SEG_E can be used, if an error occurs and should be assigned to the number 10.

The new design shall be called DISP_DRV and provides basically the same functionality as the DISP_MUX module. The main difference is that the output signal is now called DISPLAY and its width is now seven bits in order to drive the 7-segment display. The former output port DISPLAY_PHOTO will be used as an internal signal from now on. This is necessary, because the input signals can not be multiplexed directly to the new output signal DISPLAY since they are of a different data type.

The following figure shows the block diagram of the new design.

TODO Bild The display driver

The DISP_MUX process is the same as in the previous exercise. Additionally, you have to write a decoder process which maps the integer values of DISP_PHOTO to their corresponding DISPLAY values.

A (large) VHDL design is usually developed by a team. Therefore it is reasonable to define common data types for common signals. This is done by defining one package which is then referenced by all designers. In our case the two signals NO_PICS and EXP_TIME shall be of type T_DIGITS. The output signal DISPLAY shall be of type T_DISPLAY. These data types are defined in the package P_DISPLAY, together with the constants for the 7-segment display.

  • Copy the previous design DISP_MUX to DSP_DRV.
  • Adapt the module interface (port names and data types) and add the internal DISP_PHOTO signal.
  • Add a new process to map the integer values to display driver signals.
  • Generate a testbench for the new design. It may be easiest to copy the old testbench and to adapt it to the new interface. The stimulus generation need not be changed as the functionality of the multiplexer has not been changed. It is error-prone to verify the decoder process via the waveform display. The DISPLAY_DIGIT procedure from the P_DISPLAY package allows to display the seven segments graphically in a text file (named vhdl/workshop/src/display.txt). Use the concurrent statement DISPLAY_DIGIT(DISPLAY) in your testbench to update the file contents whenever the DISPLAY signal changes.
  • Compile all source files (packages first!) and run the simulation.
  • Synthesize the design.
DISP_DRV.VHD
library ieee;
use ieee.std_logic_1164.all;
use work.P_DISPLAY.all;
 
entity DISP_DRV is
  port(ERROR     : in std_ulogic;
       SHOW_TIME : in std_ulogic;
       NO_PICS   : in T_DIGITS;
       EXP_TIME  : in T_DIGITS;
       DISPLAY   : out T_DISPLAY);
end DISP_DRV;
 
architecture RTL of DISP_DRV is
-- intermediate signals
begin
  DISP_MUX: process(NO_PICS, EXP_TIME)
  -- complete sensitivity list!!
  begin
    -- add ERROR condition
    if SHOW_TIME = '1' then
      -- output = exposure time
    else
      -- output = picture count
    end if;
  end process;
  end DISP_MUX;
 
  DECODER: process (DISP_PHOTO)
  begin
    case DISP_PHOTO is
      when 0      => DISPLAY <= SEG_0;
      when 1      => DISPLAY <= SEG_1;
      when 2      => DISPLAY <= SEG_2;
      when 3      => DISPLAY <= SEG_2;
      when 4      => DISPLAY <= SEG_4;
      when 5      => DISPLAY <= SEG_5;
      when 5      => DISPLAY <= SEG_6;
      when 7      => DISPLAY <= SEG_7;
      when 8      => DISPLAY <= SEG_8;
      when 9      => DISPLAY <= SEG_9;
      when others => DISPLAY <= SEG_E;
    end case;
  end process DECODE;
end RTL;
TB_DISP_DRV.VHD
library ieee;
use ieee.std_logic_1164.all;
use work.P_DISPLAY.all;
 
entity TB_DISP_DRV is
end TB_DISP_DRV;
 
architecture TEST of TB_DISP_DRV is
  component DISP_DRV
    port(ERROR : in std_ulogic;
         SHOW_TIME : in std_ulogic;
         NO_PICS : in T_DIGITS;
         EXP_TIME : in T_DIGITS;
         DISPLAY : out T_DISPLAY);
  end component;
 
  signal W_ERROR     : std_ulogic :='0';
  signal W_SHOW_TIME : std_ulogic :='0';
  signal W_NO_PICS   : T_DIGITS;
  signal W_EXP_TIME  : T_DIGITS;
  signal W_DISPLAY   : T_DISPLAY;
 
begin
  DUT : DISP_DRV
    port map(
      ERROR => W_ERROR,
      NO_PICS => W_NO_PICS,
      EXP_TIME => W_EXP_TIME,
      SHOW_TIME => W_SHOW_TIME,
      DISPLAY => W_DISPLAY);
 
  STIMULI : process
  begin
    -- DISPLAY = 0 (3F)
    wait for 30 ns;
 
    W_NO_PICS <= 0;
    W_EXP_TIME <= 5;
    -- no changes
    wait for 20 ns;
 
    for I in 1 to 10 loop
      W_NO_PICS <= I;
      -- DISPLAY = 1..E (03,6D,67,53,76,7E,23,7F,77,7C)
      wait for 20 ns;
    end loop;
 
    W_SHOW_TIME <= '1';
    -- DISPLAY = 5 (76)
    wait for 20 ns;
 
    W_EXP_TIME <= 6;
    W_NO_PICS <= 4;
    -- DISPLAY = 6 (7E)
    wait for 20 ns;
 
    W_SHOW_TIME <= '0';
    -- DISPLAY = 4 (53)
    wait for 20 ns;
 
    W_ERROR <= '1';
    -- DISPLAY = 10 (7C)
    wait for 20 ns;
 
    W_SHOW_TIME <= '1';
    -- no changes
    wait for 20 ns;
 
    W_ERROR <= '0';
    -- DISPLAY = 6 (7E)
    wait for 20 ns;
 
    W_SHOW_TIME <= '0';
    -- DISPLAY = 4 (53)
    wait for 20 ns;
 
    wait;
  end process STIMULI;
 
  DISPLAY_DIGIT(W_DISPLAY);
end TEST;
 
configuration CFG_TB_DISP_DRV of TB_DISP_DRV is
  for TEST
  end for;
end CFG_TB_DISP_DRV;

!! Only for LAB 3 !!

P_DISPLAY.VHD
library ieee;
use ieee.std_logic_1164.all;
 
package P_DISPLAY is
  subtype T_DIGITS  is integer range 0 to 10;
  subtype T_DISPLAY is 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
  -------------
  --
  --
  --   12345678
  --  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 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 segments
    for SEGMENT in 0 to 6 loop
      if DISPLAY(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)(T_DIGIT_ROW'range) :=
          DISP_MATRIX(ROW)(T_DIGIT_ROW'range) or
          DIGIT_DEF(SEGMENT)(ROW);
        end loop;
      end if;
    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