Design 5 – Hexadecimal display

The 7-segment LED displays on the Starter Kit board work differently from the “button” LED’s. Instead of applying a high level voltage on the FPGA pins to activate (light up) a segment, a LOW level voltage is used to activate a segment. The most common way to describe this is to say that the signal is active low. It can be viewed as negated or negative logic, which is why I’ve named the signals with a N_ prefix. The N_ prefix can also be read as the not operator. I prefer the term “active low”, as it emphasizes activation, and makes the signal level a secondary (but still a necessary) property.

The 7-segment display has two sets of signals. One set determines which “digits” will be turned on. The second set determines which line segments are turned on.

So far, we’ve only looked at individual bits. Now we look at groups of bits, organized as vectors. The code examples show how values can be specified in binary form. They also show that the vectors can be compared against numbers written in decimal form. And they show conditional expression syntax, which allows you to write a single assignment, rather than a bulky IF with lots of assignments.

Another item the code examples show is vector concatenation syntax. We’re adding a zero bit to keep the decimal point turned off.

The code examples show the active low signals created in two stages. The first stage is written in “normal” style, where 1 is used for ON, and 0 is used for OFF. The second stage occurs in the two assignments that take the “normal” values and inverts them to produce “negative” logic. Because the Xilinx synthesis tool can optimize the logic, it’s mostly a matter of style. (You can optimize the source code if you want. You never know when you’ll use a tool that does not optimize as well as ISE.)

First, the VHDL code:

--   7-segment display generator
--
--   Converts 4-bit digit to 7-segment display
--
--         a
--      *-----*
--      |     |
--    f |     | b
--      |  g  |
--      *-----*
--      |     |
--    e |     | c
--      |     |
--      *-----*
--         d
--
--   segment assignments:
--
--   a  = bit 6
--   b  = bit 5
--   c  = bit 4
--   d  = bit 3
--   e  = bit 2
--   f  = bit 1
--   g  = bit 0
--
--   0 = off, 1 = on

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity Bit4To7Seg is
  port (
    digit:     in  std_logic_vector(3 downto 0);
    n_led_seg: out std_logic_vector(7 downto 0);
    n_led_en:  out std_logic_vector(3 downto 0)
  );
end Bit4To7Seg;

architecture Behavioral of Bit4To7Seg is
  signal led_seg: std_logic_vector(6 downto 0);
  signal led_en:  std_logic_vector(3 downto 0);
begin

  led_en    <= "0001";

  n_led_seg <= not ('0' & led_seg);
  n_led_en  <= not led_en;

  led_seg <=
    "1111110" when digit =  0 else
    "0110000" when digit =  1 else
    "1101101" when digit =  2 else
    "1111001" when digit =  3 else
    "0110011" when digit =  4 else
    "1011011" when digit =  5 else
    "1011111" when digit =  6 else
    "1110000" when digit =  7 else
    "1111111" when digit =  8 else
    "1111011" when digit =  9 else
    "1110111" when digit = 10 else
    "0011111" when digit = 11 else
    "1001110" when digit = 12 else
    "0111101" when digit = 13 else
    "1001111" when digit = 14 else
    "1000111";

end Behavioral;

Next, the Verilog code:

//   7-segment display generator
//
//   Converts 4-bit digit to 7-segment display
//
//         a
//      *-----*
//      |     |
//    f |     | b
//      |  g  |
//      *-----*
//      |     |
//    e |     | c
//      |     |
//      *-----*
//         d
//
//   segment assignments:
//
//   a  = bit 6
//   b  = bit 5
//   c  = bit 4
//   d  = bit 3
//   e  = bit 2
//   f  = bit 1
//   g  = bit 0
//
//   0 = off, 1 = on

module Bit4To7Seg (
  input  [3:0] digit,
  output [7:0] n_led_seg,
  output [3:0] n_led_en
  );

  wire [6:0] led_seg;
  wire [3:0] led_en = 4'b0001;

  assign n_led_seg = ~{ 1'b0, led_seg };
  assign n_led_en  = ~led_en;

  assign led_seg =
    (digit ==  0) ? 7'b1111110 :
    (digit ==  1) ? 7'b0110000 :
    (digit ==  2) ? 7'b1101101 :
    (digit ==  3) ? 7'b1111001 :
    (digit ==  4) ? 7'b0110011 :
    (digit ==  5) ? 7'b1011011 :
    (digit ==  6) ? 7'b1011111 :
    (digit ==  7) ? 7'b1110000 :
    (digit ==  8) ? 7'b1111111 :
    (digit ==  9) ? 7'b1111011 :
    (digit == 10) ? 7'b1110111 :
    (digit == 11) ? 7'b0011111 :
    (digit == 12) ? 7'b1001110 :
    (digit == 13) ? 7'b0111101 :
    (digit == 14) ? 7'b1001111 :
                    7'b1000111 ;

endmodule

The following shows how to specify the vectored signals in the Xilinx UCF file. The digit signals are assigned to slide switches, and the slide switches can be set to show all 16 hexadecimal numbers.

NET "digit<0>"  LOC = "F12" | IOSTANDARD = LVTTL ;
NET "digit<1>"  LOC = "G12" | IOSTANDARD = LVTTL ;
NET "digit<2>"  LOC = "H14" | IOSTANDARD = LVTTL ;
NET "digit<3>"  LOC = "H13" | IOSTANDARD = LVTTL ;

NET "n_led_en<0>" LOC = "D14" | IOSTANDARD = LVTTL ;
NET "n_led_en<1>" LOC = "G14" | IOSTANDARD = LVTTL ;
NET "n_led_en<2>" LOC = "F14" | IOSTANDARD = LVTTL ;
NET "n_led_en<3>" LOC = "E13" | IOSTANDARD = LVTTL ;
NET "n_led_seg<0>" LOC = "N16" | IOSTANDARD = LVTTL ;
NET "n_led_seg<1>" LOC = "F13" | IOSTANDARD = LVTTL ;
NET "n_led_seg<2>" LOC = "R16" | IOSTANDARD = LVTTL ;
NET "n_led_seg<3>" LOC = "P15" | IOSTANDARD = LVTTL ;
NET "n_led_seg<4>" LOC = "N15" | IOSTANDARD = LVTTL ;
NET "n_led_seg<5>" LOC = "G13" | IOSTANDARD = LVTTL ;
NET "n_led_seg<6>" LOC = "E14" | IOSTANDARD = LVTTL ;
NET "n_led_seg<7>" LOC = "P16" | IOSTANDARD = LVTTL ;