LAB 9: A BCD-Counter

The desired exposure time and the number of pictures are stored in BCD (Binary Coded Decimal) format. This means that every decimal digit is coded by its binary value. Therefore, two times 4 bits are needed to represent the decimal number ’15’. If a binary representation was used, 4 bits would be enough.

While this representation is convenient for the display driver modules, it makes it a lot harder to do the actual counting. A procedure shall be designed that implements a single digit BCD adder with carry. A complete BCD counter is then generated by calling this procedure for each digit of the counter range.

The start of a new exposure is signaled via TIMER_GO. It is not necessary to detect a rising edge as this signal is active for the duration of one clock period, only. When the start signal arrives, the lens shutter has to be opened. The lens shutter remains open as long as the EXPOSE signal is set to ’1’. The picture count (NO_PICS) shall be incremented whenever a new picture is taken, i.e. whenever a rising edge of the EXPOSE signal is detected. Additional TIMER_GO signals during the actual exposure of the new picture have to be ignored.

The exposure timer is a bit tricky as only the display values (EXP_TIME) are available as input. It is therefore necessary to map the BCD-values to counter limits. Of course, it would be possible to use a single counter. Yet it is probably more comprehensible to split the task between two counters as all exposure times are a multiple of 1/512 second. At a clock frequency of 8192 Hz this equals 16 clock cycles. These timesteps will be counted by a second counter and will be compared with the counter limits from the mapper.

Please have a look at the module interface before you start to design:

The exposure controller

Behaviour

A procedure to increment a BCD digit is needed. The digit itself and a carry flag are needed as parameters for this purpose.

Based on this procedure a counter for the number of pictures is to be implemented. The value shall be incremented whenever a rising edge of the EXPOSE signal occurs.

A combinational process maps the exposure time to the equivalent number of 1/512 s timesteps.

The exposure timer shall be realized with another process and consists of two counters. The first counter shall generate the 1/512 s timebase, i.e. a pulse is to be generated every 16th clock cycle. This pulse is used to count the number of timesteps and the counter value will be compared with the limit that results from the selected exposure time.

It is good design practice in synchronous designs to keep the number of clock domains, i.e. the number of different clock signals for the Flip Flops as small as possible. This avoids problems during synthesis of the design. Therefore, the CLK signal shall be used for all registers!

Data types

The control signals CLK, RESET, TIMER_GO and EXPOSE shall be of type std_ulogic, as usual. The data signals EXP_TIME and NO_PICS are of the T_DIGITS type that is defined in the P_DISPLAY package. An integer variable may be used for the binary counter of the exposure timer.

To do

  • 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 again.
EXP_CTRL.VHD
library ieee;
use ieee.std_logic_1164.all;
use work.P_DISPLAY.all;
 
entity EXP_CTRL is
  -- system signals: CLK, RESET
  -- control signals: TIMER_GO, EXPOSE
  -- data signals: EXP_TIME, NO_PICS
end EXP_CTRL;
 
architecture RTL of EXP_CTRL is
  -- The EXP_TIME signal must be mapped to a limit for the
  -- exposure timer, i.e. an internal signal is needed
 
  procedure INC_DIGIT (
    DIGIT : inout integer;
    CARRY : inout std_ulogic) is
  begin
    if CARRY = '1' then
      if DIGIT /= 9 then
        DIGIT := DIGIT + 1;
        CARRY := '0';
      else
        DIGIT := 0;
      end if; -- OVERFLOW
    end if; -- CARRY = '1'
  end INC_DIGIT;
 
