sequential component

Four-Digit Display Scanner

Open in playground

A four-digit seven-segment display needs 28 segment wires if every digit is driven directly. The scanner drives one shared seven-segment bus and four digit enables instead: eleven wires. Only one digit is lit at any instant, and the scan walks fast enough that the eye reads all four as steady.

A two-bit counter picks the active digit. Its value selects one nibble of value through a mux. A sixteen-entry constant table decodes that nibble to active-high segments, seg(0) for segment a through seg(6) for segment g. The same counter drives the one-hot digit_sel enable. This reference advances one digit per clock cycle so a whole rotation fits in a short waveform. A real board divides the clock first, so each digit holds for about a millisecond.

In the waveform: with value at 0x1A2F, seg steps through the codes for F, 2, A, and 1 while digit_sel walks 0001 to 1000.

In the netlist: there is exactly one decode table, shared by all four digits. Four direct-driven digits would synthesize four copies; the scanner trades them for one mux and one counter.

The course builds the decoder and the mux from first principles in Combinational Building Blocks.

-- Four-digit display scanner: one shared seven-segment bus, one hex decode
-- table, a scan counter walking one-hot digit enables across the value
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity four_digit_scanner is
  port (
    clk       : in  std_logic;
    rst       : in  std_logic;
    value     : in  std_logic_vector(15 downto 0);
    seg       : out std_logic_vector(6 downto 0);
    digit_sel : out std_logic_vector(3 downto 0)
  );
end entity four_digit_scanner;

architecture rtl of four_digit_scanner is
  type seg_table_t is array (0 to 15) of std_logic_vector(6 downto 0);
  -- Active-high segments, seg(0) = a .. seg(6) = g
  constant SEG_TABLE : seg_table_t := (
    "0111111", "0000110", "1011011", "1001111",
    "1100110", "1101101", "1111101", "0000111",
    "1111111", "1101111", "1110111", "1111100",
    "0111001", "1011110", "1111001", "1110001"
  );

  signal scan_r : unsigned(1 downto 0) := (others => '0');
  signal nibble : std_logic_vector(3 downto 0) := (others => '0');
begin

  -- Scan counter: one digit per clock cycle (a real board divides this down)
  scan_p : process(clk)
  begin
    if rising_edge(clk) then
      if rst = '1' then
        scan_r <= (others => '0');
      else
        scan_r <= scan_r + 1;  -- 3 wraps back to digit 0
      end if;
    end if;
  end process scan_p;

  -- Digit mux: the scan position picks one nibble of the value
  with scan_r select
    nibble <= value(3 downto 0)   when "00",
              value(7 downto 4)   when "01",
              value(11 downto 8)  when "10",
              value(15 downto 12) when others;

  seg <= SEG_TABLE(to_integer(unsigned(nibble)));

  -- One-hot enable for the digit currently being driven
  with scan_r select
    digit_sel <= "0001" when "00",
                 "0010" when "01",
                 "0100" when "10",
                 "1000" when others;

end architecture rtl;
Waveform loading...