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.
- 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.
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.
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:
For this implementation, the FPGA resource usage is as follows:
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:
For this implementation, the FPGA resource usage is as follows:
The Development Kit used have embedded a Cyclone IV - EP4CE6E22C8. This kit have others models with others variants of EP4CEx (6 and 10k LEs).