Skip to content

otaviocmaciel/7-segment-VHDL

Repository files navigation

4 digits 7-segment decoder VHDL

Description

The "4 digits 7-segment decoder VHDL" is a VHDL project designed to decode hexadecimal values and display them on 7-segment displays. Unlike a BCD (Binary-Coded Decimal) decoder (SN7447 IC for example), which only accepts numbers from 0 to 9, this decoder accepts values from 0 to F in hexadecimal. Thus, it can display numbers from 0000 to FFFF.

Features

  • Hexadecimal Decoding: Capable of decoding values from 0 to F and displaying them on 7-segment displays.
  • 4-Digit Display: Displays complete hexadecimal numbers on four 7-segment displays, allowing visualization from 0000 to FFFF.
  • Display Multiplexing: Implements a logic for multiplexing the displays, where each display stays lit for 2.5ms while the other three are off (50 MHz Crystal), providing a continuous and readable display.

Testbench

The testbench helps to visualize the simulated behavior of the implemented logic.

  • Clock Signal (clk): Corresponds to the clock generated by a 50MHz crystal.
  • Display Select Signal (sel_display): Corresponds to the display enable signal, where only one display remains active at a time. A logical level 0 activates the display since a PNP BJT is used for switching, as per the schematic diagram.
  • Digit Signals (A, B, C, D): Correspond to the segments lit, with each signal corresponding to a digit of a 4-digit number, where A is the least significant digit and D is the most significant digit, whether hexadecimal or decimal. A logical level 0 activates the segment, as the display is common anode.

image

Schematic

image

Implementation

Initially, the implementation was not well optimized, utilizing a larger number of if statements for display multiplexing. Below is an example of the first implementation that was clearly suboptimal.

Bad Implementation:

architecture hardware of decod_component is
type segmenton_array is array (0 to 15) of std_logic_vector(7 downto 0);
constant segmenton : segmenton_array := (
	"00000011", -- Zero
	"10011111", -- One
	"00100101", -- Two
	"00001101", -- Three
	"10011001", -- Four
	"01001001", -- Five
	"01000001", -- Six
	"00011111", -- Seven
	"00000001", -- Eight
	"00001001", -- Nine
	"00010001", -- A
	"11000001", -- B
	"01100011", -- C
	"10000101", -- D
	"01100001", -- E
	"01110001"  -- F
);
begin

process(clk)
variable psc : integer range 0 to 50001;
variable sel_dig : integer range 0 to 4;
variable A_var : integer range 0 to 15;
variable B_var : integer range 0 to 15;
variable C_var : integer range 0 to 15;
variable D_var : integer range 0 to 15;
begin

A_var := to_integer(unsigned(A)); -- To search in LUT4 table
B_var := to_integer(unsigned(B));
C_var := to_integer(unsigned(C));
D_var := to_integer(unsigned(D));

	if clk'event and clk = '1' then
		psc := psc + 1;
		if psc = 50001 then
			sel_dig := sel_dig + 1;
			if sel_dig = 4 then
				sel_dig := 0;
			end if;
		end if;
	end if;

case sel_dig is
	when 0 => sel_display <= "0111";
	when 1 => sel_display <= "1011";
	when 2 => sel_display <= "1101";
	when 3 => sel_display <= "1110";
	when others => sel_display <= "1111";
end case;

if sel_dig = 0 then
	case A is
		when "0000" => segment <= segmenton(A_var);
		when "0001" => segment <= segmenton(A_var);
		when "0010" => segment <= segmenton(A_var);
		when "0011" => segment <= segmenton(A_var);
		when "0100" => segment <= segmenton(A_var);
		when "0101" => segment <= segmenton(A_var);
		when "0110" => segment <= segmenton(A_var);
		when "0111" => segment <= segmenton(A_var);
		when "1000" => segment <= segmenton(A_var);
		when "1001" => segment <= segmenton(A_var);
		when others => segment <= segmenton(A_var);
	end case;
end if;

