vhdl_workshop:lab_10

LAB 10: A State Machine for the Main Controller

A main control unit is needed to coordinate the actions of the different modules. The interface of the module is depicted in the next drawing:

The main controller interface

When the trigger button is pressed the shutter shall be opened and stay opened for the selected exposure time. This is done by setting the TIMER_GO signal one period to high, so the exposure controller opens the shutter for the selected exposure time. After the exposure time has passed the film has to be transported. So the motor needs a signal to start the transport Therefore MOTOR_GO is set to high for one period.

Now two things can appear: The motor has successfully transported the film, which is signalled by the MOTOR_READY signal. In this case a new photo can be made by the camera. It is the TRIGGER signal has to be examined again.

Or an error occurred while transporting the film. In this case the MOTOR_ERROR signal is set and the ERROR output signal has to be set, so the display indicate this case. When the cause of the error is removed (e.g. a new film is inserted) this has to be signalled by the user by pressing RESET or the trigger button once, but without taking a picture at the same time.

The current architecture will contain only one finite state machine. For that a combinational process, a clocked process and the concurrent output assignments are needed. In the clocked process the next state will be stored as the current state with every rising clock edge. Here the FlipFlops of the state machine are generated.

In the combinational process the next state will be evaluated in a case statement depending of the current state and the input values of TRIGGER, EXPOSE, MOTOR_READY and MOTOR_ERROR.

The output values are evaluated and assigned to the outputs in concurrent signal assignments. The expression in this assignment depend only on the current state. So a Moore machine has to be implemented!

The main controller deals purely with control signals, i.e. only std_ulogic is used for the entity ports. Additionally, an internal signal is necessary to store the current controller state. You should define your own enumeration type for this purpose which holds all possible states of the finite state machine.

  • Create the new VHDL file.
  • Write a testbench to verify the design. Use assertions to check the exposure times during the simulation run.
  • Compile and simulate the design.
  • Compare the number of Flip Flops that you would expect with the synthesis result.
MAIN_CTRL.VHD
library ieee;
use ieee.std_logic_1164.all;
 
entity MAIN_CTRL is
  -- system signals: CLK, RESET
  -- control signals: TRIGGER, EXPOSE, MOTOR_READY, MOTOR_ERROR
  -- output signals: ERROR, TIMER_GO, MOTOR_GO
end MAIN_CTRL;
 
architecture RTL of MAIN_CTRL is
  -- The state machine has to control the exposure process:
  -- While in IDLE state, the device waits for a trigger signal
  -- Then the exposure controller is started and the main controller
  -- has to wait for the end of the exposure. Please note, that the
  -- exposure controller needs 1 clock cycle to react on input signal
  -- changes!
  -- After the picture has been taken, the film transport must be
  -- initiated and the camera has to wait for the motor to finish
  -- If an error occurs, the controller shall enter a BROKEN state
  -- to prevent any further damage to the film
 
  -- Signals to store the state information:
  signal STATE      : T_STATE;
  signal NEXT_STATE : T_STATE;
 