begin -- architecture
  MAPPER: process (EXP_TIME)
  begin
    -- default assignment for all outputs to avoid latches
    if EXP_TIME = (5,1,2) then
      LIMIT <= 0;
    elsif EXP_TIME = (2,5,6) then
      LIMIT <= 1;
      -- Map all other exposure times to the corresponding limit
    end if;
  end process MAPPER;
 
  EXP_TIMER: process (CLK, RESET)
    variable COUNT_16 : integer range ; -- Counter to generate 1/512s timesteps
    variable TIMER    : integer range ; -- Counter for the final exposure time
  begin
    if RESET = '1' then
      -- Reset all registers
    elsif (CLK'event and CLK = '1') then
      if EXPOSE = '1' then
        -- Exposure timer
        -- 2 cascaded counters: 1st -> 1/512s timesteps
        -- 2nd -> exposure time
      elsif TIMER_GO = '1' then
        -- Start exposure timer
      end if;
    end if;
  end process EXP_TIMER;
 
  PIC_COUNT: process(CLK, RESET)
    variable LAST_EXPOSE : std_ulogic;
    variable CARRY       : std_ulogic;
    variable DIGIT       : integer;
  begin
    if RESET = '1' then
      LAST_EXPOSE := '0';
      NO_PICS <= (0, 0, 0);
    elsif CLK'event and CLK = '1' then
      -- Check for rising edge of EXPOSE signal
        CARRY := '1';
        for I in T_DIGITS'low to T_DIGITS'high loop
          -- Increment the picture counter
        end loop;
      end if; -- Rising EXPOSE edge
    end if; -- Rising clock edge
  end process PIC_COUNT;
end RTL;
TB_EXP_CTRL.VHD
library ieee;
use ieee.std_logic_1164.all;
use work.P_DISPLAY.all;
 
entity TB_EXP_CTRL is
end TB_EXP_CTRL;
 
architecture TEST of TB_EXP_CTRL is
  constant PERIOD : time := 1 sec/8192;
  --constant PERIOD : time := 5 ns;
 
  component EXP_CTRL
    port(CLK      : in std_ulogic;
         RESET    : in std_ulogic;
         TIMER_GO : in std_ulogic;
         EXP_TIME : in T_DIGITS;
         EXPOSE   : buffer std_ulogic;
         NO_PICS  : buffer T_DIGITS);
  end component;
 
  signal W_CLK : std_ulogic := '0';
  signal W_RESET : std_ulogic;
  signal W_TIMER_GO : std_ulogic;
  signal W_EXP_TIME : T_DIGITS;
  signal W_EXPOSE : std_ulogic;
  signal W_NO_PICS : T_DIGITS;
 
begin
  DUT : EXP_CTRL
    port map(
      CLK      => W_CLK,
      RESET    => W_RESET,
      TIMER_GO => W_TIMER_GO,
      EXP_TIME => W_EXP_TIME,
      EXPOSE   => W_EXPOSE,
      NO_PICS  => W_NO_PICS);
 
  W_CLK <= not W_CLK after PERIOD/2;
 
  STIMULI : process
  begin
    W_RESET <= '1';
    W_TIMER_GO <= '1';
    W_EXP_TIME <= (0,6,4);
    -- EXPOSE: '0', NO_PICS: (0,0,0)
    wait for 3*PERIOD;
 
    W_RESET <= '0';
    W_TIMER_GO <= '0';
    -- no changes
    wait for 3*PERIOD;
 
    W_TIMER_GO <= '1';
    wait for 1*PERIOD;
    W_TIMER_GO <= '0';
    wait for 1*PERIOD;
    W_TIMER_GO <= '1';
    wait for 1*PERIOD;
    W_TIMER_GO <= '0';
    wait for 1*PERIOD;
    W_TIMER_GO <= '1';
    wait for 1*PERIOD;
    W_TIMER_GO <= '0';
    wait for 1 sec/64;
    -- EXPOSE: '1', NO_PICS: (0,0,1)
 
    wait for 10 * PERIOD;
    W_EXP_TIME <= (5,1,2);
 
    for I in 1 to 100 loop
      W_TIMER_GO <= '1';
      wait for 1*PERIOD;
      W_TIMER_GO <= '0';
      wait for 1 sec/512;
    end loop;
    -- NO_PICS: (1,0,1)
 
    wait;
  end process STIMULI;
end TEST;
 
configuration CFG_TB_EXP_CTRL of TB_EXP_CTRL is
  for TEST
  end for;
end CFG_TB_EXP_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