vhdl_workshop:lab_8

LAB 8: A Timer

After a picture has been taken, the film is transported forward automatically. In order to detect malfunction, e.g. end of film or a torn film, a transport supervision module needs to be implemented. If the servo motor has not finished the film transport after 2 seconds, an error signal is generated.

The timeout function is generated with the help of a counter. As the clock frequency is 8192 Hz, 2 seconds correspond to a maximum value of 16383 (=14 bit). The CLK and RESET signals are needed for the Flip-Flops again.

The start of the film transport is signaled via the MOTOR_GO signal. It is set to ’1’ when the transportation begins. When the servo motor has finished, the MOTOR_READY signal is set to ’1’. An error is reported via the MOTOR_ERROR signal in the same way, i.e. it is set to ’1’ if the MOTOR_READY pulse has not occurred in time. The signals are active for the duration of one clock period, only, in order to make an edge detection dispensable.

The interface of the module is depicted in the next drawing:

The transport timeout supervision module

Usually, the reset strategy is defined for the complete design. As an asynchronous reset was used in the first clocked module, we have to use the same template for clocked processes as before.

The timeout counter is started whenever a ’1’ is detected on the MOTOR_GO signal. The counter is stopped either if the MOTOR_READY signal is set to ’1’ or if the counter reaches its maximum value. In the last case, the MOTOR_ERROR is set to ’1’ for one clock cycle.

The module processes and generates control signals, only. They are of type std_ulogic, as usual. The counter is most easily implemented by a counter variable of type integer.

  • Create a new VHDL file for the MOTOR_TIMER module.
  • Write a testbench to verify the design. The clock period is approx. 122.07 us, in case you want to use realistic time values for the stimuli generation.
  • Compile and simulate the design.
  • How many Flip Flops are generated? Synthesize the design and compare your answer with the synthesis result.
MOTOR_TIMER.VHD
library ieee;
use ieee.std_logic_1164.all;
 
entity MOTOR_TIMER is
  port(CLK         : in std_ulogic;
       RESET       : in std_ulogic;
       MOTOR_GO    : in std_ulogic;
       MOTOR_READY : in std_ulogic;
       MOTOR_ERROR : out std_ulogic);
end MOTOR_TIMER;
 
architecture RTL of MOTOR_TIMER is
begin
  -- Clocked process with asynchronous reset
  process
    -- Variables for timeout counter value and counter active flag
  begin
    if (RESET = '1') then
      -- Reset values for all registers
    elsif (CLK'event and CLK = '1') then
      if MOTOR_GO = '1' then
        -- Start timeout detector
      end if;
 
      if MOTOR_READY = '1' then
        -- Stop timeout detector
      end if;
 
      if COUNT = '1' then
        -- Counter with overflow detection
      end if; -- if COUNTING
    end if; -- end of clocked process
  end process;
end RTL;
TB_MOTOR_TIMER.VHD
library ieee;
use std.textio.all;
use ieee.std_logic_1164.all;
 
entity TB_MOTOR_TIMER is
end TB_MOTOR_TIMER;
 
architecture TEST of TB_MOTOR_TIMER is
  constant PERIOD : time := 1 sec/8192;
 
  component MOTOR_TIMER
    port(CLK         : in std_ulogic;
         RESET       : in std_ulogic;
         MOTOR_GO    : in std_ulogic;
         MOTOR_READY : in std_ulogic;
         MOTOR_ERROR : out std_ulogic);
  end component;
 
  signal W_CLK         : std_ulogic := '0';
  signal W_RESET       : std_ulogic;
  signal W_MOTOR_GO    : std_ulogic;
  signal W_MOTOR_READY : std_ulogic;
  signal W_MOTOR_ERROR : std_ulogic;
 
begin
  DUT : MOTOR_TIMER
    port map(
      CLK         => W_CLK,
      RESET       => W_RESET,
      MOTOR_GO    => W_MOTOR_GO,
      MOTOR_READY => W_MOTOR_READY,
      MOTOR_ERROR => W_MOTOR_ERROR);
 
  W_CLK <= not W_CLK after PERIOD/2;
 
  STIMULI : process
  begin
    W_RESET       <= '1';
    W_MOTOR_GO    <= '1';
    W_MOTOR_READY <= '0';
    -- MOTOR_ERROR: '0'
    wait for 3*PERIOD;
 
    W_RESET    <= '0';
    W_MOTOR_GO <= '0';
    -- no changes
    wait for 2.1 sec;
 
    W_MOTOR_GO <= '1';
    wait for PERIOD;
    W_MOTOR_GO <= '0';
    -- MOTOR_ERROR -> '1' -> '0'
    wait for 2.1 sec;
 
    W_MOTOR_GO    <= '1';
    wait for PERIOD;
    W_MOTOR_GO    <= '0';
    wait for 1.9 sec;
    W_MOTOR_READY <= '1';
    wait for PERIOD;
    W_MOTOR_READY <= '0';
    -- MOTOR_ERROR remains '0';
    wait for 2.1 sec;
 
    assert false report "End of stimuli reached"
                 severity failure;
  end process STIMULI;
 
  SAMPLE : process(W_MOTOR_GO, W_MOTOR_ERROR)
    constant SPACE     : string := " ";
    variable FILE_LINE : line;
    FILE     OUT_FILE  : text IS OUT "lab_8.trace";
  begin
    write(FILE_LINE, now);
    write(FILE_LINE, SPACE);
    write(FILE_LINE, to_bit(W_MOTOR_GO));
    write(FILE_LINE, SPACE);
    write(FILE_LINE, to_bit(W_MOTOR_ERROR));
 
    writeline(OUT_FILE, FILE_LINE);
  end process SAMPLE;
end TEST;
 
configuration CFG_TB_MOTOR_TIMER of TB_MOTOR_TIMER is
  for TEST
  end for;
end CFG_TB_MOTOR_TIMER;

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