Olaoluwa Raji
Published © MIT

Altera FPGA Project 1: 4-digit decade counter

Design and implementation of a 4-digit decade counter with an Altera FPGA development board and a 7-segment display.

BeginnerFull instructions provided85
Altera FPGA Project 1: 4-digit decade counter

Things used in this project

Hardware components

Altera Cyclone IV FPGA Development Board
The vendor sells the USB cable and blaster to respectively power the board and program the FPGA.
×1

Software apps and online services

Intel Quartus Prime 20.1

Story

Read more

Schematics

RTL Schematic

FPGA Board Schematic

Code

seg_display.vhd

VHDL
Top-level design
--MIT License
--
--Copyright (c) 2024 Olaoluwa Raji
--
--Permission is hereby granted, free of charge, to any person obtaining a copy
--of this software and associated documentation files (the "Software"), to deal
--in the Software without restriction, including without limitation the rights
--to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
--copies of the Software, and to permit persons to whom the Software is
--furnished to do so, subject to the following conditions:
--
--The above copyright notice and this permission notice shall be included in all
--copies or substantial portions of the Software.
--
--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
--IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
--FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
--AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
--LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
--OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
--SOFTWARE.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
library work;

-- Project: 0000 to 9999 counter. 

-- Common anode 7 segment display.
-- Logic low:  Activates the LEDs.
-- Logic high: Deactivates the LEDs.
-- System clock (clk): 50MHz.
-- 10KHz (to drive the 4 counters for each digit).
-- 10,000 system clock cycles (counter, shift register, and ...
-- ROM index counter) to switch from one digit to another.

-- Asynchronous reset (active low).

entity seg_display is
   port(rst_n: in std_logic;
        clk:   in std_logic;
        seg:  out std_logic_vector(6 downto 0);
        sel:  out std_logic_vector(3 downto 0));
end seg_display;

architecture seg_display_rtl of seg_display is
   constant TIME_TO_SWITCH_DIGIT: integer := 9_999;
   type digit_type is array(0 to 3) of std_logic_vector(3 downto 0);
   ------------------------------------------------------------------
   signal digit:       digit_type;
   signal count:       unsigned(15 downto 0);
   signal shift_reg:   std_logic_vector(3 downto 0);
   signal digit_index: unsigned(2 downto 0);
