VHDL coding tips and tricks: Digital Clock (With ability to Set time) And Testbench in VHDL

Sunday, April 3, 2022

Digital Clock (With ability to Set time) And Testbench in VHDL

    More than a decade back I had written a Digital Clock module in this blog, which was when I just started learning VHDL. Obviously it had its own shortcomings and through this post, I wanted to rectify these shortcomings. Plus add the ability to set time.

The codes are shared below. They are commented to help you understand the logic. For a detailed understanding watch the video. Also please make sure to like the video if it was helpful and subscribe to my YouTube channel for more such videos in the future. 




Digital Clock:

--Declare the libraries
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;  --we need this one because we are using "unsigned" data type.


entity digital_clock is
 --frequency of the clock passed as a generic parameter.
generic( CLOCK_FREQ : integer := 50000000	); 
port(	
		Clock : in std_logic;  --system clock
		reset : in std_logic;  --resets the time
		inc_secs : in std_logic;  --set a pulse here to increment the seconds by 1.
		inc_mins : in std_logic;  --set a pulse here to increment the minutes by 1.
		inc_hrs : in std_logic;  --set a pulse here to increment the hours by 1.
		seconds :out unsigned(5 downto 0);  --seconds output
		minutes :out unsigned(5 downto 0);  --minutes output
		hours :out unsigned(4 downto 0)  --hours output
		);
end digital_clock;

architecture Behavioral of digital_clock is

--temperory signals as we cant directly perform arithmetic operations on outputs
signal secs, mins, hrs : integer := 0;
--counter used for getting the 1 sec duration from the system Clock.
signal counter : integer := 0; begin process(Clock, reset) begin if(reset = '1') then --reset the time. secs <= 0; mins <= 0; hrs <= 0; counter <= 0; elsif(rising_edge(Clock)) then --increment the seconds. also increment mins and hours if needed. if(inc_secs = '1') then if(secs = 59) then secs <= 0; if(mins = 59) then mins <= 0; if(hrs = 23) then hrs <= 0; else hrs <= hrs+1; end if; else mins <= mins+1; end if; else secs <= secs + 1; end if; --increment the minutes. also increment hours if needed. elsif(inc_mins = '1') then if(mins = 59) then mins <= 0; if(hrs = 23) then hrs <= 0; else hrs <= hrs+1; end if; else mins <= mins+1; end if; --increment the hours. elsif(inc_hrs = '1') then if(hrs = 23) then hrs <= 0; else hrs <= hrs+1; end if; end if; --regular operation of the clock if(counter = CLOCK_FREQ-1) then --counting CLOCK_FREQ times takes 1 second. counter <= 0; --check and change values of secs, mins and hours if(secs = 59) then secs <= 0; if(mins = 59) then mins <= 0; if(hrs = 23) then hrs <= 0; else hrs <= hrs+1; end if; else mins <= mins+1; end if; else secs <= secs + 1; end if; else counter <= counter+1; end if; end if; end process; --The internal integer signals are converted into unsigned format. --The size of the output unsigned signal is assigned via the 2nd parameter(5 or 6 bits)
seconds <= to_unsigned(secs, 6);
minutes <= to_unsigned(mins, 6);
hours <= to_unsigned(hrs, 5);

end Behavioral;

Testbench:


LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
--the below library is used for finishing the simulation after we are done. 
--Otherwise it will run continuously.
library std;
use std.env.finish;
 
ENTITY tb_digitalClock IS
END tb_digitalClock;
 
ARCHITECTURE behavior OF tb_digitalClock IS 
 
    -- Component Declaration for the Unit Under Test (UUT)
 
    COMPONENT digital_clock
    generic( CLOCK_FREQ : integer := 50000000	); 
    PORT(
         Clock : IN  std_logic;
         reset : IN  std_logic;
         inc_secs : IN  std_logic;
         inc_mins : IN  std_logic;
         inc_hrs : IN  std_logic;
         seconds : OUT  unsigned(5 downto 0);
         minutes : OUT  unsigned(5 downto 0);
         hours : OUT  unsigned(4 downto 0)
        );
    END COMPONENT;
    

   --Inputs
   signal Clock : std_logic := '0';
   signal reset : std_logic := '0';
   signal inc_secs : std_logic := '0';
   signal inc_mins : std_logic := '0';
   signal inc_hrs : std_logic := '0';

 	--Outputs
   signal seconds : unsigned(5 downto 0);
   signal minutes : unsigned(5 downto 0);
   signal hours : unsigned(4 downto 0);

   -- Clock period definitions
   constant Clock_period : time := 10 ns;
	
	--Clock frequency in Hz. Use a smaller value for testbench. 
  --When testing on board it need to be set as 50 million, 100 million etc.
	constant CLOCK_FREQ : integer := 10;
 
BEGIN
 
	-- Instantiate the Unit Under Test (UUT)
   uut: digital_clock 
		GENERIC MAP(CLOCK_FREQ => CLOCK_FREQ)
		PORT MAP (
          Clock => Clock,
          reset => reset,
          inc_secs => inc_secs,
          inc_mins => inc_mins,
          inc_hrs => inc_hrs,
          seconds => seconds,
          minutes => minutes,
          hours => hours
        );

   -- Clock process definitions
   Clock_process :process
   begin
		Clock <= '0';
		wait for Clock_period/2;
		Clock <= '1';
		wait for Clock_period/2;
   end process;
 

   -- Stimulus process
   stim_proc: process
   begin		
		reset <= '1';
      -- hold reset state for 100 ns.
      wait for 100 ns;	
		reset <= '0';
      wait for Clock_period*CLOCK_FREQ*60*60*25;  --run the clock for 25 hours
		
		--increment seconds
		inc_secs <= '1';	wait for Clock_period;
		inc_secs <= '0';	
    wait for Clock_period*CLOCK_FREQ*5;	--wait for 5 secs after incrementing seconds once.
		
		--increment seconds 60 times
		inc_secs <= '1';	wait for Clock_period*60;
		inc_secs <= '0';	
    wait for Clock_period*CLOCK_FREQ*5;	--wait for 5 secs after incrementing seconds.
		
		--increment minutes
		inc_mins <= '1';	wait for Clock_period;
		inc_mins <= '0';	
    wait for Clock_period*CLOCK_FREQ*5;	--wait for 5 secs after incrementing minutes once.
--increment minutes 60 times inc_mins <= '1'; wait for Clock_period*60; inc_mins <= '0';
    wait for Clock_period*CLOCK_FREQ*5;	--wait for 5 secs after incrementing minutes.
--increment hours inc_hrs <= '1'; wait for Clock_period; inc_hrs <= '0';
    wait for Clock_period*CLOCK_FREQ*5;	--wait for 5 secs after incrementing hours once.
--increment hours 25 times inc_hrs <= '1'; wait for Clock_period*25; inc_hrs <= '0';
    wait for Clock_period*CLOCK_FREQ*5;	--wait for 5 secs after incrementing hours.
--apply reset reset <= '1'; --wait for 100 Clock cycles and then finish the simulation. --with the current settings it will run around 9 ms of simualtion time. wait for Clock_period*100; finish; end process; END;


So what next?

    As an extension of this project I want to show you how you can convert the time into BCD format and then connect a BCD to 7 segment converter to it. This will be useful for those who want to try this clock on a real FPGA board. Look forward to it in the next post.

So until next time...

No comments:

Post a Comment