Search code examples
synchronizationvhdlcounter

Decade Digit of Cascading Counter increments too late


I'm trying to design a two digit counter the counts between 00 and 99 in a circular fashion both up and down. I have it working for the most part, however, no matter what I try I can't get the decade digit to stay in synch with the first digit. My results right now give me stuff like this:

08 -> 09 -> 00 -> 11 ... 18 -> 19 -> 10 -> 21

and

21 -> 20 -> 29 -> 18 ... 11 -> 10 -> 19 -> 08

From this it seems that the overflow from the first digit is delayed in reaching the decade digit. I've tried several things to try and fix this. The only thing to provide any beneficial result was to add an additional if statement that sent the overflow a state early, but that was only a supperficial fix. If I stopped the counter while the first digit was 8 or 0, and started it again, I'd be back to the same problem as before.

I also tried making an additional 'synchronizer' module, thinking maybe I could set it up, so even though they're out of sync, they'd be displayed as if they were in sync, but it didn't change anything.

I've been working on trying to fix this for over two weeks and I'm at my wits end.

Here's my code for the counter, and the synchronizer if anyone wants to check that out, any and all help is appreciated.

**I'm using VHDL, programming the Zybo Digilent Board with Vivado 2015.2

Counter Module for single digit, the overflow becomes the enable of the decade digit.

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.numeric_std.all;

entity counter is
    generic(N      : positive := 4);
    port(
        AR       : in  STD_LOGIC;
        clk      : in  STD_LOGIC;
        ld       : in  STD_LOGIC;
        en       : in  STD_LOGIC;
        up_dn    : in  STD_LOGIC;
        D        : in  STD_LOGIC_VECTOR(N - 1 downto 0);
        overflow : out STD_LOGIC;
        Q        : out STD_LOGIC_VECTOR(N - 1 downto 0);
        sync_in  : in STD_LOGIC;
        sync_out : out STD_LOGIC
    );
end counter;

architecture counter of counter is
    signal Qt : std_logic_vector(N - 1 downto 0);
    signal OvrFlw : std_logic;
    signal sync : std_logic;