begin   
   -- Enables time-based digit switching
   digit_switching_counter: process(rst_n,clk)
   begin
      if rst_n = '0' then
         count <= (others => '0');
      elsif rising_edge(clk) then
         if count = to_unsigned(TIME_TO_SWITCH_DIGIT,count'length) then 
            count <= (others => '0');
         else
            count <= count + 1;
         end if;
      end if;
   end process;
   
   -- Select a digit using the switching counter and shift register
   sel <= shift_reg;
   
   shift_register: process(rst_n,clk)
   begin
      if rst_n = '0' then
         shift_reg <= "1110"; -- select 'units' digit by default
      elsif rising_edge(clk) then
         if count = to_unsigned(TIME_TO_SWITCH_DIGIT,count'length) then
            shift_reg(3 downto 1) <= shift_reg(2 downto 0);
            shift_reg(0) <= shift_reg(3);
         end if;
      end if;
   end process;
   
   -- Counter to select which digit should be loaded into the ROM
   digit_index_counter: process(rst_n,clk)
   begin
      if rst_n = '0' then
         digit_index <= (others => '0');
      elsif rising_edge(clk) then
         if count = to_unsigned(TIME_TO_SWITCH_DIGIT,count'length) then
            if digit_index = to_unsigned(3,digit_index'length) then
               digit_index <= (others => '0');
            else
               digit_index <= digit_index + 1;
            end if;
         end if;
      end if;  
   end process;
   
   -- Counter for 'units' digit
   counter_1: entity work.seg_counter(seg_counter_rtl)
   generic map(PLACE_VALUE => 1)
   port map(rst_n          => rst_n, 
            clk            => clk, 
            digit_out      => digit(0));
   
   -- Counter for 'tens' digit          
   counter_2: entity work.seg_counter(seg_counter_rtl)
   generic map(PLACE_VALUE => 10)
   port map(rst_n          => rst_n, 
            clk            => clk, 
            digit_out      => digit(1));
   
   -- Counter for 'hundreds' digit         
   counter_3: entity work.seg_counter(seg_counter_rtl)
   generic map(PLACE_VALUE => 100)
   port map(rst_n          => rst_n, 
            clk            => clk, 
            digit_out      => digit(2));
   
   -- Counter for 'thousands' digit     
   counter_4: entity work.seg_counter(seg_counter_rtl)
   generic map(PLACE_VALUE => 1000)
   port map(rst_n          => rst_n, 
            clk            => clk, 
            digit_out      => digit(3)); 
   
   -- Converts a digit's current value to signals to drive the display
   rom: entity work.seg_rom(seg_rom_rtl)
   port map(addr     => digit(to_integer(digit_index)), 
            data_out => seg);
   
end seg_display_rtl;

seg_counter.vhd

VHDL
--MIT License
--
--Copyright (c) 2024 Olaoluwa Raji
--
--Permission is hereby granted, free of charge, to any person obtaining a copy
--of this software and associated documentation files (the "Software"), to deal
--in the Software without restriction, including without limitation the rights
--to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
--copies of the Software, and to permit persons to whom the Software is
--furnished to do so, subject to the following conditions:
--
--The above copyright notice and this permission notice shall be included in all
--copies or substantial portions of the Software.
--
--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
--IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
--FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
--AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
--LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
--OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
--SOFTWARE.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

-- 4-bit generic counter for each digit or segment ...
-- of the 4-digit 7 segment display.

entity seg_counter is
   generic(PLACE_VALUE: integer := 1);
   
   port(rst_n:      in std_logic;
        clk:        in std_logic;
        digit_out: out std_logic_vector(3 downto 0));
end seg_counter;

architecture seg_counter_rtl of seg_counter is
   -- System clock: 50 MHz 
   -- 1 period of 10 kHz clock: 5000 cycles of system clock
   -- 10,000 periods of 10 kHz clock = 1 second
   -- Half cycle for 10 kHz: 2500
   -- Maximum clock cycles (10 kHz) for a digit = 10,000 x place value
   constant HALF_CYCLE: integer := 2499;
   constant DIGIT_CLKS: integer := (PLACE_VALUE * 10_000) - 1;
   ------------------------------------------------------------------
   signal cnt1:  integer range 0 to HALF_CYCLE;
   signal cnt2:  integer range 0 to DIGIT_CLKS;
   signal tick:  std_logic; 
   signal rise:  std_logic; -- Rising edge of 10 kHz clock
   signal digit: unsigned(3 downto 0);
begin
   clock_10kHz: process(rst_n,clk)
   begin
      if rst_n = '0' then
         cnt1 <=  0;
         tick <= '1';
      elsif rising_edge(clk) then
         if cnt1 = HALF_CYCLE then
            cnt1 <= 0;
            tick <= not tick;
         else
            cnt1 <= cnt1 + 1;
         end if;
      end if;
   end process;
   
   -- Rising edge detector
   rise <= '1' when tick = '1' and cnt1 = 0 else '0';
   
   clock_counter_10kHz: process(rst_n,clk)
   begin
      if rst_n = '0' then
         cnt2 <= 0;
      elsif rising_edge(clk) then
         if rise = '1' then
            if cnt2 = DIGIT_CLKS then
               cnt2 <= 0;
            else
               cnt2 <= cnt2 + 1;
            end if;
         end if;
      end if;
   end process;
   
   -- Buffered output
   digit_out <= std_logic_vector(digit);  
   
   digit_counter: process(rst_n,clk)
   begin
      if rst_n = '0' then
         digit <= (others => '0');     
      elsif rising_edge(clk) then
         if rise = '1' and cnt2 = DIGIT_CLKS then
            if digit = to_unsigned(9,digit'length) then
               digit <= (others => '0');
            else
               digit <= digit + 1;
            end if;        
         end if;
      end if;
   end process;
   
end seg_counter_rtl;

seg_rom.vhd

VHDL
--MIT License
--
--Copyright (c) 2024 Olaoluwa Raji
--
--Permission is hereby granted, free of charge, to any person obtaining a copy
--of this software and associated documentation files (the "Software"), to deal
--in the Software without restriction, including without limitation the rights
--to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
--copies of the Software, and to permit persons to whom the Software is
--furnished to do so, subject to the following conditions:
--
--The above copyright notice and this permission notice shall be included in all
--copies or substantial portions of the Software.
--
--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
--IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
--FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
--AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
--LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
--OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
--SOFTWARE.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

-- ROM: to map digits to signals to drive the 7 segment display.
-- g,f,e,d,c,b,a are the individual LED segments.
entity seg_rom is
  port(addr:      in std_logic_vector(3 downto 0);
       data_out: out std_logic_vector(6 downto 0));
end seg_rom;

architecture seg_rom_rtl of seg_rom is
   type rom_type is array(0 to 9) of std_logic_vector(6 downto 0);
   --------------------------------"gfedcba"
   constant ROM: rom_type := (0 => "1000000",
                              1 => "1111001",
                              2 => "0100100",
                              3 => "0110000",
                              4 => "0011001",
                              5 => "0010010",
                              6 => "0000010",
                              7 => "1111000",
                              8 => "0000000",
                              9 => "0010000");
begin
   data_out <= ROM(to_integer(unsigned(addr)));
end seg_rom_rtl;

Github file

Credits

Olaoluwa Raji
2 projects • 2 followers
Embedded system design

Comments