TITLE: Project 5: Multi-digit decimal counter
AUTHOR: Chuck McManis
LAST UPDATE: 10-Mar-2001


The schematic on the right represents the solution I came up with. It got to be so complex that hand drawing the resulting schematic became more complicated than I expected. So, this is the last solution with a hand drawn schematic! For the full resolution version click here. Items in blue represent things in the top level VHDL module, COUNTER (which is shown on this page). The guts of the display are in the module MUXDISP and is shown as a component on the right.

The specification called for a decimal counter that could be reset, and one in which leading zeros could be blanked.

Frankly I learned a lot from this project and consider it something of a milestone. Suddenly I went from doing pretty straight-forward stuff to a place where there was lots of things going on and I re-used a lot of knowledge I've built up so far. I've concluded that is you've got these five projects down, you can do a wide variety of things in FPGA logic.

As you can see from the schematic the solution is the most complex so far and uses several sub-solutions. In particular I figured out from this project that "counting by 10" is not just a matter of putting your counter output through a "convert to base 10" decoder. Instead, each digit is generated by a BCD_COUNTER that counts to 9 and then generates a carry when it overflows back to zero. The stack of boxes on the left are the eight BCD_COUNTER instances.

The BCD counters feed a leading zero computation block (defined in the COUNTER) and that feeds the MUXDISP inputs for leading zero control. They also provide the data for MUXDISP to display.

CLOCK_10hz is in there providing the count input for the displays, and HEX_DISPLAY is a sub-circuit of the MUXDISP module that allows that module to generate the seven segment output.

In the first version of MUXDISP, I was using something like the looper project to control the digit enables. This works however it required 8 digit comparators for the other parts of the circuit that wanted to be driven by which digit was being displayed. This wasted gates unnecessarily in the design.

The Source Code

-- counter.vhd                Chuck McManis 20-Mar-2001
-- Top level entity for the multiplexed counter project. This module
-- instantiates the mux display module, some bcd counters, and the
-- clock_10hz module.
library IEEE;

entity counter is
    Port ( src_clk : in std_logic;
           blank : in std_logic;
           test : in std_logic;
           rst : in std_logic;
           digits : out std_logic_vector(7 downto 0);
           segs : out std_logic_vector(6 downto 0);
           dp: out std_logic);
end counter;

architecture behavioral of counter is
    component muxdisp port (
        mux_clk, rst : in std_logic;
        test, bl_ena, dp_ena : in std_logic;
        blank, dp : in std_logic_vector(2 downto 0);
        data : in std_logic_vector(31 downto 0);
        display : out std_logic_vector(6 downto 0);
        dpo : out std_logic;
        digits : out std_logic_vector(7 downto 0));
    end component;

    component clock_10hz port (
        clk_in : in std_logic;
        clk_out : out std_logic;
        reset : in std_logic);
    end component;

    component bcd_counter port (
        clk : in std_logic;
        lzo, carry : out std_logic;
        lzi, reset : in std_logic;
        q : out std_logic_vector(3 downto 0));
    end component;

    signal count : std_logic_vector (31 downto 0);
    signal clk, cy0, cy1, cy2, cy3, cy4, cy5, cy6, cy7 : std_logic;
    signal do_blanks : std_logic_vector(7 downto 0);
    signal blank_dig : std_logic_vector(2 downto 0);

    u1 : muxdisp port map (
        mux_clk => src_clk, rst => rst, data => count,
        bl_ena => blank, blank => blank_dig,
        test => test,
        dp_ena => '1', dp => "110", -- dp at digit 6
        display => segs, digits => digits, dpo => dp);

    u2: clock_10hz port map (
        clk_in => src_clk, clk_out => clk, reset => rst);

    ctr1: bcd_counter port map ( lzi => '0', lzo => do_blanks(0),
        clk => clk, reset => rst, carry => cy0, q => count(3 downto 0));
    ctr2: bcd_counter port map ( lzi => '0', lzo => do_blanks(1),
        clk => cy0, reset => rst, carry => cy1, q => count(7 downto 4));
    ctr3: bcd_counter port map ( lzi => do_blanks(3), lzo => do_blanks(2),
        clk => cy1, reset => rst, carry => cy2, q => count(11 downto 8));
    ctr4: bcd_counter port map (lzi => do_blanks(4), lzo => do_blanks(3),
        clk => cy2, reset => rst, carry => cy3, q => count(15 downto 12));
    ctr5: bcd_counter port map (lzi => do_blanks(5), lzo => do_blanks(4),
        clk => cy3, reset => rst, carry => cy4, q => count(19 downto 16));
    ctr6: bcd_counter port map (lzi => do_blanks(6), lzo => do_blanks(5),
        clk => cy4, reset => rst, carry => cy5, q => count(23 downto 20));
    ctr7: bcd_counter port map (lzi => do_blanks(7), lzo => do_blanks(6),
        clk => cy5, reset => rst, carry => cy6, q => count(27 downto 24));
    ctr8: bcd_counter port map (lzi => blank, lzo => do_blanks(7),
        clk => cy6, reset => rst, carry => cy7, q => count(31 downto 28));

    -- This then is a priority encoder that sets the value in 
    -- blank_dig to be the number of the lowest digit that
    -- has "blank suppression" enabled (the counters above compute
    -- it based on whether or not they contain zero and the digit
    -- ahead of them has zero in it.
    blank_dig <= "111" when do_blanks = "11111110" else
                 "110" when do_blanks = "11111100" else
                 "101" when do_blanks = "11111000" else
                 "100" when do_blanks = "11110000" else
                 "011" when do_blanks = "11100000" else
                 "010" when do_blanks = "11000000" else
                 "001" when do_blanks = "10000000" else

end behavioral;


Creative Commons License

This work is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported License. You are free to play around with it and modify it but you are not licensed to use it for commercial purposes. Click the link above for more details on your rights under this license.