begin
    process(clk, AR)
    begin
        if (AR = '1') then
            Qt       <= (others => '0');
            OvrFlw <= '0';
            sync <= sync_in;
        elsif (clk = '1' and clk'event) then
            if ld = '1' then
                Qt <= D;
                sync <= sync_in;
            elsif en = '1' then
                if up_dn = '0' then     -- if counting down
                    if (unsigned(Qt) = 0) then
                        Qt       <= "1001";--(others => '1');
                        OvrFlw <= '1';
                        sync <= sync_in and en;
                    --elsif (unsigned(Qt) = 1) then
                    --    Qt       <= std_logic_vector(unsigned(Qt) - 1);
                    --    OvrFlw <= '1';
                    else
                        Qt       <= std_logic_vector(unsigned(Qt) - 1);
                        OvrFlw <= '0';
                        sync <= sync_in and en;
                    end if;
                else                    -- if counting up
                    if (unsigned(Qt) = 2**N-7) then
                        Qt       <= (others => '0');
                        OvrFlw <= '1';
                        sync <= sync_in and en;
                    --elsif (unsigned(Qt) = 2**N-8) then
                    --    Qt       <= std_logic_vector(unsigned(Qt) + 1);
                    --    OvrFlw <= '1';
                    else
                        Qt       <= std_logic_vector(unsigned(Qt) + 1);
                        OvrFlw <= '0';
                        sync <= sync_in and en;
                    end if;
                end if;
            end if;
        end if;
    end process;

    sync_out <= sync;
    Q <= Qt;
    overflow <= OvrFlw;
end counter;

Here's the code for the synchronizer I tried to put together. Don't know if it's really relevant but I thought I'd put it up just in case.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Synchronizer is
    generic(N      : positive := 4);
    Port (  
          MSB_Sync : in  STD_LOGIC;
          LSB_Sync : in  STD_LOGIC;
          MSB_Q    : in  STD_LOGIC_VECTOR(N-1 downto 0);
          LSB_Q    : in  STD_LOGIC_VECTOR(N-1 downto 0);
          MSB_Out  : out STD_LOGIC_VECTOR(N-1 downto 0);
          LSB_Out  : out STD_LOGIC_VECTOR(N-1 downto 0));
end Synchronizer;

architecture Behavioral of Synchronizer is

begin
process (MSB_Sync, LSB_Sync)
    begin
    if ((MSB_Sync and LSB_Sync) = '1') then
        MSB_Out <= MSB_Q;
        LSB_Out <= LSB_Q;
    end if;
end process;

end Behavioral;

Solution

  • The apparent basic problem you had resulting in the sync stuff is that your 'overflow' signal is registered, it's assignment found in the if statement conditional on a rising clock edge.

    I dumped the sync stuff and removed the overflow register in the following example:

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    
    entity bcd_ud_ctr is
        port (
            AR:         in  std_logic;
            clk:        in  std_logic;
            ld:         in  std_logic;
            en:         in  std_logic;
            up_dn:      in  std_logic;
            D:          in  std_logic_vector (3 downto 0);
            overflow:   out std_logic;
            Q:          out std_logic_vector (3 downto 0)
        );
    end entity;
    
    architecture off of bcd_ud_ctr is
        signal Qt:      unsigned (3 downto 0);
        signal nine:    std_logic;
        signal zero:    std_logic;
    
    begin
        -- Count recognizers
        nine <= '1' when Qt = "1001" else
                '0';
        zero <= '1' when Qt = "0000" else
                '0';
    COUNT:
        process (clk, AR)
        begin
            if AR = '1' then
                Qt <= (others => '0');
            elsif rising_edge(clk) then
                if ld = '1' then
                    Qt <= unsigned(D);
                elsif en = '1' then
                    if up_dn = '0' then  -- up
                        if nine = '1' then
                            Qt <= (others => '0');
                        else
                            Qt <= Qt + 1;
                        end if;
                    else                  -- down
                        if zero = '1' then 
                            Qt <= "1001"; 
                        else 
                            Qt <= Qt - 1;
                        end if;
                    end if;
                end if;
            end if;
        end process;
    
    BUFFERED_OUT:    
        Q <= std_logic_vector(Qt);
    
    CARRY_BORROW:
        overflow <= (en and     up_dn and zero) or
                    (en and not up_dn and nine);
    end architecture;
    

    Notice I also ANDed the enable into the overflow signal so it could be used as an enable to the next stage:

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity bcd_2digit_tb is
    end entity;
    
    architecture foo of bcd_2digit_tb is
        signal AR:          std_logic;
        signal clk:         std_logic := '0';
        signal ld:          std_logic := '0'; 
        signal en:          std_logic := '0';
        signal up_dn:       std_logic := '1';
        signal rollover:    std_logic;
        signal Q:           std_logic_vector (7 downto 0);
    
        constant DIG_PAR:   std_logic_vector(3 downto 0) := "0010"; -- 2
        constant TEN_PAR:   std_logic_vector(3 downto 0) := "0100"; -- 4 (42)
    begin
    
    CLOCK:
        process
        begin
            wait for 10 ns;
            clk <= not clk;
            if Now > 500 ns then
                wait;
            end if;
        end process;
    
    DIGITS:
        entity work.bcd_ud_ctr
            port map (
                AR => AR,
                clk => clk,
                ld => ld,
                en => en,
                up_Dn => up_dn,
                D => DIG_PAR,
                overflow => rollover,
                Q => Q(3 downto 0)
            );
    TENS:
        entity work.bcd_ud_ctr
            port map (
                AR => AR,
                clk => clk,
                ld => ld,
                en => rollover, 
                up_Dn => up_dn,
                D => TEN_PAR,
                overflow => open,
                Q => Q(7 downto 4)
            );
    STIMULUS:
        process
        begin
            wait for 10 ns;
            AR <= '1';
            wait for 10 ns;
            AR <= '0';
            up_dn <= '0'; -- up
            en <= '1';
            wait for 260 ns;
            up_dn <= '1';
            wait;
        end process;
    end architecture;
    

    And this gives:

    bcd_2digit.png (clickable)

    While this simply shows the one's digit rolling over then down counting and rolling under the testbench can be extended.