if sel_dig = 1 then
	case B is
		when "0000" => segment <= segmenton(B_var);
		when "0001" => segment <= segmenton(B_var);
		when "0010" => segment <= segmenton(B_var);
		when "0011" => segment <= segmenton(B_var);
		when "0100" => segment <= segmenton(B_var);
		when "0101" => segment <= segmenton(B_var);
		when "0110" => segment <= segmenton(B_var);
		when "0111" => segment <= segmenton(B_var);
		when "1000" => segment <= segmenton(B_var);
		when "1001" => segment <= segmenton(B_var);
		when others => segment <= segmenton(B_var);
	end case;
end if;

if sel_dig = 2 then
	case C is
		when "0000" => segment <= segmenton(C_var);
		when "0001" => segment <= segmenton(C_var);
		when "0010" => segment <= segmenton(C_var);
		when "0011" => segment <= segmenton(C_var);
		when "0100" => segment <= segmenton(C_var);
		when "0101" => segment <= segmenton(C_var);
		when "0110" => segment <= segmenton(C_var);
		when "0111" => segment <= segmenton(C_var);
		when "1000" => segment <= segmenton(C_var);
		when "1001" => segment <= segmenton(C_var);
		when others => segment <= segmenton(C_var);
	end case;
end if;

if sel_dig = 3 then
	case D is
		when "0000" => segment <= segmenton(D_var);
		when "0001" => segment <= segmenton(D_var);
		when "0010" => segment <= segmenton(D_var);
		when "0011" => segment <= segmenton(D_var);
		when "0100" => segment <= segmenton(D_var);
		when "0101" => segment <= segmenton(D_var);
		when "0110" => segment <= segmenton(D_var);
		when "0111" => segment <= segmenton(D_var);
		when "1000" => segment <= segmenton(D_var);
		when "1001" => segment <= segmenton(D_var);
		when others => segment <= segmenton(D_var);
	end case;
end if;

end process;
--sel_display <= digito_buffer;

end hardware;

RTL:

image

For this implementation, the FPGA resource usage is as follows:

image

Despite the logic for the implementation being simple, I tried to optimize it as much as possible to reduce the usage of Logic Elements (LEs). Now, here’s a slightly less terrible implementation 😄:

architecture hardware of decod_component is

type digit_array is array (0 to 3) of std_logic_vector(3 downto 0); -- Array type to hold 4 digits
signal digits  : digit_array; -- Signal to hold the input digits

type segmenton_array is array (0 to 15) of std_logic_vector(7 downto 0); -- Array type to hold the 7-segment encoding for 0-F
constant segmenton : segmenton_array := (
    "00000011", -- 0
    "10011111", -- 1
    "00100101", -- 2
    "00001101", -- 3
    "10011001", -- 4
    "01001001", -- 5
    "01000001", -- 6
    "00011111", -- 7
    "00000001", -- 8
    "00001001", -- 9
    "00010001", -- A
    "11000001", -- B
    "01100011", -- C
    "10000101", -- D
    "01100001", -- E
    "01110001"  -- F
); -- Constant array with 7-segment encoding

signal psc     : integer range 0 to 50000 := 0; -- Prescaler for clock division
signal sel_dig : integer range 0 to 3 := 0; -- Signal to select the current digit

begin
    -- Assign input digits to the array
    digits(0) <= A;
    digits(1) <= B;
    digits(2) <= C;
    digits(3) <= D;

    -- Clock process to update the prescaler and digit selector
    process(clk)
    begin
        if rising_edge(clk) then
            psc <= psc + 1;
            if psc = 50000 then
                sel_dig <= sel_dig + 1; -- Increment digit selector
            end if;
        end if;
    end process;

    -- Process to update the selected display and segment values
    process(sel_dig, digits)
    begin
        case sel_dig is
            when 0 => sel_display <= "0111"; -- Enable first display
            when 1 => sel_display <= "1011"; -- Enable second display
            when 2 => sel_display <= "1101"; -- Enable third display
            when 3 => sel_display <= "1110"; -- Enable fourth display
            when others => sel_display <= "1111"; -- Default case (disable all)
        end case;

        -- Set the segment output based on the current digit
        segment <= segmenton(to_integer(unsigned(digits(sel_dig))));
    end process;
end hardware;

RTL:

image

For this implementation, the FPGA resource usage is as follows:

image

FPGA Development Kit

The Development Kit used have embedded a Cyclone IV - EP4CE6E22C8. This kit have others models with others variants of EP4CEx (6 and 10k LEs).

image

References

Releases

No releases published

Packages

No packages published