====== LAB 9: A BCD-Counter ======
===== Synopsis =====
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:
{{:vhdl_workshop:workshop_exposurecontroller.svg?nolink&550|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.
===== Implementation =====
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;
===== Testbench =====
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;
===== Package =====
{{page>.:package}}