vhdl_workshop:lab_9

This is an old revision of the document!


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:

TODO Bild The exposure controller

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!

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.

  • 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;