begin
  -- process to calculate the next state
  begin
    -- Keep the old state as default action
    -- Check all transitions
    case STATE is
      when IDLE =>
      when TAKE_PIC =>
      when DELAY =>
      when WAIT_EXP_TIME =>
      -- Other states
      when others =>
        NULL;
    end case;
  end process;
 
  -- Clocked process to update the FSM registers
  begin
    if (RESET = '1') then
      -- Default system state
    elsif (CLK'event and CLK = '1') then
      -- Update of register values
    end if; -- end of clocked process
  end process;
 
  -- Concurrent statements to drive the output signals with STATE select
  ERROR <= '1' when BROKEN,
           '0' when others;
end RTL;
TB_MAIN_CTRL.VHD
library ieee;
use std.textio.all;
use ieee.std_logic_1164.all;
 
entity TB_MAIN_CTRL is
end TB_MAIN_CTRL;
 
architecture TEST of TB_MAIN_CTRL is
  constant PERIOD : time := 10 ns;
 
  component MAIN_CTRL
    port(CLK         : in std_ulogic;
         RESET       : in std_ulogic;
         TRIGGER     : in std_ulogic;
         EXPOSE      : in std_ulogic;
         MOTOR_READY : in std_ulogic;
         MOTOR_ERROR : in std_ulogic;
         ERROR       : out std_ulogic;
         TIMER_GO    : out std_ulogic;
         MOTOR_GO    : out std_ulogic);
  end component;
 
  signal W_CLK         : std_ulogic := '0';
  signal W_RESET       : std_ulogic;
  signal W_TRIGGER     : std_ulogic;
  signal W_EXPOSE      : std_ulogic;
  signal W_MOTOR_READY : std_ulogic;
  signal W_MOTOR_ERROR : std_ulogic;
  signal W_ERROR : std_ulogic;
  signal W_TIMER_GO : std_ulogic;
  signal W_MOTOR_GO : std_ulogic;
 
begin
  DUT : MAIN_CTRL
    port map(
      CLK         => W_CLK,
      RESET       => W_RESET,
      TRIGGER     => W_TRIGGER,
      EXPOSE      => W_EXPOSE,
      MOTOR_READY => W_MOTOR_READY,
      MOTOR_ERROR => W_MOTOR_ERROR,
      ERROR       => W_ERROR,
      TIMER_GO    => W_TIMER_GO,
      MOTOR_GO    => W_MOTOR_GO);
 
  W_CLK <= not W_CLK after PERIOD/2;
 
  STIMULI : process
  begin
    W_RESET       <= '1';
    W_TRIGGER     <= '1';
    W_MOTOR_READY <= '0';
    W_MOTOR_ERROR <= '1';
    wait for 3*PERIOD;
 
    W_RESET       <= '0';
    W_TRIGGER     <= '0';
    W_MOTOR_READY <= '0';
    W_MOTOR_ERROR <= '0';
    wait for 10*PERIOD;
 
    W_TRIGGER <= '1';
    wait for PERIOD;
    W_TRIGGER <= '0';
    wait for 20*PERIOD;
 
    W_TRIGGER <= '1';
    wait for PERIOD;
    W_TRIGGER <= '0';
    wait for 5*PERIOD;
 
    W_MOTOR_READY <= '1';
    wait for PERIOD;
    W_MOTOR_READY <= '0';
    wait for 5*PERIOD;
 
    W_TRIGGER <= '1';
    wait for 15*PERIOD;
 
    W_MOTOR_READY <= '1';
    wait for PERIOD;
    W_MOTOR_READY <= '0';
    wait for 15*PERIOD;
 
    W_MOTOR_READY <= '1';
    wait for PERIOD;
    W_MOTOR_READY <= '0';
    wait for 15*PERIOD;
 
    W_MOTOR_READY <= '1';
    wait for PERIOD;
    W_MOTOR_READY <= '0';
    wait for 15*PERIOD;
 
    W_MOTOR_ERROR <= '1';
    wait for PERIOD;
    W_MOTOR_ERROR <= '0';
    W_MOTOR_READY <= '1';
    wait for 15*PERIOD;
 
    assert false report "End of stimuli reached"
                 severity failure;
  end process STIMULI;
 
  SAMPLE : process(W_TRIGGER, W_EXPOSE, W_MOTOR_READY, W_MOTOR_ERROR,
                   W_ERROR, W_TIMER_GO, W_MOTOR_GO)
    constant SPACE     : string := " ";
    variable FILE_LINE : line;
    FILE     OUT_FILE  : text IS OUT "lab_10.trace";
  begin
    write(FILE_LINE, now);
    write(FILE_LINE, SPACE);
    write(FILE_LINE, to_bit(W_TRIGGER));
    write(FILE_LINE, to_bit(W_EXPOSE));
    write(FILE_LINE, to_bit(W_MOTOR_READY));
    write(FILE_LINE, to_bit(W_MOTOR_ERROR));
    write(FILE_LINE, SPACE);
    write(FILE_LINE, to_bit(W_ERROR));
    write(FILE_LINE, SPACE);
    write(FILE_LINE, to_bit(W_TIMER_GO));
    write(FILE_LINE, SPACE);
    write(FILE_LINE, to_bit(W_MOTOR_GO));
    writeline(OUT_FILE, FILE_LINE);
  end process SAMPLE;
 
  TIMER : process
  begin
    W_EXPOSE <= '0';
    wait until W_TIMER_GO'EVENT and W_TIMER_GO = '1' and W_RESET = '0';
 
    wait for PERIOD;
    W_EXPOSE <= '1';
    wait for 10*PERIOD;
    W_EXPOSE <= '0';
  end process TIMER;
end TEST;
 
configuration CFG_TB_MAIN_CTRL of TB_MAIN_CTRL is
  for TEST
  end for;
end CFG_TB_MAIN_CTRL;

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